diff --git a/.env b/.env
index 6e41482..13d4145 100644
--- a/.env
+++ b/.env
@@ -3,7 +3,7 @@ APP_URL=http://localhost/bbnepal/School-With-Accounts/account/
 DB_CONNECTION=mysql
 DB_HOST=localhost
 DB_PORT=3309
-DATABASE_OPTIONS=bbnepal_accounting,myurlsco_accounts1,myurlsco_accounts2,myurlsco_accounts3
-DB_DATABASE=bbnepal_accounting
+DATABASE_OPTIONS=bbnepal_accounting,myurlsco_accounts1,myurlsco_accounts2,myurlsco_accounts3,myurlsco_bbnepal_acc   #given
+DB_DATABASE=bbnepal_accounting  # given
 DB_USERNAME=root
 DB_PASSWORD=
\ No newline at end of file
diff --git a/account/application/controllers/accounts/Ledger.php b/account/application/controllers/accounts/Ledger.php
index 45c7a2e..7780f7e 100644
--- a/account/application/controllers/accounts/Ledger.php
+++ b/account/application/controllers/accounts/Ledger.php
@@ -10,8 +10,8 @@ class Ledger extends CI_Controller
     }
     public function _remap($alias = "", $params = array())
     {
-
         $data['dataValue'] = $this->session;
+        // echo "<pre>"; print_r($_GET['accategory_id']);die();
 
         $data['pageTitle'] = "Account Head";
         switch ($alias) {
@@ -19,9 +19,13 @@ class Ledger extends CI_Controller
                 $data['pageTitle'] = "Ledger";
                 $AccountGroups = $this->db->where("status", 1)->get("tbl_acgroups")->result();
                 foreach ($AccountGroups as $AccountGroup) {
-                    $AccountGroup->AccountCategories = $this->db->where("status", 1)->where("acgroup_id", $AccountGroup->acgroup_id)->get("tbl_accategories")->result();
+                    // $AccountGroup->AccountCategories = $this->db->where("status", 1)->where("acgroup_id", $AccountGroup->acgroup_id)->get("tbl_accategories")->result();
+                                    $AccountGroup->AccountCategories = $this->db->where("status", 1)->where("accategory_id ", ($_GET['accategory_id'] ?? ""))->get("tbl_accategories")->result();
+
                 }
                 $data['AccountGroups'] = $AccountGroups;
+                // echo "<pre>"; print_r($data['AccountGroups']);die();
+
                 loadView("accounts/ledger/navigate", $data);
                 break;
             case 'getledgersummary':
@@ -39,13 +43,27 @@ class Ledger extends CI_Controller
                 $this->load->view("accounts/ledgersummary", $data);
                 break;
             case 'partyledger':
-                $data['pageTitle'] = "View Party Ledger";
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                // $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
+                // $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
                 // $_GET['show_ledger'];
+                
+                if (!isset($_GET['from_date'])) {
+                    $data['from_date'] = null;
+                }
+                if (!isset($_GET['to_date'])) {
+                    $data['to_date'] = null;
+                }
+                if (isset($_GET)) {
+                    $data = $_GET;
+                }
+                $data['pageTitle'] = "View Party Ledger";
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+                  
+
                 if (isset($_GET['show_ledger'])) {
-                    $fromDate_Nepali = $_GET['from_date'];
-                    $toDate_Nepali = $_GET['to_date'];
+                    $fromDate_Nepali=$data['from_date'];
+                    $toDate_Nepali =$data['to_date'];
                     $data['fromDate'] = NepaliToEnglishDate($fromDate_Nepali);
                     $data['toDate'] = NepaliToEnglishDate($toDate_Nepali);
                     $data['fromDate_Nepali'] = $fromDate_Nepali;
@@ -54,7 +72,7 @@ class Ledger extends CI_Controller
                 }
                 loadView("accounts/ledger/partywise", $data);
                 break;
-            case 'print':
+            case 'print': 
                 $account_id = $this->uri->segment(4);
                 $fromDate=(isset($_GET['from_date'])?$_GET['from_date']:NepaliDate(($this->session->userdata('FiscalYear'))->fiscalyear_from));
                 $toDate=(isset($_GET['to_date'])?$_GET['to_date']:NepaliDate(($this->session->userdata('FiscalYear'))->fiscalyear_to));
@@ -77,14 +95,39 @@ class Ledger extends CI_Controller
                 loadView("accounts/daybook", $data);
                 break;
             case 'bank_book':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+               
+                // $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
+                // $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
                 $data['pageTitle'] = "Bank Book";               
                 loadView("accounts/ledger_bankbook", $data);
                 break;
             case 'cash_book':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+               
+                // $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
+                // $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
                 $data['pageTitle'] = "Cash Book";               
                 loadView("accounts/ledger_cashbook", $data);
                 break;
diff --git a/account/application/controllers/accounts/Reports.php b/account/application/controllers/accounts/Reports.php
index 41df1a9..5e7136d 100644
--- a/account/application/controllers/accounts/Reports.php
+++ b/account/application/controllers/accounts/Reports.php
@@ -65,8 +65,20 @@ class Reports extends CI_Controller
                 loadView("accounts/trialbalance", $data);
                 break;
             case 'trialbalance':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+                
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+                // echo "<pre>"; print_r($data); die(); 
+
                 $data['pageTitle'] = "Trial Balance";
                 loadView("accounts/trialbalance_new", $data);
                 break;
@@ -132,8 +144,17 @@ class Reports extends CI_Controller
                 loadView("accounts/pl", $data);
                 break;
             case 'pl':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
                 $data['pageTitle'] = "Profit & Loss Statement";
                 loadView("accounts/pl_new", $data);
                 break;
@@ -147,8 +168,20 @@ class Reports extends CI_Controller
                 loadView("accounts/balancesheet", $data);
                 break;
             case 'balance_sheet':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                // $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
+                // $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+               
                 $data['pageTitle'] = "Balance Sheet";
                 loadView("accounts/balancesheet_new", $data);
                 break;
@@ -157,8 +190,20 @@ class Reports extends CI_Controller
                 loadView("accounts/balance_sheet_vertical", $data);
                 break;
             case 'cash_flow':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                // $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
+                // $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+               
                 $data['pageTitle'] = "Cash Flow";
                 loadView("accounts/cash_flow", $data);
                 break;
@@ -171,8 +216,22 @@ class Reports extends CI_Controller
                 loadView("accounts/incomes_expenses", $data);
                 break;
             case 'receipt_and_payment':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+               
+               
+
+                // $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
+                // $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
                 $data['pageTitle'] = "Cash Flow";
                 loadView("accounts/receipt_and_payment", $data);
                 break;
diff --git a/account/application/controllers/inventory/Stocks.php b/account/application/controllers/inventory/Stocks.php
index 2bbf4fc..e2ad68a 100644
--- a/account/application/controllers/inventory/Stocks.php
+++ b/account/application/controllers/inventory/Stocks.php
@@ -6,10 +6,18 @@ class Stocks extends CI_Controller
     {
         parent::__construct();
         $this->load->model('MStocks');
-        checkLogin();
+        $this->load->helper('form');
+        // checkLogin();
     }
+
+
     public function _remap($alias = "", $params = array())
     {
+        // echo "<pre>"; var_dump($alias = "", $params = array()); die();
+        // echo "<pre>"; var_dump($alias,$this->input->post('fromDate'),$this->input->post('stocklocations_id'),$this->input->post('toDate')); die();
+
+
+        // var_dump(($_POST['stocklocations_id']));die();
         $data['dataValue'] = $this->session;
         $data['pageTitle'] = "Inventory Stock";
         switch ($alias) {
@@ -57,10 +65,28 @@ class Stocks extends CI_Controller
                 loadView("inventory/stocks/add", $data);
                 break;
             case 'summary':
-                $data['fiscalStart'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_from);
-                $data['fiscalEnd'] = NepaliDate($this->session->userdata['FiscalYear']->fiscalyear_to);
-                $data['StockRecords'] = $this->MStocks->getStockSummary();
-                // pre($data['StockRecords']);
+                
+                if (!isset($_POST['stocklocations_id'])) {
+                    $data['stocklocations_id'] = null;
+                }
+                if (!isset($_POST['fromDate'])) {
+                    $data['fromDate'] = null;
+                }
+                if (!isset($_POST['toDate'])) {
+                    $data['toDate'] = null;
+                }
+                if (isset($_POST)) {
+                    $data = $_POST;
+                }
+            
+                $data['pageTitle'] = "Inventory Stock";
+                $data['fiscalStart'] =  NepaliToEnglishDate($data['fromDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_from);
+                $data['fiscalEnd'] = NepaliToEnglishDate($data['toDate'] ?? $this->session->userdata['FiscalYear']->fiscalyear_to);
+
+
+                $data['StockRecords'] = $this->MStocks->getStockSummary($data);
+                
+
                 loadView("inventory/stocks/summary", $data);
                 break;
             case 'delete':
@@ -70,8 +96,24 @@ class Stocks extends CI_Controller
                 redirect("inventory/stocks/list");
                 break;
             default:
+                // var_dump("hello");
+                // die();
+                // echo '<pre>';
+                // var_dump($_POST['fromDate'], $_POST['toDate']);
+                // die();
                 $data['StockRecords'] = $this->MStocks->getStockRecords();
                 loadView("inventory/stocks/list", $data);
         }
     }
+    // public function summary()
+    // {
+    //     var_dump("hello");
+    //     die();
+    //     echo '<pre>';
+    //     var_dump($_POST['stocklocations_id'], $_POST['fromDate'], $_POST['toDate']);
+    //     die();
+    //     $data['StockRecords'] = $this->MStocks->getStockSummary($_POST['stocklocations_id'] = 0, $_POST['fromDate'], $_POST['toDate']);
+    //     // echo "<pre>", var_dump($data['StockRecords']); die();
+    //     loadView("inventory/stocks/summary", $data);
+    // }
 }
diff --git a/account/application/libraries/BIBAccounts.php b/account/application/libraries/BIBAccounts.php
index 5930957..2bfce27 100644
--- a/account/application/libraries/BIBAccounts.php
+++ b/account/application/libraries/BIBAccounts.php
@@ -76,7 +76,7 @@ class bibaccounts
                 $group->cr_total += $accountBalances['cr_total'];
                 $group->regular_balance_dr += $accountBalances['regular_balance_dr'];
                 $group->regular_balance_cr += $accountBalances['regular_balance_cr'];
-                $account->balances=$accountBalances;
+                $account->balances = $accountBalances;
             }
             if (
                 $group->dr_total > 0 &&
@@ -90,7 +90,67 @@ class bibaccounts
             $group->opening_balance = ($group->posting_side == "DR") ? $group->opening_balance_dr - $group->opening_balance_cr : $group->opening_balance_cr - $group->opening_balance_dr;
             $group->closing_balance = ($group->posting_side == "DR") ? $group->dr_total - $group->cr_total : $group->cr_total - $group->dr_total;
             $group->regular_balance = ($group->posting_side == "DR") ? $group->regular_balance_dr - $group->regular_balance_cr : $group->regular_balance_cr - $group->regular_balance_dr;
-           
+
+            $group->accounts = $accounts;
+            $result[] = $group;
+        }
+        // pre($result);die;
+        return $result;
+    }
+
+    // new function
+    function getAccountGroupsWithBalancesNew($acgroup_id = null, $fiscalStart = null, $fiscalEnd = null)
+    {
+
+        $CI = &get_instance();
+        $result = [];
+
+        $whereClause = "";
+        if ($acgroup_id !== null) {
+            $whereClause = "WHERE acgroup_id = '$acgroup_id' AND status=1 AND created_on >= '$fiscalStart' AND created_on >= '$fiscalEnd'  ";
+        }
+
+        $t = "SELECT * FROM `tbl_acgroups` $whereClause";
+        $accountGroups = $CI->db->query($t)->result();
+        foreach ($accountGroups as $group) {
+            $t = "SELECT * FROM tbl_accounts 
+              WHERE (accategory_id IN (SELECT accategory_id FROM tbl_accategories WHERE acgroup_id = '{$group->acgroup_id}') 
+                     OR accategory_id IN (SELECT parent_category_id FROM tbl_accategories WHERE accategory_id IN (SELECT accategory_id FROM tbl_accategories WHERE acgroup_id = '{$group->acgroup_id}')))
+                    AND status <> -1";
+
+            $accounts = $CI->db->query($t)->result();
+
+            $group->dr_total = 0;
+            $group->cr_total = 0;
+            $group->regular_balance_dr = 0;
+            $group->regular_balance_cr = 0;
+            $group->opening_balance_dr = 0;
+            $group->opening_balance_cr = 0;
+
+            foreach ($accounts as $account) {
+                $accountBalances = $this->getAccountBalances($account->account_id);
+                //    pre($accountBalances);die;
+                $group->opening_balance_dr += $accountBalances['opening_balance_dr'];
+                $group->opening_balance_cr += $accountBalances['opening_balance_cr'];
+                $group->dr_total += $accountBalances['dr_total'];
+                $group->cr_total += $accountBalances['cr_total'];
+                $group->regular_balance_dr += $accountBalances['regular_balance_dr'];
+                $group->regular_balance_cr += $accountBalances['regular_balance_cr'];
+                $account->balances = $accountBalances;
+            }
+            if (
+                $group->dr_total > 0 &&
+                $group->cr_total > 0 &&
+                $group->regular_balance_dr > 0 &&
+                $group->regular_balance_cr > 0 &&
+                $group->opening_balance_dr > 0 &&
+                $group->opening_balance_cr > 0
+            ) $group->isZero = 1;
+            else $group->isZero = 0;
+            $group->opening_balance = ($group->posting_side == "DR") ? $group->opening_balance_dr - $group->opening_balance_cr : $group->opening_balance_cr - $group->opening_balance_dr;
+            $group->closing_balance = ($group->posting_side == "DR") ? $group->dr_total - $group->cr_total : $group->cr_total - $group->dr_total;
+            $group->regular_balance = ($group->posting_side == "DR") ? $group->regular_balance_dr - $group->regular_balance_cr : $group->regular_balance_cr - $group->regular_balance_dr;
+
             $group->accounts = $accounts;
             $result[] = $group;
         }
@@ -99,7 +159,9 @@ class bibaccounts
     }
 
 
-    function getAccountCategoriesWithBalances($acgroup_id = null, $accategory_id=null)
+    // new function ends
+
+    function getAccountCategoriesWithBalances($acgroup_id = null, $accategory_id = null)
     {
         $CI = &get_instance();
         $result = [];
@@ -138,7 +200,7 @@ class bibaccounts
                 $category->cr_total += $accountBalances['cr_total'];
                 $category->regular_balance_dr += $accountBalances['regular_balance_dr'];
                 $category->regular_balance_cr += $accountBalances['regular_balance_cr'];
-                $account->balances=$accountBalances;
+                $account->balances = $accountBalances;
             }
             $category->posting_side = $CI->db->query("SELECT posting_side FROM tbl_acgroups WHERE acgroup_id=(SELECT acgroup_id FROM tbl_accategories WHERE accategory_id='" . $category->accategory_id . "')")->row()->posting_side;
 
@@ -149,8 +211,8 @@ class bibaccounts
             $category->accounts = $accounts;
             $result[] = $category;
         }
-      //  pre($category);
-        if($accategory_id!=null)return isset($category)?$category:'';
+        //  pre($category);
+        if ($accategory_id != null) return isset($category) ? $category : '';
         return $result;
     }
 
@@ -538,7 +600,7 @@ class bibaccounts
         $fiscal_year_id = ($fiscal_year_id == "") ? $this->FY : $fiscal_year_id;
         $branch_id = ($branch_id == "") ? $this->Branch : $branch_id;
         $CI = &get_instance();
-        $query = $CI->db->query("SELECT SUM(dr) AS dr_total, SUM(cr) AS cr_total FROM tbl_voucherdetails WHERE account_id='$account_id' AND status=1 AND fiscalyear_id='$fiscal_year_id' AND branch_id='$branch_id'");
+        $query = $CI->db->query("SELECT SUM(dr) AS dr_total, SUM(cr) AS cr_total FROM tbl_voucherdetails WHERE account_id='$account_id' AND status=1 AND fiscalyear_id='$fiscal_year_id' AND branch_id='$branch_id' ");
         $result = $query->row();
 
         $drTotal = $result->dr_total;
@@ -578,7 +640,51 @@ class bibaccounts
             'regular_balance_cr' => $regularBalanceCr,
         );
     }
+    function getAccountBalancesNew($account_id, $fiscal_year_id = "", $branch_id = "",$fiscalStart = null, $fiscalEnd = null)
+    {
+        $fiscal_year_id = ($fiscal_year_id == "") ? $this->FY : $fiscal_year_id;
+        $branch_id = ($branch_id == "") ? $this->Branch : $branch_id;
+        $CI = &get_instance();
+        $query = $CI->db->query("SELECT SUM(dr) AS dr_total, SUM(cr) AS cr_total FROM tbl_voucherdetails WHERE account_id='$account_id' AND status=1 AND fiscalyear_id='$fiscal_year_id' AND branch_id='$branch_id' AND created_on >= '$fiscalStart' AND created_on >= '$fiscalEnd' ");
+        $result = $query->row();
+        echo "<pre>";print_r($result);die();
+        $drTotal = $result->dr_total;
+        $crTotal = $result->cr_total;
 
+        $posting_side = $CI->db->query("SELECT posting_side FROM tbl_acgroups WHERE acgroup_id=(SELECT acgroup_id FROM tbl_accategories WHERE accategory_id=(SELECT accategory_id FROM tbl_accounts WHERE account_id='$account_id'))")->row()->posting_side;
+
+        $drBalance = 0;
+        $crBalance = 0;
+
+        if ($posting_side == "DR") {
+            $drBalance = $drTotal - $crTotal;
+        } else {
+            $crBalance = $crTotal - $drTotal;
+        }
+
+        $openingBalanceQuery = $CI->db->query("SELECT SUM(dr) AS opening_balance_dr, SUM(cr) AS opening_balance_cr FROM tbl_voucherdetails WHERE account_id='$account_id' AND voucher_id=0 AND status=1 AND fiscalyear_id='$fiscal_year_id' AND branch_id='$branch_id'");
+        $openingBalanceResult = $openingBalanceQuery->row();
+
+        $openingBalanceDr = $openingBalanceResult->opening_balance_dr;
+        $openingBalanceCr = $openingBalanceResult->opening_balance_cr;
+
+        $regularBalanceQuery = $CI->db->query("SELECT SUM(dr) AS regular_balance_dr, SUM(cr) AS regular_balance_cr FROM tbl_voucherdetails WHERE account_id='$account_id' AND voucher_id<>0 AND status=1 AND fiscalyear_id='$fiscal_year_id' AND branch_id='$branch_id'");
+        $regularBalanceResult = $regularBalanceQuery->row();
+
+        $regularBalanceDr = $regularBalanceResult->regular_balance_dr;
+        $regularBalanceCr = $regularBalanceResult->regular_balance_cr;
+
+        return array(
+            'dr_total' => $drTotal,
+            'cr_total' => $crTotal,
+            'dr_balance' => $drBalance,
+            'cr_balance' => $crBalance,
+            'opening_balance_dr' => $openingBalanceDr,
+            'opening_balance_cr' => $openingBalanceCr,
+            'regular_balance_dr' => $regularBalanceDr,
+            'regular_balance_cr' => $regularBalanceCr,
+        );
+    }
 
 
     //////
@@ -608,12 +714,13 @@ class bibaccounts
             <?php } ?>
             <tbody>
                 <?php foreach ($accountCategories as $accountCategory) : ?>
-                    <?php //print_r($accountCategory);die; ?>
+                    <?php //print_r($accountCategory);die; 
+                    ?>
                     <?php if ($ReportOptions['showZeroBalances'] && $accountCategory->isZero == true) continue; ?>
                     <tr class="<?php echo ($accountCategory->isParent) ? ' parent-row' : ''; ?>" data-toggle="collapse" data-target="#accategory_<?php echo $accountCategory->accategory_id; ?>">
                         <td class="group-name">
-                            <?php if($accountCategory->parent_category_id): ?>
-                                <?php echo "&nbsp;&nbsp;". $accountCategory->accategory_name; ?>
+                            <?php if ($accountCategory->parent_category_id): ?>
+                                <?php echo "&nbsp;&nbsp;" . $accountCategory->accategory_name; ?>
                             <?php else: ?>
                                 <b><?php echo $accountCategory->accategory_name; ?></b>
                             <?php endif; ?>
@@ -817,14 +924,14 @@ class bibaccounts
     /*
      * Retrieves the account groups and accounts with balances that has cash/bank in debit side
      */
-    function getIncomeAndExpenditure()
+    function getIncomeAndExpenditure($fiscalStart,$fiscalEnd)
     {
         $CI = &get_instance();
         #2 >> Bank Accounts
         #3 >> Cash Accounts
         $Incomes = array();
         $Expenses = array();
-        $t = "select * from tbl_voucherdetails where voucher_id in (select voucher_id from tbl_voucherdetails where voucher_id<>0 and account_id in (select account_id from tbl_accounts where accategory_id=2 or accategory_id=3)) and account_id not in (select account_id from tbl_accounts where accategory_id=2 or accategory_id=3)";
+        $t = "select * from tbl_voucherdetails where voucher_id in (select voucher_id from tbl_voucherdetails where voucher_id<>0 and account_id in (select account_id from tbl_accounts where accategory_id=2 or accategory_id=3)) AND created_on >= '$fiscalStart' AND created_on >= '$fiscalEnd' and account_id not in (select account_id from tbl_accounts where accategory_id=2 or accategory_id=3)";
         $accounts = $CI->db->query($t)->result();
         foreach ($accounts as $account) {
             $account->details = $CI->db->query("select * from tbl_accounts where account_id=" . $account->account_id)->row();
@@ -954,14 +1061,14 @@ class bibaccounts
                 $sides["Incomes"] = $newArray;
             else
                 $sides["Expenses"] = $newArray;
-           
+
         endforeach;
 
         return $sides;
     }
-    function prepareIncomeExpenses()
+    function prepareIncomeExpenses($fiscalStart,$fiscalEnd)
     {
-        $cashFlow = $this->getIncomeAndExpenditure();
+        $cashFlow = $this->getIncomeAndExpenditure($fiscalStart,$fiscalEnd);
         $a = 0;
         foreach ($cashFlow as $side) :
             $Groups = array();
@@ -1003,7 +1110,7 @@ class bibaccounts
                 $sides["Incomes"] = $newArray;
             else
                 $sides["Expenses"] = $newArray;
-           
+
         endforeach;
 
         return $sides;
diff --git a/account/application/libraries/Myaccounts.php b/account/application/libraries/Myaccounts.php
index 06dfebb..4115bc9 100644
--- a/account/application/libraries/Myaccounts.php
+++ b/account/application/libraries/Myaccounts.php
@@ -800,7 +800,8 @@ class myaccounts
         $CI = &get_instance();
         $CI->load->library("numbertoword");
         $ledgerData = $this->getLedgerData($account_id, $fromDate, $toDate);
-        $file = APPPATH . "/../pdf/e_ledger1.pdf";
+        $file = APPPATH . "/../pdf/
+        .pdf";
         $pdf = new FPDM($file);
         $i = 0;
         $crTotal = 0;
@@ -844,7 +845,7 @@ class myaccounts
         $OutputFile = "Ledger_" . $ledgerData['Account']->account_id . ".pdf";
         $pdf->Output("F", $OutputFileLocation . $OutputFile);
         return $OutputFile;
-    }
+    } 
     function ledgerPDF($account_id, $fromDate, $toDate)
     {
         $CI = &get_instance();
diff --git a/account/application/models/MStocks.php b/account/application/models/MStocks.php
index 7cfb1a8..fa99b9c 100644
--- a/account/application/models/MStocks.php
+++ b/account/application/models/MStocks.php
@@ -71,8 +71,8 @@ class MStocks extends CI_Model
             $nos++;
             $rate += $Item->rate;
         }
-        if($nos!=0)        $rate = $rate / $nos;
-        
+        if ($nos != 0)        $rate = $rate / $nos;
+
         $SalesStock = new stdClass;
         $SalesStock->qty = $qty;
         $SalesStock->rate = $rate;
@@ -94,14 +94,14 @@ class MStocks extends CI_Model
             $nos++;
             $rate += $Item->rate;
         }
-        if($nos!=0)        $rate = $rate / $nos;
+        if ($nos != 0)        $rate = $rate / $nos;
         $PurchaseStock = new stdClass;
         $PurchaseStock->qty = $qty;
         $PurchaseStock->rate = $rate;
         $PurchaseStock->amount = $qty * $rate;
         return $PurchaseStock;
     }
-   
+
     public function getOpeningStock($item_id)
     {
         $this->db->where("status", 1);
@@ -116,7 +116,7 @@ class MStocks extends CI_Model
             $nos++;
             $rate += $Item->price;
         }
-        if($nos!=0)        $rate = $rate / $nos;
+        if ($nos != 0)        $rate = $rate / $nos;
         $OpeningStock = new stdClass;
         $OpeningStock->qty = $qty;
         $OpeningStock->rate = $rate;
@@ -125,12 +125,12 @@ class MStocks extends CI_Model
     }
     public function getClosingStock($item_id)
     {
-        $OpeningStock=$this->getOpeningStock($item_id);
-        $PurchaseStock=$this->getItemPurchaseStock($item_id);
-        $SalesStock=$this->getItemSalesStock($item_id);
+        $OpeningStock = $this->getOpeningStock($item_id);
+        $PurchaseStock = $this->getItemPurchaseStock($item_id);
+        $SalesStock = $this->getItemSalesStock($item_id);
         $ClosingStock = new stdClass;
-        $ClosingStock->qty =$PurchaseStock->qty+$OpeningStock->qty-$SalesStock->qty;
-        $ClosingStock->rate = $PurchaseStock->rate+$OpeningStock->rate-$SalesStock->rate;
+        $ClosingStock->qty = $PurchaseStock->qty + $OpeningStock->qty - $SalesStock->qty;
+        $ClosingStock->rate = $PurchaseStock->rate + $OpeningStock->rate - $SalesStock->rate;
         $ClosingStock->amount = $ClosingStock->qty * $ClosingStock->rate;
         return $ClosingStock;
     }
@@ -151,8 +151,10 @@ class MStocks extends CI_Model
         $Item->Closing = $this->getClosingStock($item_id);
         return $Item;
     }
-    public function getStockSummary($stocklocation_id = 0)
+    public function getStockSummary($data)
     {
+     
+        $stocklocation_id = $data['stocklocation_id'] ?? null;
         $this->db->where("status", 1);
         if ($stocklocation_id != 0) {
             $this->db->where("stocklocations_id", $stocklocation_id);
@@ -160,9 +162,19 @@ class MStocks extends CI_Model
         $StockRecords = $this->db->select("distinct(items_id) as items_id")->get("tbl_stocks")->result();
         foreach ($StockRecords as $StockRecord) {
             $StockRecord->Summary = $this->getStockItemSummary($StockRecord->items_id);
-            // $StockRecord->StockLocation = $this->db->where("stocklocation_id", $StockRecord->stocklocations_id)->get("tbl_stocklocations")->row();
-            $StockRecord->Item = $this->db->where("item_id", $StockRecord->items_id)->get("tbl_items")->row();
-        }
+            $this->db->where("stocklocation_id ", $StockRecord->Summary->stocklocations_id);
+            $query1 = $this->db->get("tbl_stocklocations");
+            $result2 = $query1->result();
+            $StockRecord->StockLocation = $result2;
+            //tbl_items
+            $this->db->where('created_on >=', date('Y-m-d', (strtotime($data['fiscalStart']))));
+            $this->db->where('created_on <=', date('Y-m-d', (strtotime($data['fiscalEnd']))));
+            $this->db->where('item_id', $StockRecord->items_id);
+            $query3 = $this->db->get('tbl_items');
+            $result3 = $query3->result(); 
+            $StockRecord->Item = $result3;
+ }
+       
         return $StockRecords;
     }
     public function getStockRecords($stocklocation_id = 0)
@@ -215,6 +227,5 @@ class MStocks extends CI_Model
         $Item = $this->db->where("item_id", $id)->get("tbl_items")->row();
         $Unit = $this->db->where("unit_id", $Item->units_id)->get("tbl_units")->row();
         return $Unit;
-
     }
 }
diff --git a/account/application/views/accounts/balancesheet_new.php b/account/application/views/accounts/balancesheet_new.php
index 0a0bb7b..2df5674 100644
--- a/account/application/views/accounts/balancesheet_new.php
+++ b/account/application/views/accounts/balancesheet_new.php
@@ -115,7 +115,7 @@ $BIBAccounts = new BIBAccounts();
                 <div class="card-header">
                     <h3 class="card-title mt-1"><?php echo $pageTitle; ?> </h3>
                     <div class="card-tools">
-                        <form method="post" action="" id="FilterForm">
+                        <form method="post" action=<?php echo base_url('accounts/reports/balance_sheet') ;?> id="FilterForm">
                             <!-- button with a dropdown -->
                             <div class="btn-group">
                                 <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown" data-offset="-52">
@@ -203,7 +203,7 @@ $BIBAccounts = new BIBAccounts();
                                         'showPeriod' => false,
                                     );
                                     ?>
-                                      <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances(2); ?>
+                                      <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalancesNew(2,$fiscalStart,$fiscalEnd); ?>
                                       <?php $Side1Total = 0; ?>
                                 <div class="col-6 p-0 m-0 gy-0">
                                     <div class="table-responsive">
@@ -216,7 +216,7 @@ $BIBAccounts = new BIBAccounts();
                                             </thead>
                                             <tbody>
                                             
-                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances(2); ?>
+                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalancesNew(2,$fiscalStart,$fiscalEnd); ?>
                                             <?php $Side1Total = 0; ?>
                                             <?php
                                                 // Initialize variables for column totals
@@ -245,7 +245,7 @@ $BIBAccounts = new BIBAccounts();
                                                     $Side1Total += $group->closing_balance;
                                                     ?>
                                                 <?php endforeach; ?>
-                                                <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances(5); ?>
+                                                <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalancesNew(5,$fiscalStart,$fiscalEnd); ?>
                                             
                                                 <?php
                                                 // Initialize variables for column totals
@@ -291,7 +291,7 @@ $BIBAccounts = new BIBAccounts();
                                                 </tr>
                                             </thead>
                                             <tbody>
-                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances(1); ?>
+                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalancesNew(1,$fiscalStart,$fiscalEnd); ?>
                                             
                                                 <?php
                                                 // Initialize variables for column totals
@@ -328,14 +328,14 @@ $BIBAccounts = new BIBAccounts();
                                             
                                         
                                             </tbody>
-                                           
+                                                    
                                         </table>
                                     </div>
                                 </div>
                                 <?php
                                     // pre($BIBAccounts->getAccountGroupsWithBalances(4));
-                                    $TotalIncomes = $BIBAccounts->getAccountGroupsWithBalances(3)[0]->closing_balance;
-                                    $TotalExpenses = $BIBAccounts->getAccountGroupsWithBalances(4)[0]->closing_balance;
+                                    $TotalIncomes = ($BIBAccounts->getAccountGroupsWithBalancesNew(3,$fiscalStart,$fiscalEnd)[0]->closing_balance)  ?? 0;
+                                    $TotalExpenses = ($BIBAccounts->getAccountGroupsWithBalancesNew(4,$fiscalStart,$fiscalEnd)[0]->closing_balance) ?? 0;
                                     $PL = $TotalIncomes - $TotalExpenses;
                                     $Side1Total += $PL;
                                 ?>
diff --git a/account/application/views/accounts/cash_flow.php b/account/application/views/accounts/cash_flow.php
index 948a4e4..efdec8f 100644
--- a/account/application/views/accounts/cash_flow.php
+++ b/account/application/views/accounts/cash_flow.php
@@ -129,7 +129,7 @@ $PL = $IncomesTotal - $ExpensesTotal;
                     <h3 class="card-title mt-1">Cash Flow<?php //echo $pageTitle; 
                                                             ?> </h3>
                     <div class="card-tools">
-                        <form method="post" action="" id="FilterForm">
+                        <form method="post" action=<?php echo base_url('accounts/reports/cash_flow') ;?> id="FilterForm">
                             <!-- button with a dropdown -->
                             <div class="btn-group">
                                 <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown" data-offset="-52">
@@ -264,7 +264,9 @@ $PL = $IncomesTotal - $ExpensesTotal;
                     <h4 class="card-title">Cash Flow</h4>
                 </div> -->
                 <div class="card-body table-responsive p-0">
-                    <?php $IncomesExpenses = $BIBAccounts->prepareIncomeExpenses() ?>
+                    <?php $IncomesExpenses = $BIBAccounts->prepareIncomeExpenses($fiscalStart,$fiscalEnd); 
+                    // echo "<pre>";print_r($IncomesExpenses); die();
+                     ?>
                  
           
 
diff --git a/account/application/views/accounts/ledger_bankbook.php b/account/application/views/accounts/ledger_bankbook.php
index 39fd4d5..6a17e96 100644
--- a/account/application/views/accounts/ledger_bankbook.php
+++ b/account/application/views/accounts/ledger_bankbook.php
@@ -7,8 +7,8 @@
                         <div class="card-header  disabled color-palette">
                             <h3 class="card-title mt-1"><?php echo $pageTitle; ?> </h3>
                             <div class="card-tools">
-                                <form method="post" action="" id="FilterForm">
-                                    <!-- button with a dropdown -->
+                                <form method="post" action="<?php echo base_url('accounts/ledger/bank_book'); ?>" id="FilterForm">
+                                    <!-- button with a dropdown --> 
                                     <div class="btn-group">
                                         <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown" data-offset="-52">
                                             Filters
@@ -56,7 +56,7 @@
                                         </div>
                                     </div>
                                     <div class="btn-group">
-                                        <button type="submit" class="btn btn-warning btn-sm " data-toggle="dropdown" data-offset="-52">
+                                        <button type="submit"  onclick="exportTableToCSV('table_data.csv')" class="btn btn-warning btn-sm " data-toggle="dropdown" data-offset="-52">
                                             Export
                                         </button>
                                         <!-- <div class="dropdown-menu" role="menu">
@@ -90,7 +90,7 @@
                                         $accategory_id = 3;
                                         $CI = &get_instance();
 
-                                        $CI->db->where("status", 1)->where("accategory_id = 2");
+                                        $CI->db->where("status", 1)->where("accategory_id = 2")->where('created_on >=', date('Y-m-d', (strtotime($fiscalStart))))->where('created_on <=', date('Y-m-d', (strtotime($fiscalEnd))));
                                         $CI->db->order_by("account_name ASC");
                                         $Accounts = $CI->db->get("tbl_accounts")->result();
 
@@ -231,6 +231,101 @@
             maxDate: $('#toDate').data('end'),
         });
     </script>
+
+    <!-- csv printing -->
+    <!-- <script type="text/javascript">
+        function tableToCSV() {
+
+            // Variable to store the final csv data
+            let csv_data = [];
+
+            // Get each row data
+            let rows = document.getElementsByTagName('tr');
+            for (let i = 0; i < rows.length; i++) {
+
+                // Get each column data
+                let cols = rows[i].querySelectorAll('td,th,span,a');
+
+                // Stores each csv row data
+                let csvrow = [];
+                for (let j = 0; j < cols.length; j++) {
+
+                    // Get the text data of each cell
+                    // of a row and push it to csvrow
+                    csvrow.push(cols[j].innerText);
+                }
+
+                // Combine each column value with comma
+                csv_data.push(csvrow.join(","));
+            }
+
+            // Combine each row data with new line character
+            csv_data = csv_data.join('\n');
+
+            // Call this function to download csv file  
+            downloadCSVFile(csv_data);
+
+        }
+
+        function downloadCSVFile(csv_data) {
+
+            // Create CSV file object and feed
+            // our csv_data into it
+            CSVFile = new Blob([csv_data], {
+                type: "text/csv"
+            });
+
+            // Create to temporary link to initiate
+            // download process
+            let temp_link = document.createElement('a');
+
+            // Download csv file
+            temp_link.download = "GfG.csv";
+            let url = window.URL.createObjectURL(CSVFile);
+            temp_link.href = url;
+
+            // This link should not be displayed
+            temp_link.style.display = "none";
+            document.body.appendChild(temp_link);
+
+            // Automatically click the link to
+            // trigger download
+            temp_link.click();
+            document.body.removeChild(temp_link);
+        } -->
+    </script>
+
+    <script>
+        function exportTableToCSV(filename) {
+            const csvRows = [];
+            const rows = document.querySelectorAll(".longdataTable tr"); // Use the specific class
+
+            for (const row of rows) {
+                const cols = row.querySelectorAll("td, th");
+                const csvRow = [];
+
+                for (const col of cols) {
+                    csvRow.push(col.innerText); // Get text content of each cell
+                }
+
+                csvRows.push(csvRow.join(",")); // Join cells with commas
+            }
+
+            // Create a CSV string
+            const csvString = csvRows.join("\n");
+
+            // Create a Blob for the CSV string
+            const blob = new Blob([csvString], { type: "text/csv" });
+            const url = URL.createObjectURL(blob);
+            const a = document.createElement("a");
+            a.href = url;
+            a.download = filename;
+            document.body.appendChild(a);
+            a.click();
+            document.body.removeChild(a);
+        }
+    </script>
+    <!-- csv printing ends -->
 <?php
 }
 ?>
\ No newline at end of file
diff --git a/account/application/views/accounts/ledger_cashbook.php b/account/application/views/accounts/ledger_cashbook.php
index bfd6648..f942c50 100644
--- a/account/application/views/accounts/ledger_cashbook.php
+++ b/account/application/views/accounts/ledger_cashbook.php
@@ -91,7 +91,7 @@
                                         $CI = &get_instance();
 
                                         // $CI->db->where("status", 1)->where("(accategory_id = 2 OR accategory_id = 3)");
-                                        $CI->db->where("status", 1)->where("accategory_id = 3");
+                                        $CI->db->where("status", 1)->where("accategory_id = 3")->where("created_on >= '$fiscalStart'")->where("created_on >= '$fiscalEnd'");
                                         $CI->db->order_by("account_name ASC");
                                         $Accounts = $CI->db->get("tbl_accounts")->result();
 
diff --git a/account/application/views/accounts/pl_new.php b/account/application/views/accounts/pl_new.php
index eafc3a8..753e7c5 100644
--- a/account/application/views/accounts/pl_new.php
+++ b/account/application/views/accounts/pl_new.php
@@ -124,7 +124,7 @@ $BIBAccounts = new BIBAccounts();
                     <h2 class="card-title mt-1"><?php echo $pageTitle; ?> <?php //myLang("Report"); 
                                                                             ?></h2>
                     <div class="card-tools">
-                        <form method="post" action="" id="FilterForm">
+                        <form method="post" action="<?php echo base_url('accounts/reports/pl'); ?>" id="FilterForm">
                             <!-- button with a dropdown -->
                             <div class="btn-group">
                                 <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown" data-offset="-52">
@@ -194,7 +194,9 @@ $BIBAccounts = new BIBAccounts();
                                 document.getElementById("showClosing").checked = <?php echo  "false"; ?>;
                                 document.getElementById("FilterForm").submit();
                             }
+
                         </script>
+                       
                     </div>
                 </div>
             </div>
@@ -228,7 +230,7 @@ $BIBAccounts = new BIBAccounts();
                                                 </tr>
                                             </thead>
                                             <tbody>
-                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances(3); ?>
+                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalancesNew(3,$fiscalStart,$fiscalEnd); ?>
                                             <?php
                                                 // Initialize variables for column totals
                                                 $incomes_totalOpeningDr = 0;
@@ -290,7 +292,7 @@ $BIBAccounts = new BIBAccounts();
                                                 </tr>
                                             </thead>
                                             <tbody>
-                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances(4); ?>
+                                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalancesNew(4,$fiscalStart,$fiscalEnd); ?>
                                             <?php
                                                 // Initialize variables for column totals
                                                 $totalOpeningDr = 0;
diff --git a/account/application/views/accounts/receipt_and_payment.php b/account/application/views/accounts/receipt_and_payment.php
index e06dc9f..80340d2 100644
--- a/account/application/views/accounts/receipt_and_payment.php
+++ b/account/application/views/accounts/receipt_and_payment.php
@@ -104,7 +104,7 @@ $PL = $IncomesTotal - $ExpensesTotal;
                     <h3 class="card-title mt-1">Receipts And Payments <?php //echo $pageTitle; 
                                                                         ?> </h3>
                     <div class="card-tools">
-                        <form method="post" action="" id="FilterForm">
+                        <form method="post" action=<?php echo base_url('accounts/reports/receipt_and_payment'); ?> id="FilterForm">
                             <!-- button with a dropdown -->
                             <div class="btn-group">
                                 <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown" data-offset="-52">
@@ -113,7 +113,7 @@ $PL = $IncomesTotal - $ExpensesTotal;
                                 <div class="dropdown-menu" role="menu">
                                     <div class="form-group p-2 pb-0">
                                         <div class="col">
-                                            <div class="form-group">
+                                            <div class="form-group"> 
                                                 <label for="fromDate"><?php myLang("Starting Period"); ?></label>
                                                 <input type="text" class="form-control " name="fromDate" value="<?php echo (isset($_POST['fromDate'])) ? $_POST['fromDate'] : NepaliDate($this->session->FiscalYear->fiscalyear_from); ?>" data-start="<?php echo $fiscalStart; ?>" data-end="<?php echo $fiscalEnd; ?>" id="fromDate" aria-describedby="helpId_fromDate" placeholder="Starting Period">
                                             </div>
@@ -234,7 +234,7 @@ $PL = $IncomesTotal - $ExpensesTotal;
             </div>
             <div class="card">
                 <div class="card card-primary card-outline">
-                    <?php $IncomesExpenses = $BIBAccounts->prepareIncomeExpenses() ?>
+                    <?php $IncomesExpenses = $BIBAccounts->prepareIncomeExpenses($fiscalStart,$fiscalEnd) ?>
                     <table class="table table-head-fixed table-bordered  g-0" id="TrialBalanceTable">
                         <thead>
                             <tr>
diff --git a/account/application/views/accounts/trialbalance_new.php b/account/application/views/accounts/trialbalance_new.php
index 8aba2ee..fcbb751 100644
--- a/account/application/views/accounts/trialbalance_new.php
+++ b/account/application/views/accounts/trialbalance_new.php
@@ -1,6 +1,7 @@
 <?php
 $this->load->library("BIBAccounts");
 $BIBAccounts = new BIBAccounts();
+
 ?>
 <!-- Include jQuery -->
 <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
@@ -26,7 +27,7 @@ $BIBAccounts = new BIBAccounts();
 
     .table-gray {
         background-color: #eee;
-    }
+    } 
 
     .table td,
     .table th {
@@ -107,7 +108,7 @@ $BIBAccounts = new BIBAccounts();
                                                                             ?></h2>
 
                     <div class="card-tools">
-                        <form method="post" action="" id="FilterForm">
+                        <form method="post" action=<?php echo site_url('/Accounts/Reports/trialbalance'); ?> id="FilterForm">
 
                             <!-- button with a dropdown -->
                             <div class="btn-group">
@@ -207,7 +208,7 @@ $BIBAccounts = new BIBAccounts();
                             );
                             $showZeroBalances = false;
                             ?>
-                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances(); ?>
+                            <?php $AccountGroups = $BIBAccounts->getAccountGroupsWithBalances();?>
                             <table class="table table-bordered vertical-borders" id="TrialBalanceTable">
                                 <thead class="bg-gray">
                                     <tr>
diff --git a/account/application/views/includes/sidebar.php b/account/application/views/includes/sidebar.php
index 27096c6..235244e 100644
--- a/account/application/views/includes/sidebar.php
+++ b/account/application/views/includes/sidebar.php
@@ -14,7 +14,7 @@ $subparam2 = $this->uri->segment(4);
     <!-- Sidebar user panel (optional) -->
     <div class="user-panel mt-3 pb-3 mb-3 d-flex">
       <div class="image"> <img src="<?php echo base_url(); ?>dist/img/user2-160x160.jpg" class="img-circle elevation-2" alt="User Image"> </div>
-      <div class="info"> <a href="#" class="d-block"><?php echo $this->session->userdata("CompanyName"); ?> (<?php echo ($this->session->userdata("FiscalYear")->fiscalyear_year); ?>)</a></div>
+      <div class="info"> <a href="#" class="d-block"><?php echo $this->session->userdata("CompanyName"); ?> (<?php echo ($this->session->userdata("FiscalYear")?->fiscalyear_year); ?>)</a></div>
     </div> <!-- SidebarSearch Form -->
     <div class="form-inline pb-2">
       <div class="input-group" data-widget="sidebar-search">
diff --git a/account/application/views/inventory/stocks/summary.php b/account/application/views/inventory/stocks/summary.php
index 7243e0d..3cca82f 100644
--- a/account/application/views/inventory/stocks/summary.php
+++ b/account/application/views/inventory/stocks/summary.php
@@ -94,8 +94,13 @@
                                                                                                                 ?></a> -->
                             </h5>
                             <div class="card-tools">
-                                <form method="post" action="" id="FilterForm">
+                                <!-- test -->
+
+                                <!-- test ends -->
+
+                                <form method="post" action=<?php echo site_url('/Inventory/Stocks/summary'); ?> id="FilterForm">
                                     <!-- button with a dropdown -->
+                                    <input type="hidden" name="stocklocations_id" value="1" />
                                     <div class="btn-group">
                                         <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown" data-offset="-52">
                                             Filters
@@ -106,6 +111,8 @@
                                                     <div class="form-group">
                                                         <label for="fromDate"><?php myLang("Starting Period"); ?></label>
                                                         <input type="text" class="form-control " name="fromDate" value="<?php echo (isset($_POST['fromDate'])) ? $_POST['fromDate'] : NepaliDate($this->session->FiscalYear->fiscalyear_from); ?>" data-start="<?php echo $fiscalStart; ?>" data-end="<?php echo $fiscalEnd; ?>" id="fromDate" aria-describedby="helpId_fromDate" placeholder="Starting Period">
+                                                        <!-- <input type="text" class="form-control " name="fromDate" value="" data-end="<?php echo $fiscalEnd; ?>" id="fromDate" aria-describedby="helpId_fromDate" placeholder="Starting Period"> -->
+
                                                     </div>
                                                     <div class="form-group">
                                                         <label for="toDate"><?php myLang("Ending Period"); ?></label>
@@ -123,6 +130,7 @@
                                                     <input type="checkbox" class="form-check-input " name="showOB" id="showOB" aria-describedby="helpId_showOB" placeholder="Show Opening Balance" <?php echo (isset($_POST['showOB'])) ? "CHECKED" : ""; ?>>
                                                     <!-- <input class="form-check-input" type="checkbox"> -->
                                                     <label class="form-check-label" for="showOB">Opening</label>
+                                                    StockRecords
                                                 </div>
                                                 <div class="form-check">
                                                     <!-- <input class="form-check-input" type="checkbox"> -->
@@ -201,27 +209,30 @@
                                     $STotal = 0;
                                     $CTotal = 0;
                                     foreach ($StockRecords as $index => $TableRow) :  ?>
-                                        <tr>
-                                            <td class="text-center"><?php echo $index + 1; ?></td>
-                                            <td><?php echo ($TableRow->Item) ? $TableRow->Item->title : "N/A"; ?></td>
-                                            <td><?php echo getFieldfromValue("tbl_units", "title", "unit_id", $TableRow->Item->units_id); ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Opening->qty; ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Opening->rate; ?></td>
-                                            <td><?php echo myCurrency($lTotal = $TableRow->Summary->Opening->amount);
-                                                $Total += $lTotal; ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Purchase->qty; ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Purchase->rate; ?></td>
-                                            <td><?php echo myCurrency($pTotal = $TableRow->Summary->Purchase->amount);
-                                                $PTotal += $pTotal; ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Sales->qty; ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Sales->rate; ?></td>
-                                            <td><?php echo myCurrency($sTotal = $TableRow->Summary->Sales->amount);
-                                                $STotal += $sTotal; ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Closing->qty; ?></td>
-                                            <td class="text-right"><?php echo $TableRow->Summary->Closing->rate; ?></td>
-                                            <td><?php echo myCurrency($cTotal = $TableRow->Summary->Closing->amount);
-                                                $CTotal += $cTotal; ?></td>
-                                        </tr>
+                                        <?php if ((count($TableRow->Item)) > 0):; ?>
+                                            <tr>
+                                                <td class="text-center"><?php echo $index + 1; ?></td>
+                                                <td><?php echo ($TableRow->Summary->Item->title) ? $TableRow->Summary->Item->title : "N/A"; ?></td>
+                                                <!-- $StockRecord->Summary->Unit->unit_id -->
+                                                <td><?php echo getFieldfromValue("tbl_units", "title", "unit_id", $TableRow->Summary->Unit->unit_id); ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Opening->qty; ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Opening->rate; ?></td>
+                                                <td><?php echo myCurrency($lTotal = $TableRow->Summary->Opening->amount);
+                                                    $Total += $lTotal; ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Purchase->qty; ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Purchase->rate; ?></td>
+                                                <td><?php echo myCurrency($pTotal = $TableRow->Summary->Purchase->amount);
+                                                    $PTotal += $pTotal; ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Sales->qty; ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Sales->rate; ?></td>
+                                                <td><?php echo myCurrency($sTotal = $TableRow->Summary->Sales->amount);
+                                                    $STotal += $sTotal; ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Closing->qty; ?></td>
+                                                <td class="text-right"><?php echo $TableRow->Summary->Closing->rate; ?></td>
+                                                <td><?php echo myCurrency($cTotal = $TableRow->Summary->Closing->amount);
+                                                    $CTotal += $cTotal; ?></td>
+                                            </tr>
+                                        <?php endif; ?>
                                     <?php endforeach; ?>
                                 <tbody>
                                 <tfoot>
@@ -234,7 +245,7 @@
                                         <td><?php echo myCurrency($STotal); ?></td>
                                         <td colspan="2"></td>
                                         <td><?php echo myCurrency($CTotal); ?></td>
-                                        
+
                                     </tr>
                                 </tfoot>
                             </table>
@@ -316,12 +327,11 @@
     </script>
     <script>
         $("#toDate").nepaliDatePicker({
-                dateFormat: "%y-%m-%d",
-                closeOnDateSelect: true,
-                minDate: $('#toDate').data('start'),
-                maxDate: $('#toDate').data('end'),
-            }
-        );
+            dateFormat: "%y-%m-%d",
+            closeOnDateSelect: true,
+            minDate: $('#toDate').data('start'),
+            maxDate: $('#toDate').data('end'),
+        });
     </script>
 <?php
 }
diff --git a/account/pdf/ledgers/ledger_2.wpdf b/account/pdf/ledgers/ledger_2.wpdf
index dfa4123..9076e0c 100644
Binary files a/account/pdf/ledgers/ledger_2.wpdf and b/account/pdf/ledgers/ledger_2.wpdf differ
diff --git a/account/pdf/ledgers/ledger_28.wpdf b/account/pdf/ledgers/ledger_28.wpdf
index 2c7b55d..aa14de4 100644
Binary files a/account/pdf/ledgers/ledger_28.wpdf and b/account/pdf/ledgers/ledger_28.wpdf differ
diff --git a/account/pdf/ledgers/ledger_33.wpdf b/account/pdf/ledgers/ledger_33.wpdf
index 3381107..18a5d5a 100644
Binary files a/account/pdf/ledgers/ledger_33.wpdf and b/account/pdf/ledgers/ledger_33.wpdf differ
diff --git a/account/projects_bibaccounts.sql b/account/projects_bibaccounts.sql
index 51737c1..1611f27 100644
--- a/account/projects_bibaccounts.sql
+++ b/account/projects_bibaccounts.sql
@@ -25,6 +25,7 @@ SET time_zone = "+00:00";
 
 --
 -- Table structure for table `tbl_accategories`
+
 --
 
 CREATE TABLE `tbl_accategories` (
diff --git a/application/config/routes.php b/application/config/routes.php
index 8463592..0453cb2 100644
--- a/application/config/routes.php
+++ b/application/config/routes.php
@@ -74,6 +74,12 @@ $route['student/admission_payment'] = 'Student/admission-payment';
 $route['student/get_installdetails'] = 'Student/get-installdetails';
 $route['student/paypalipn_admission'] = 'Student/paypalipn-admission';
 
+//inventory
+$route['inventory/stocks/summary'] = 'Inventory/Stocks/summary';
+
+
+//inventory ends
+
 $route['admin'] = 'Admin/index';
 $route['admin-logout'] = 'Admin/admin_logout';
 
diff --git a/composer.lock b/composer.lock
index 92bb82e..bd6eb8e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -8,26 +8,21 @@
     "packages": [
         {
             "name": "ernilambar/nepali-date",
-            "version": "1.0.5",
+            "version": "1.0.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/ernilambar/nepali-date.git",
-                "reference": "8ac91a49267e3821bf03f052d3cb6b7876af4a12"
+                "reference": "886dcb25b10760b4f9c35083d0a1d3ef6fb98584"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/ernilambar/nepali-date/zipball/8ac91a49267e3821bf03f052d3cb6b7876af4a12",
-                "reference": "8ac91a49267e3821bf03f052d3cb6b7876af4a12",
+                "url": "https://api.github.com/repos/ernilambar/nepali-date/zipball/886dcb25b10760b4f9c35083d0a1d3ef6fb98584",
+                "reference": "886dcb25b10760b4f9c35083d0a1d3ef6fb98584",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.6"
             },
-            "require-dev": {
-                "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2",
-                "phpunit/phpunit": "^9",
-                "squizlabs/php_codesniffer": "^3.5"
-            },
             "type": "library",
             "autoload": {
                 "psr-4": {
@@ -49,93 +44,34 @@
             "description": "Nepali Date",
             "homepage": "https://github.com/ernilambar/nepali-date",
             "keywords": [
-                "date"
+                "bikram-sambat",
+                "date",
+                "nepali"
             ],
             "support": {
                 "issues": "https://github.com/ernilambar/nepali-date/issues",
-                "source": "https://github.com/ernilambar/nepali-date/tree/1.0.5"
+                "source": "https://github.com/ernilambar/nepali-date/tree/1.0.7"
             },
-            "time": "2021-07-20T06:11:00+00:00"
-        },
-        {
-            "name": "ezyang/htmlpurifier",
-            "version": "v4.16.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/ezyang/htmlpurifier.git",
-                "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8",
-                "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8",
-                "shasum": ""
-            },
-            "require": {
-                "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
-            },
-            "require-dev": {
-                "cerdic/css-tidy": "^1.7 || ^2.0",
-                "simpletest/simpletest": "dev-master"
-            },
-            "suggest": {
-                "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
-                "ext-bcmath": "Used for unit conversion and imagecrash protection",
-                "ext-iconv": "Converts text to and from non-UTF-8 encodings",
-                "ext-tidy": "Used for pretty-printing HTML"
-            },
-            "type": "library",
-            "autoload": {
-                "files": [
-                    "library/HTMLPurifier.composer.php"
-                ],
-                "psr-0": {
-                    "HTMLPurifier": "library/"
-                },
-                "exclude-from-classmap": [
-                    "/library/HTMLPurifier/Language/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "LGPL-2.1-or-later"
-            ],
-            "authors": [
-                {
-                    "name": "Edward Z. Yang",
-                    "email": "admin@htmlpurifier.org",
-                    "homepage": "http://ezyang.com"
-                }
-            ],
-            "description": "Standards compliant HTML filter written in PHP",
-            "homepage": "http://htmlpurifier.org/",
-            "keywords": [
-                "html"
-            ],
-            "support": {
-                "issues": "https://github.com/ezyang/htmlpurifier/issues",
-                "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0"
-            },
-            "time": "2022-09-18T07:06:19+00:00"
+            "time": "2024-06-14T05:45:58+00:00"
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.5.0",
+            "version": "7.9.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
+                "reference": "d281ed313b989f213357e3be1a179f02196ac99b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
+                "reference": "d281ed313b989f213357e3be1a179f02196ac99b",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
-                "guzzlehttp/promises": "^1.5",
-                "guzzlehttp/psr7": "^1.9 || ^2.4",
+                "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+                "guzzlehttp/psr7": "^2.7.0",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
                 "symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -144,10 +80,11 @@
                 "psr/http-client-implementation": "1.0"
             },
             "require-dev": {
-                "bamarni/composer-bin-plugin": "^1.8.1",
+                "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
-                "php-http/client-integration-tests": "^3.0",
-                "phpunit/phpunit": "^8.5.29 || ^9.5.23",
+                "guzzle/client-integration-tests": "3.0.2",
+                "php-http/message-factory": "^1.1",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
             },
             "suggest": {
@@ -160,9 +97,6 @@
                 "bamarni-bin": {
                     "bin-links": true,
                     "forward-command": false
-                },
-                "branch-alias": {
-                    "dev-master": "7.5-dev"
                 }
             },
             "autoload": {
@@ -228,7 +162,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.5.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.9.2"
             },
             "funding": [
                 {
@@ -244,38 +178,37 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-08-28T15:39:27+00:00"
+            "time": "2024-07-24T11:22:20+00:00"
         },
         {
             "name": "guzzlehttp/promises",
-            "version": "1.5.2",
+            "version": "2.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/promises.git",
-                "reference": "b94b2807d85443f9719887892882d0329d1e2598"
+                "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
-                "reference": "b94b2807d85443f9719887892882d0329d1e2598",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8",
+                "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5"
+                "php": "^7.2.5 || ^8.0"
             },
             "require-dev": {
-                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20"
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-master": "1.5-dev"
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
                 }
             },
             "autoload": {
-                "files": [
-                    "src/functions_include.php"
-                ],
                 "psr-4": {
                     "GuzzleHttp\\Promise\\": "src/"
                 }
@@ -312,7 +245,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/promises/issues",
-                "source": "https://github.com/guzzle/promises/tree/1.5.2"
+                "source": "https://github.com/guzzle/promises/tree/2.0.3"
             },
             "funding": [
                 {
@@ -328,26 +261,26 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-08-28T14:55:35+00:00"
+            "time": "2024-07-18T10:29:17+00:00"
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.4.3",
+            "version": "2.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "67c26b443f348a51926030c83481b85718457d3d"
+                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d",
-                "reference": "67c26b443f348a51926030c83481b85718457d3d",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-factory": "^1.0",
-                "psr/http-message": "^1.0",
+                "psr/http-message": "^1.1 || ^2.0",
                 "ralouphie/getallheaders": "^3.0"
             },
             "provide": {
@@ -355,9 +288,9 @@
                 "psr/http-message-implementation": "1.0"
             },
             "require-dev": {
-                "bamarni/composer-bin-plugin": "^1.8.1",
-                "http-interop/http-factory-tests": "^0.9",
-                "phpunit/phpunit": "^8.5.29 || ^9.5.23"
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "http-interop/http-factory-tests": "0.9.0",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20"
             },
             "suggest": {
                 "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@@ -367,9 +300,6 @@
                 "bamarni-bin": {
                     "bin-links": true,
                     "forward-command": false
-                },
-                "branch-alias": {
-                    "dev-master": "2.4-dev"
                 }
             },
             "autoload": {
@@ -431,7 +361,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.4.3"
+                "source": "https://github.com/guzzle/psr7/tree/2.7.0"
             },
             "funding": [
                 {
@@ -447,36 +377,39 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-10-26T14:07:24+00:00"
+            "time": "2024-07-18T11:15:46+00:00"
         },
         {
             "name": "maennchen/zipstream-php",
-            "version": "2.2.6",
+            "version": "3.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/maennchen/ZipStream-PHP.git",
-                "reference": "30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f"
+                "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f",
-                "reference": "30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f",
+                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
+                "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
                 "shasum": ""
             },
             "require": {
-                "myclabs/php-enum": "^1.5",
-                "php": "^7.4 || ^8.0",
-                "psr/http-message": "^1.0",
-                "symfony/polyfill-mbstring": "^1.0"
+                "ext-mbstring": "*",
+                "ext-zlib": "*",
+                "php-64bit": "^8.1"
             },
             "require-dev": {
                 "ext-zip": "*",
-                "friendsofphp/php-cs-fixer": "^3.9",
-                "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+                "friendsofphp/php-cs-fixer": "^3.16",
+                "guzzlehttp/guzzle": "^7.5",
                 "mikey179/vfsstream": "^1.6",
-                "php-coveralls/php-coveralls": "^2.4",
-                "phpunit/phpunit": "^8.5.8 || ^9.4.2",
-                "vimeo/psalm": "^4.1"
+                "php-coveralls/php-coveralls": "^2.5",
+                "phpunit/phpunit": "^10.0",
+                "vimeo/psalm": "^5.0"
+            },
+            "suggest": {
+                "guzzlehttp/psr7": "^2.4",
+                "psr/http-message": "^2.0"
             },
             "type": "library",
             "autoload": {
@@ -513,7 +446,7 @@
             ],
             "support": {
                 "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
-                "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.2.6"
+                "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0"
             },
             "funding": [
                 {
@@ -525,7 +458,7 @@
                     "type": "open_collective"
                 }
             ],
-            "time": "2022-11-25T18:57:19+00:00"
+            "time": "2023-06-21T14:59:35+00:00"
         },
         {
             "name": "markbaker/complex",
@@ -634,69 +567,6 @@
             },
             "time": "2022-12-02T22:17:43+00:00"
         },
-        {
-            "name": "myclabs/php-enum",
-            "version": "1.8.4",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/myclabs/php-enum.git",
-                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
-                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
-                "shasum": ""
-            },
-            "require": {
-                "ext-json": "*",
-                "php": "^7.3 || ^8.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.5",
-                "squizlabs/php_codesniffer": "1.*",
-                "vimeo/psalm": "^4.6.2"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "MyCLabs\\Enum\\": "src/"
-                },
-                "classmap": [
-                    "stubs/Stringable.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "PHP Enum contributors",
-                    "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
-                }
-            ],
-            "description": "PHP Enum implementation",
-            "homepage": "http://github.com/myclabs/php-enum",
-            "keywords": [
-                "enum"
-            ],
-            "support": {
-                "issues": "https://github.com/myclabs/php-enum/issues",
-                "source": "https://github.com/myclabs/php-enum/tree/1.8.4"
-            },
-            "funding": [
-                {
-                    "url": "https://github.com/mnapoli",
-                    "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
-                    "type": "tidelift"
-                }
-            ],
-            "time": "2022-08-04T09:53:51+00:00"
-        },
         {
             "name": "paragonie/random_compat",
             "version": "v9.99.100",
@@ -749,16 +619,16 @@
         },
         {
             "name": "paragonie/sodium_compat",
-            "version": "v1.19.0",
+            "version": "v1.21.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/paragonie/sodium_compat.git",
-                "reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933"
+                "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/cb15e403ecbe6a6cc515f855c310eb6b1872a933",
-                "reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933",
+                "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bb312875dcdd20680419564fe42ba1d9564b9e37",
+                "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37",
                 "shasum": ""
             },
             "require": {
@@ -829,22 +699,22 @@
             ],
             "support": {
                 "issues": "https://github.com/paragonie/sodium_compat/issues",
-                "source": "https://github.com/paragonie/sodium_compat/tree/v1.19.0"
+                "source": "https://github.com/paragonie/sodium_compat/tree/v1.21.1"
             },
-            "time": "2022-09-26T03:40:35+00:00"
+            "time": "2024-04-22T22:05:04+00:00"
         },
         {
             "name": "phpoffice/phpspreadsheet",
-            "version": "1.26.0",
+            "version": "2.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
-                "reference": "5b6ceea9705b068f993e268e4debc566c2637063"
+                "reference": "ffbcee68069b073bff07a71eb321dcd9f2763513"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/5b6ceea9705b068f993e268e4debc566c2637063",
-                "reference": "5b6ceea9705b068f993e268e4debc566c2637063",
+                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/ffbcee68069b073bff07a71eb321dcd9f2763513",
+                "reference": "ffbcee68069b073bff07a71eb321dcd9f2763513",
                 "shasum": ""
             },
             "require": {
@@ -861,25 +731,24 @@
                 "ext-xmlwriter": "*",
                 "ext-zip": "*",
                 "ext-zlib": "*",
-                "ezyang/htmlpurifier": "^4.15",
-                "maennchen/zipstream-php": "^2.1",
+                "maennchen/zipstream-php": "^2.1 || ^3.0",
                 "markbaker/complex": "^3.0",
                 "markbaker/matrix": "^3.0",
-                "php": "^7.4 || ^8.0",
+                "php": "^8.1",
                 "psr/http-client": "^1.0",
                 "psr/http-factory": "^1.0",
                 "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
             },
             "require-dev": {
-                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
-                "dompdf/dompdf": "^1.0 || ^2.0",
+                "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+                "dompdf/dompdf": "^2.0 || ^3.0",
                 "friendsofphp/php-cs-fixer": "^3.2",
-                "mitoteam/jpgraph": "^10.2.4",
+                "mitoteam/jpgraph": "^10.3",
                 "mpdf/mpdf": "^8.1.1",
                 "phpcompatibility/php-compatibility": "^9.3",
                 "phpstan/phpstan": "^1.1",
                 "phpstan/phpstan-phpunit": "^1.0",
-                "phpunit/phpunit": "^8.5 || ^9.0",
+                "phpunit/phpunit": "^9.6 || ^10.5",
                 "squizlabs/php_codesniffer": "^3.7",
                 "tecnickcom/tcpdf": "^6.5"
             },
@@ -934,27 +803,27 @@
             ],
             "support": {
                 "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
-                "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.26.0"
+                "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.2.2"
             },
-            "time": "2022-12-21T12:22:06+00:00"
+            "time": "2024-08-08T02:31:26+00:00"
         },
         {
             "name": "psr/http-client",
-            "version": "1.0.1",
+            "version": "1.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-client.git",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.0 || ^8.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
             "extra": {
@@ -974,7 +843,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP clients",
@@ -986,27 +855,27 @@
                 "psr-18"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-client/tree/master"
+                "source": "https://github.com/php-fig/http-client"
             },
-            "time": "2020-06-29T06:28:15+00:00"
+            "time": "2023-09-23T14:17:50+00:00"
         },
         {
             "name": "psr/http-factory",
-            "version": "1.0.1",
+            "version": "1.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-factory.git",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.0.0",
-                "psr/http-message": "^1.0"
+                "php": ">=7.1",
+                "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
             "extra": {
@@ -1026,10 +895,10 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
-            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
             "keywords": [
                 "factory",
                 "http",
@@ -1041,31 +910,31 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-factory/tree/master"
+                "source": "https://github.com/php-fig/http-factory"
             },
-            "time": "2019-04-30T12:38:16+00:00"
+            "time": "2024-04-15T12:06:14+00:00"
         },
         {
             "name": "psr/http-message",
-            "version": "1.0.1",
+            "version": "2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-message.git",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
@@ -1080,7 +949,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP messages",
@@ -1094,36 +963,36 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-message/tree/master"
+                "source": "https://github.com/php-fig/http-message/tree/2.0"
             },
-            "time": "2016-08-06T14:39:51+00:00"
+            "time": "2023-04-04T09:54:51+00:00"
         },
         {
             "name": "psr/log",
-            "version": "1.1.4",
+            "version": "3.0.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/log.git",
-                "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
-                "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": ">=8.0.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1.x-dev"
+                    "dev-master": "3.x-dev"
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "Psr\\Log\\": "Psr/Log/"
+                    "Psr\\Log\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -1144,31 +1013,31 @@
                 "psr-3"
             ],
             "support": {
-                "source": "https://github.com/php-fig/log/tree/1.1.4"
+                "source": "https://github.com/php-fig/log/tree/3.0.2"
             },
-            "time": "2021-05-03T11:20:27+00:00"
+            "time": "2024-09-11T13:17:53+00:00"
         },
         {
             "name": "psr/simple-cache",
-            "version": "1.0.1",
+            "version": "3.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/simple-cache.git",
-                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
-                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": ">=8.0.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "3.0.x-dev"
                 }
             },
             "autoload": {
@@ -1183,7 +1052,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interfaces for simple caching",
@@ -1195,22 +1064,22 @@
                 "simple-cache"
             ],
             "support": {
-                "source": "https://github.com/php-fig/simple-cache/tree/master"
+                "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
             },
-            "time": "2017-10-23T01:57:42+00:00"
+            "time": "2021-10-29T13:26:27+00:00"
         },
         {
             "name": "pusher/pusher-php-server",
-            "version": "7.2.2",
+            "version": "7.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/pusher/pusher-http-php.git",
-                "reference": "4ace4873873b06c25cecb2dd6d9fdcbf2f20b640"
+                "reference": "de2f72296808f9cafa6a4462b15a768ff130cddb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/4ace4873873b06c25cecb2dd6d9fdcbf2f20b640",
-                "reference": "4ace4873873b06c25cecb2dd6d9fdcbf2f20b640",
+                "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/de2f72296808f9cafa6a4462b15a768ff130cddb",
+                "reference": "de2f72296808f9cafa6a4462b15a768ff130cddb",
                 "shasum": ""
             },
             "require": {
@@ -1256,9 +1125,9 @@
             ],
             "support": {
                 "issues": "https://github.com/pusher/pusher-http-php/issues",
-                "source": "https://github.com/pusher/pusher-http-php/tree/7.2.2"
+                "source": "https://github.com/pusher/pusher-http-php/tree/7.2.4"
             },
-            "time": "2022-12-20T19:52:36+00:00"
+            "time": "2023-12-15T10:58:53+00:00"
         },
         {
             "name": "ralouphie/getallheaders",
@@ -1306,25 +1175,25 @@
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v2.5.2",
+            "version": "v3.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
+                "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
-                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+                "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
+                "php": ">=8.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "2.5-dev"
+                    "dev-main": "3.5-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -1353,7 +1222,7 @@
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
             },
             "funding": [
                 {
@@ -1369,93 +1238,57 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-01-02T09:53:40+00:00"
-        },
+            "time": "2024-04-18T09:32:20+00:00"
+        }
+    ],
+    "packages-dev": [
         {
-            "name": "symfony/polyfill-mbstring",
-            "version": "v1.27.0",
+            "name": "doctrine/deprecations",
+            "version": "1.1.3",
             "source": {
                 "type": "git",
-                "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
+                "url": "https://github.com/doctrine/deprecations.git",
+                "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
-                "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+                "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
+                "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
+                "php": "^7.1 || ^8.0"
             },
-            "provide": {
-                "ext-mbstring": "*"
+            "require-dev": {
+                "doctrine/coding-standard": "^9",
+                "phpstan/phpstan": "1.4.10 || 1.10.15",
+                "phpstan/phpstan-phpunit": "^1.0",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+                "psalm/plugin-phpunit": "0.18.4",
+                "psr/log": "^1 || ^2 || ^3",
+                "vimeo/psalm": "4.30.0 || 5.12.0"
             },
             "suggest": {
-                "ext-mbstring": "For best performance"
+                "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-main": "1.27-dev"
-                },
-                "thanks": {
-                    "name": "symfony/polyfill",
-                    "url": "https://github.com/symfony/polyfill"
-                }
-            },
             "autoload": {
-                "files": [
-                    "bootstrap.php"
-                ],
                 "psr-4": {
-                    "Symfony\\Polyfill\\Mbstring\\": ""
+                    "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "MIT"
             ],
-            "authors": [
-                {
-                    "name": "Nicolas Grekas",
-                    "email": "p@tchwork.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "Symfony polyfill for the Mbstring extension",
-            "homepage": "https://symfony.com",
-            "keywords": [
-                "compatibility",
-                "mbstring",
-                "polyfill",
-                "portable",
-                "shim"
-            ],
+            "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+            "homepage": "https://www.doctrine-project.org/",
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
+                "issues": "https://github.com/doctrine/deprecations/issues",
+                "source": "https://github.com/doctrine/deprecations/tree/1.1.3"
             },
-            "funding": [
-                {
-                    "url": "https://symfony.com/sponsor",
-                    "type": "custom"
-                },
-                {
-                    "url": "https://github.com/fabpot",
-                    "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
-                    "type": "tidelift"
-                }
-            ],
-            "time": "2022-11-03T14:55:06+00:00"
-        }
-    ],
-    "packages-dev": [
+            "time": "2024-01-30T19:34:25+00:00"
+        },
         {
             "name": "doctrine/instantiator",
             "version": "1.5.0",
@@ -1562,16 +1395,16 @@
         },
         {
             "name": "myclabs/deep-copy",
-            "version": "1.11.0",
+            "version": "1.12.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
+                "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
-                "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
+                "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
                 "shasum": ""
             },
             "require": {
@@ -1579,11 +1412,12 @@
             },
             "conflict": {
                 "doctrine/collections": "<1.6.8",
-                "doctrine/common": "<2.13.3 || >=3,<3.2.2"
+                "doctrine/common": "<2.13.3 || >=3 <3.2.2"
             },
             "require-dev": {
                 "doctrine/collections": "^1.6.8",
                 "doctrine/common": "^2.13.3 || ^3.2.2",
+                "phpspec/prophecy": "^1.10",
                 "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
             },
             "type": "library",
@@ -1609,7 +1443,7 @@
             ],
             "support": {
                 "issues": "https://github.com/myclabs/DeepCopy/issues",
-                "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
+                "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
             },
             "funding": [
                 {
@@ -1617,7 +1451,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-03-03T13:19:32+00:00"
+            "time": "2024-06-12T14:39:25+00:00"
         },
         {
             "name": "phpdocumentor/reflection-common",
@@ -1674,28 +1508,35 @@
         },
         {
             "name": "phpdocumentor/reflection-docblock",
-            "version": "5.3.0",
+            "version": "5.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+                "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
-                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
+                "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
                 "shasum": ""
             },
             "require": {
+                "doctrine/deprecations": "^1.1",
                 "ext-filter": "*",
-                "php": "^7.2 || ^8.0",
+                "php": "^7.4 || ^8.0",
                 "phpdocumentor/reflection-common": "^2.2",
-                "phpdocumentor/type-resolver": "^1.3",
+                "phpdocumentor/type-resolver": "^1.7",
+                "phpstan/phpdoc-parser": "^1.7",
                 "webmozart/assert": "^1.9.1"
             },
             "require-dev": {
-                "mockery/mockery": "~1.3.2",
-                "psalm/phar": "^4.8"
+                "mockery/mockery": "~1.3.5",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan": "^1.8",
+                "phpstan/phpstan-mockery": "^1.1",
+                "phpstan/phpstan-webmozart-assert": "^1.2",
+                "phpunit/phpunit": "^9.5",
+                "vimeo/psalm": "^5.13"
             },
             "type": "library",
             "extra": {
@@ -1719,36 +1560,39 @@
                 },
                 {
                     "name": "Jaap van Otterdijk",
-                    "email": "account@ijaap.nl"
+                    "email": "opensource@ijaap.nl"
                 }
             ],
             "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
             "support": {
                 "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
-                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1"
             },
-            "time": "2021-10-19T17:43:47+00:00"
+            "time": "2024-05-21T05:55:05+00:00"
         },
         {
             "name": "phpdocumentor/type-resolver",
-            "version": "1.6.2",
+            "version": "1.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
+                "reference": "153ae662783729388a584b4361f2545e4d841e3c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
-                "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c",
+                "reference": "153ae662783729388a584b4361f2545e4d841e3c",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.4 || ^8.0",
-                "phpdocumentor/reflection-common": "^2.0"
+                "doctrine/deprecations": "^1.0",
+                "php": "^7.3 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.0",
+                "phpstan/phpdoc-parser": "^1.13"
             },
             "require-dev": {
                 "ext-tokenizer": "*",
+                "phpbench/phpbench": "^1.2",
                 "phpstan/extension-installer": "^1.1",
                 "phpstan/phpstan": "^1.8",
                 "phpstan/phpstan-phpunit": "^1.1",
@@ -1780,9 +1624,9 @@
             "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
             "support": {
                 "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
-                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
+                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2"
             },
-            "time": "2022-10-14T12:47:21+00:00"
+            "time": "2024-02-23T11:10:43+00:00"
         },
         {
             "name": "phpspec/prophecy",
@@ -1851,6 +1695,53 @@
             },
             "time": "2020-03-05T15:02:03+00:00"
         },
+        {
+            "name": "phpstan/phpdoc-parser",
+            "version": "1.31.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpdoc-parser.git",
+                "reference": "249f15fb843bf240cf058372dad29e100cee6c17"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/249f15fb843bf240cf058372dad29e100cee6c17",
+                "reference": "249f15fb843bf240cf058372dad29e100cee6c17",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/annotations": "^2.0",
+                "nikic/php-parser": "^4.15",
+                "php-parallel-lint/php-parallel-lint": "^1.2",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^1.5",
+                "phpstan/phpstan-phpunit": "^1.1",
+                "phpstan/phpstan-strict-rules": "^1.0",
+                "phpunit/phpunit": "^9.5",
+                "symfony/process": "^5.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PHPStan\\PhpDocParser\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPDoc parser with support for nullable, intersection and generic types",
+            "support": {
+                "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.31.0"
+            },
+            "time": "2024-09-22T11:32:18+00:00"
+        },
         {
             "name": "phpunit/php-code-coverage",
             "version": "4.0.8",
@@ -2276,16 +2167,16 @@
         },
         {
             "name": "sebastian/code-unit-reverse-lookup",
-            "version": "1.0.2",
+            "version": "1.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
-                "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
+                "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
-                "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
+                "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
                 "shasum": ""
             },
             "require": {
@@ -2319,7 +2210,7 @@
             "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
             "support": {
                 "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
-                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
+                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.3"
             },
             "funding": [
                 {
@@ -2327,7 +2218,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-11-30T08:15:22+00:00"
+            "time": "2024-03-01T13:45:45+00:00"
         },
         {
             "name": "sebastian/comparator",
@@ -2835,20 +2726,20 @@
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.27.0",
+            "version": "v1.31.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
-                "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
+                "php": ">=7.2"
             },
             "provide": {
                 "ext-ctype": "*"
@@ -2858,9 +2749,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.27-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -2897,7 +2785,7 @@
                 "portable"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
             },
             "funding": [
                 {
@@ -2913,7 +2801,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-11-03T14:55:06+00:00"
+            "time": "2024-09-09T11:45:10+00:00"
         },
         {
             "name": "symfony/yaml",
@@ -3054,5 +2942,5 @@
         "php": ">=5.3.7"
     },
     "platform-dev": [],
-    "plugin-api-version": "2.3.0"
+    "plugin-api-version": "2.6.0"
 }
diff --git a/env (2) b/env (2)
index c827909..7bb4286 100644
--- a/env (2)	
+++ b/env (2)	
@@ -3,7 +3,7 @@ APP_URL=http://localhost/bbnepal/BBnepal-Accounts
 DB_CONNECTION=mysql
 DB_HOST=127.0.0.1:3309
 DB_PORT=3309
-DATABASE_OPTIONS=bbnepal_accounting,myurlsco_accounts1,myurlsco_accounts2,myurlsco_accounts3
+DATABASE_OPTIONS=bbnepal_accounting,myurlsco_accounts1,myurlsco_accounts2,myurlsco_accounts3  #given
 DB_DATABASE=bbnepal_accounting
 DB_USERNAME=root
 DB_PASSWORD=
\ No newline at end of file
diff --git a/vendor/autoload.php b/vendor/autoload.php
index 887784d..b8d3d1d 100644
--- a/vendor/autoload.php
+++ b/vendor/autoload.php
@@ -3,8 +3,21 @@
 // autoload.php @generated by Composer
 
 if (PHP_VERSION_ID < 50600) {
-    echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
-    exit(1);
+    if (!headers_sent()) {
+        header('HTTP/1.1 500 Internal Server Error');
+    }
+    $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
+    if (!ini_get('display_errors')) {
+        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+            fwrite(STDERR, $err);
+        } elseif (!headers_sent()) {
+            echo $err;
+        }
+    }
+    trigger_error(
+        $err,
+        E_USER_ERROR
+    );
 }
 
 require_once __DIR__ . '/composer/autoload_real.php';
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
index afef3fa..7824d8f 100644
--- a/vendor/composer/ClassLoader.php
+++ b/vendor/composer/ClassLoader.php
@@ -42,35 +42,37 @@ namespace Composer\Autoload;
  */
 class ClassLoader
 {
-    /** @var ?string */
+    /** @var \Closure(string):void */
+    private static $includeFile;
+
+    /** @var string|null */
     private $vendorDir;
 
     // PSR-4
     /**
-     * @var array[]
-     * @psalm-var array<string, array<string, int>>
+     * @var array<string, array<string, int>>
      */
     private $prefixLengthsPsr4 = array();
     /**
-     * @var array[]
-     * @psalm-var array<string, array<int, string>>
+     * @var array<string, list<string>>
      */
     private $prefixDirsPsr4 = array();
     /**
-     * @var array[]
-     * @psalm-var array<string, string>
+     * @var list<string>
      */
     private $fallbackDirsPsr4 = array();
 
     // PSR-0
     /**
-     * @var array[]
-     * @psalm-var array<string, array<string, string[]>>
+     * List of PSR-0 prefixes
+     *
+     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+     *
+     * @var array<string, array<string, list<string>>>
      */
     private $prefixesPsr0 = array();
     /**
-     * @var array[]
-     * @psalm-var array<string, string>
+     * @var list<string>
      */
     private $fallbackDirsPsr0 = array();
 
@@ -78,8 +80,7 @@ class ClassLoader
     private $useIncludePath = false;
 
     /**
-     * @var string[]
-     * @psalm-var array<string, string>
+     * @var array<string, string>
      */
     private $classMap = array();
 
@@ -87,29 +88,29 @@ class ClassLoader
     private $classMapAuthoritative = false;
 
     /**
-     * @var bool[]
-     * @psalm-var array<string, bool>
+     * @var array<string, bool>
      */
     private $missingClasses = array();
 
-    /** @var ?string */
+    /** @var string|null */
     private $apcuPrefix;
 
     /**
-     * @var self[]
+     * @var array<string, self>
      */
     private static $registeredLoaders = array();
 
     /**
-     * @param ?string $vendorDir
+     * @param string|null $vendorDir
      */
     public function __construct($vendorDir = null)
     {
         $this->vendorDir = $vendorDir;
+        self::initializeIncludeClosure();
     }
 
     /**
-     * @return string[]
+     * @return array<string, list<string>>
      */
     public function getPrefixes()
     {
@@ -121,8 +122,7 @@ class ClassLoader
     }
 
     /**
-     * @return array[]
-     * @psalm-return array<string, array<int, string>>
+     * @return array<string, list<string>>
      */
     public function getPrefixesPsr4()
     {
@@ -130,8 +130,7 @@ class ClassLoader
     }
 
     /**
-     * @return array[]
-     * @psalm-return array<string, string>
+     * @return list<string>
      */
     public function getFallbackDirs()
     {
@@ -139,8 +138,7 @@ class ClassLoader
     }
 
     /**
-     * @return array[]
-     * @psalm-return array<string, string>
+     * @return list<string>
      */
     public function getFallbackDirsPsr4()
     {
@@ -148,8 +146,7 @@ class ClassLoader
     }
 
     /**
-     * @return string[] Array of classname => path
-     * @psalm-return array<string, string>
+     * @return array<string, string> Array of classname => path
      */
     public function getClassMap()
     {
@@ -157,8 +154,7 @@ class ClassLoader
     }
 
     /**
-     * @param string[] $classMap Class to filename map
-     * @psalm-param array<string, string> $classMap
+     * @param array<string, string> $classMap Class to filename map
      *
      * @return void
      */
@@ -175,24 +171,25 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix, either
      * appending or prepending to the ones previously set for this prefix.
      *
-     * @param string          $prefix  The prefix
-     * @param string[]|string $paths   The PSR-0 root directories
-     * @param bool            $prepend Whether to prepend the directories
+     * @param string              $prefix  The prefix
+     * @param list<string>|string $paths   The PSR-0 root directories
+     * @param bool                $prepend Whether to prepend the directories
      *
      * @return void
      */
     public function add($prefix, $paths, $prepend = false)
     {
+        $paths = (array) $paths;
         if (!$prefix) {
             if ($prepend) {
                 $this->fallbackDirsPsr0 = array_merge(
-                    (array) $paths,
+                    $paths,
                     $this->fallbackDirsPsr0
                 );
             } else {
                 $this->fallbackDirsPsr0 = array_merge(
                     $this->fallbackDirsPsr0,
-                    (array) $paths
+                    $paths
                 );
             }
 
@@ -201,19 +198,19 @@ class ClassLoader
 
         $first = $prefix[0];
         if (!isset($this->prefixesPsr0[$first][$prefix])) {
-            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+            $this->prefixesPsr0[$first][$prefix] = $paths;
 
             return;
         }
         if ($prepend) {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
-                (array) $paths,
+                $paths,
                 $this->prefixesPsr0[$first][$prefix]
             );
         } else {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
                 $this->prefixesPsr0[$first][$prefix],
-                (array) $paths
+                $paths
             );
         }
     }
@@ -222,9 +219,9 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace, either
      * appending or prepending to the ones previously set for this namespace.
      *
-     * @param string          $prefix  The prefix/namespace, with trailing '\\'
-     * @param string[]|string $paths   The PSR-4 base directories
-     * @param bool            $prepend Whether to prepend the directories
+     * @param string              $prefix  The prefix/namespace, with trailing '\\'
+     * @param list<string>|string $paths   The PSR-4 base directories
+     * @param bool                $prepend Whether to prepend the directories
      *
      * @throws \InvalidArgumentException
      *
@@ -232,17 +229,18 @@ class ClassLoader
      */
     public function addPsr4($prefix, $paths, $prepend = false)
     {
+        $paths = (array) $paths;
         if (!$prefix) {
             // Register directories for the root namespace.
             if ($prepend) {
                 $this->fallbackDirsPsr4 = array_merge(
-                    (array) $paths,
+                    $paths,
                     $this->fallbackDirsPsr4
                 );
             } else {
                 $this->fallbackDirsPsr4 = array_merge(
                     $this->fallbackDirsPsr4,
-                    (array) $paths
+                    $paths
                 );
             }
         } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@@ -252,18 +250,18 @@ class ClassLoader
                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
             }
             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
-            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+            $this->prefixDirsPsr4[$prefix] = $paths;
         } elseif ($prepend) {
             // Prepend directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
-                (array) $paths,
+                $paths,
                 $this->prefixDirsPsr4[$prefix]
             );
         } else {
             // Append directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
                 $this->prefixDirsPsr4[$prefix],
-                (array) $paths
+                $paths
             );
         }
     }
@@ -272,8 +270,8 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix,
      * replacing any others previously set for this prefix.
      *
-     * @param string          $prefix The prefix
-     * @param string[]|string $paths  The PSR-0 base directories
+     * @param string              $prefix The prefix
+     * @param list<string>|string $paths  The PSR-0 base directories
      *
      * @return void
      */
@@ -290,8 +288,8 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace,
      * replacing any others previously set for this namespace.
      *
-     * @param string          $prefix The prefix/namespace, with trailing '\\'
-     * @param string[]|string $paths  The PSR-4 base directories
+     * @param string              $prefix The prefix/namespace, with trailing '\\'
+     * @param list<string>|string $paths  The PSR-4 base directories
      *
      * @throws \InvalidArgumentException
      *
@@ -425,7 +423,8 @@ class ClassLoader
     public function loadClass($class)
     {
         if ($file = $this->findFile($class)) {
-            includeFile($file);
+            $includeFile = self::$includeFile;
+            $includeFile($file);
 
             return true;
         }
@@ -476,9 +475,9 @@ class ClassLoader
     }
 
     /**
-     * Returns the currently registered loaders indexed by their corresponding vendor directories.
+     * Returns the currently registered loaders keyed by their corresponding vendor directories.
      *
-     * @return self[]
+     * @return array<string, self>
      */
     public static function getRegisteredLoaders()
     {
@@ -555,18 +554,26 @@ class ClassLoader
 
         return false;
     }
-}
 
-/**
- * Scope isolated include.
- *
- * Prevents access to $this/self from included files.
- *
- * @param  string $file
- * @return void
- * @private
- */
-function includeFile($file)
-{
-    include $file;
+    /**
+     * @return void
+     */
+    private static function initializeIncludeClosure()
+    {
+        if (self::$includeFile !== null) {
+            return;
+        }
+
+        /**
+         * Scope isolated include.
+         *
+         * Prevents access to $this/self from included files.
+         *
+         * @param  string $file
+         * @return void
+         */
+        self::$includeFile = \Closure::bind(static function($file) {
+            include $file;
+        }, null, null);
+    }
 }
diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php
index c6b54af..51e734a 100644
--- a/vendor/composer/InstalledVersions.php
+++ b/vendor/composer/InstalledVersions.php
@@ -98,7 +98,7 @@ class InstalledVersions
     {
         foreach (self::getInstalled() as $installed) {
             if (isset($installed['versions'][$packageName])) {
-                return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
             }
         }
 
@@ -119,7 +119,7 @@ class InstalledVersions
      */
     public static function satisfies(VersionParser $parser, $packageName, $constraint)
     {
-        $constraint = $parser->parseConstraints($constraint);
+        $constraint = $parser->parseConstraints((string) $constraint);
         $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
 
         return $provided->matches($constraint);
@@ -328,7 +328,9 @@ class InstalledVersions
                 if (isset(self::$installedByVendor[$vendorDir])) {
                     $installed[] = self::$installedByVendor[$vendorDir];
                 } elseif (is_file($vendorDir.'/composer/installed.php')) {
-                    $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+                    $required = require $vendorDir.'/composer/installed.php';
+                    $installed[] = self::$installedByVendor[$vendorDir] = $required;
                     if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
                         self::$installed = $installed[count($installed) - 1];
                     }
@@ -340,12 +342,17 @@ class InstalledVersions
             // only require the installed.php file if this file is loaded from its dumped location,
             // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
             if (substr(__DIR__, -8, 1) !== 'C') {
-                self::$installed = require __DIR__ . '/installed.php';
+                /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+                $required = require __DIR__ . '/installed.php';
+                self::$installed = $required;
             } else {
                 self::$installed = array();
             }
         }
-        $installed[] = self::$installed;
+
+        if (self::$installed !== array()) {
+            $installed[] = self::$installed;
+        }
 
         return $installed;
     }
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index e739749..8db98fd 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -471,6 +471,5 @@ return array(
     'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => $vendorDir . '/sebastian/recursion-context/src/InvalidArgumentException.php',
     'SebastianBergmann\\ResourceOperations\\ResourceOperations' => $vendorDir . '/sebastian/resource-operations/src/ResourceOperations.php',
     'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php',
-    'Stringable' => $vendorDir . '/myclabs/php-enum/stubs/Stringable.php',
     'Text_Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php',
 );
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
index 3c43096..913ada5 100644
--- a/vendor/composer/autoload_files.php
+++ b/vendor/composer/autoload_files.php
@@ -7,11 +7,8 @@ $baseDir = dirname($vendorDir);
 
 return array(
     '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
-    'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
     '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
     '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
-    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
-    '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
     '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
     '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
     '3109cb1a231dcd04bee1f9f620d46975' => $vendorDir . '/paragonie/sodium_compat/autoload.php',
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
index be00363..74a4ed5 100644
--- a/vendor/composer/autoload_namespaces.php
+++ b/vendor/composer/autoload_namespaces.php
@@ -7,5 +7,4 @@ $baseDir = dirname($vendorDir);
 
 return array(
     'org\\bovigo\\vfs' => array($vendorDir . '/mikey179/vfsstream/src/main/php'),
-    'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
 );
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
index e89f08e..d6d872a 100644
--- a/vendor/composer/autoload_psr4.php
+++ b/vendor/composer/autoload_psr4.php
@@ -9,23 +9,23 @@ return array(
     'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'),
     'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'),
     'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
-    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
     'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
     'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
     'Pusher\\' => array($vendorDir . '/pusher/pusher-php-server/src'),
     'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
-    'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
+    'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
     'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
     'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
     'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'),
     'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
+    'PHPStan\\PhpDocParser\\' => array($vendorDir . '/phpstan/phpdoc-parser/src'),
     'Nilambar\\NepaliDate\\' => array($vendorDir . '/ernilambar/nepali-date/src'),
-    'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
     'Matrix\\' => array($vendorDir . '/markbaker/matrix/classes/src'),
     'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
     'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
     'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
     'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
+    'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'),
     'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
     'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
 );
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
index f56d2a5..414cc04 100644
--- a/vendor/composer/autoload_real.php
+++ b/vendor/composer/autoload_real.php
@@ -31,25 +31,18 @@ class ComposerAutoloaderInitade43c0983b611153cb43d6dce42fdb7
 
         $loader->register(true);
 
-        $includeFiles = \Composer\Autoload\ComposerStaticInitade43c0983b611153cb43d6dce42fdb7::$files;
-        foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequireade43c0983b611153cb43d6dce42fdb7($fileIdentifier, $file);
+        $filesToLoad = \Composer\Autoload\ComposerStaticInitade43c0983b611153cb43d6dce42fdb7::$files;
+        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
+            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+                $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+                require $file;
+            }
+        }, null, null);
+        foreach ($filesToLoad as $fileIdentifier => $file) {
+            $requireFile($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
-
-/**
- * @param string $fileIdentifier
- * @param string $file
- * @return void
- */
-function composerRequireade43c0983b611153cb43d6dce42fdb7($fileIdentifier, $file)
-{
-    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
-        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
-
-        require $file;
-    }
-}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index ff2715e..231379a 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -8,11 +8,8 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
 {
     public static $files = array (
         '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
-        'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
         '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
         '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
-        '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
-        '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
         '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
         '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
         '3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php',
@@ -33,7 +30,6 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         ),
         'S' => 
         array (
-            'Symfony\\Polyfill\\Mbstring\\' => 26,
             'Symfony\\Polyfill\\Ctype\\' => 23,
             'Symfony\\Component\\Yaml\\' => 23,
         ),
@@ -46,6 +42,7 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
             'Psr\\Http\\Client\\' => 16,
             'Prophecy\\' => 9,
             'PhpOffice\\PhpSpreadsheet\\' => 25,
+            'PHPStan\\PhpDocParser\\' => 21,
         ),
         'N' => 
         array (
@@ -53,7 +50,6 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         ),
         'M' => 
         array (
-            'MyCLabs\\Enum\\' => 13,
             'Matrix\\' => 7,
         ),
         'G' => 
@@ -65,6 +61,7 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         'D' => 
         array (
             'Doctrine\\Instantiator\\' => 22,
+            'Doctrine\\Deprecations\\' => 22,
             'DeepCopy\\' => 9,
         ),
         'C' => 
@@ -88,10 +85,6 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         array (
             0 => __DIR__ . '/..' . '/webmozart/assert/src',
         ),
-        'Symfony\\Polyfill\\Mbstring\\' => 
-        array (
-            0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
-        ),
         'Symfony\\Polyfill\\Ctype\\' => 
         array (
             0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
@@ -110,7 +103,7 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         ),
         'Psr\\Log\\' => 
         array (
-            0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
+            0 => __DIR__ . '/..' . '/psr/log/src',
         ),
         'Psr\\Http\\Message\\' => 
         array (
@@ -129,14 +122,14 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         array (
             0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
         ),
+        'PHPStan\\PhpDocParser\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src',
+        ),
         'Nilambar\\NepaliDate\\' => 
         array (
             0 => __DIR__ . '/..' . '/ernilambar/nepali-date/src',
         ),
-        'MyCLabs\\Enum\\' => 
-        array (
-            0 => __DIR__ . '/..' . '/myclabs/php-enum/src',
-        ),
         'Matrix\\' => 
         array (
             0 => __DIR__ . '/..' . '/markbaker/matrix/classes/src',
@@ -157,6 +150,10 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         array (
             0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator',
         ),
+        'Doctrine\\Deprecations\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations',
+        ),
         'DeepCopy\\' => 
         array (
             0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy',
@@ -175,13 +172,6 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
                 0 => __DIR__ . '/..' . '/mikey179/vfsstream/src/main/php',
             ),
         ),
-        'H' => 
-        array (
-            'HTMLPurifier' => 
-            array (
-                0 => __DIR__ . '/..' . '/ezyang/htmlpurifier/library',
-            ),
-        ),
     );
 
     public static $classMap = array (
@@ -650,7 +640,6 @@ class ComposerStaticInitade43c0983b611153cb43d6dce42fdb7
         'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/recursion-context/src/InvalidArgumentException.php',
         'SebastianBergmann\\ResourceOperations\\ResourceOperations' => __DIR__ . '/..' . '/sebastian/resource-operations/src/ResourceOperations.php',
         'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php',
-        'Stringable' => __DIR__ . '/..' . '/myclabs/php-enum/stubs/Stringable.php',
         'Text_Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php',
     );
 
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 6090c7d..5499e25 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -1,5 +1,55 @@
 {
     "packages": [
+        {
+            "name": "doctrine/deprecations",
+            "version": "1.1.3",
+            "version_normalized": "1.1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/deprecations.git",
+                "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
+                "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^9",
+                "phpstan/phpstan": "1.4.10 || 1.10.15",
+                "phpstan/phpstan-phpunit": "^1.0",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+                "psalm/plugin-phpunit": "0.18.4",
+                "psr/log": "^1 || ^2 || ^3",
+                "vimeo/psalm": "4.30.0 || 5.12.0"
+            },
+            "suggest": {
+                "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+            },
+            "time": "2024-01-30T19:34:25+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+            "homepage": "https://www.doctrine-project.org/",
+            "support": {
+                "issues": "https://github.com/doctrine/deprecations/issues",
+                "source": "https://github.com/doctrine/deprecations/tree/1.1.3"
+            },
+            "install-path": "../doctrine/deprecations"
+        },
         {
             "name": "doctrine/instantiator",
             "version": "1.5.0",
@@ -75,28 +125,23 @@
         },
         {
             "name": "ernilambar/nepali-date",
-            "version": "1.0.5",
-            "version_normalized": "1.0.5.0",
+            "version": "1.0.7",
+            "version_normalized": "1.0.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/ernilambar/nepali-date.git",
-                "reference": "8ac91a49267e3821bf03f052d3cb6b7876af4a12"
+                "reference": "886dcb25b10760b4f9c35083d0a1d3ef6fb98584"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/ernilambar/nepali-date/zipball/8ac91a49267e3821bf03f052d3cb6b7876af4a12",
-                "reference": "8ac91a49267e3821bf03f052d3cb6b7876af4a12",
+                "url": "https://api.github.com/repos/ernilambar/nepali-date/zipball/886dcb25b10760b4f9c35083d0a1d3ef6fb98584",
+                "reference": "886dcb25b10760b4f9c35083d0a1d3ef6fb98584",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.6"
             },
-            "require-dev": {
-                "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2",
-                "phpunit/phpunit": "^9",
-                "squizlabs/php_codesniffer": "^3.5"
-            },
-            "time": "2021-07-20T06:11:00+00:00",
+            "time": "2024-06-14T05:45:58+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -119,97 +164,35 @@
             "description": "Nepali Date",
             "homepage": "https://github.com/ernilambar/nepali-date",
             "keywords": [
-                "date"
+                "bikram-sambat",
+                "date",
+                "nepali"
             ],
             "support": {
                 "issues": "https://github.com/ernilambar/nepali-date/issues",
-                "source": "https://github.com/ernilambar/nepali-date/tree/1.0.5"
+                "source": "https://github.com/ernilambar/nepali-date/tree/1.0.7"
             },
             "install-path": "../ernilambar/nepali-date"
         },
-        {
-            "name": "ezyang/htmlpurifier",
-            "version": "v4.16.0",
-            "version_normalized": "4.16.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/ezyang/htmlpurifier.git",
-                "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8",
-                "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8",
-                "shasum": ""
-            },
-            "require": {
-                "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
-            },
-            "require-dev": {
-                "cerdic/css-tidy": "^1.7 || ^2.0",
-                "simpletest/simpletest": "dev-master"
-            },
-            "suggest": {
-                "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
-                "ext-bcmath": "Used for unit conversion and imagecrash protection",
-                "ext-iconv": "Converts text to and from non-UTF-8 encodings",
-                "ext-tidy": "Used for pretty-printing HTML"
-            },
-            "time": "2022-09-18T07:06:19+00:00",
-            "type": "library",
-            "installation-source": "dist",
-            "autoload": {
-                "files": [
-                    "library/HTMLPurifier.composer.php"
-                ],
-                "psr-0": {
-                    "HTMLPurifier": "library/"
-                },
-                "exclude-from-classmap": [
-                    "/library/HTMLPurifier/Language/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "LGPL-2.1-or-later"
-            ],
-            "authors": [
-                {
-                    "name": "Edward Z. Yang",
-                    "email": "admin@htmlpurifier.org",
-                    "homepage": "http://ezyang.com"
-                }
-            ],
-            "description": "Standards compliant HTML filter written in PHP",
-            "homepage": "http://htmlpurifier.org/",
-            "keywords": [
-                "html"
-            ],
-            "support": {
-                "issues": "https://github.com/ezyang/htmlpurifier/issues",
-                "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0"
-            },
-            "install-path": "../ezyang/htmlpurifier"
-        },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.5.0",
-            "version_normalized": "7.5.0.0",
+            "version": "7.9.2",
+            "version_normalized": "7.9.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
+                "reference": "d281ed313b989f213357e3be1a179f02196ac99b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
-                "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
+                "reference": "d281ed313b989f213357e3be1a179f02196ac99b",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
-                "guzzlehttp/promises": "^1.5",
-                "guzzlehttp/psr7": "^1.9 || ^2.4",
+                "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+                "guzzlehttp/psr7": "^2.7.0",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0",
                 "symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -218,10 +201,11 @@
                 "psr/http-client-implementation": "1.0"
             },
             "require-dev": {
-                "bamarni/composer-bin-plugin": "^1.8.1",
+                "bamarni/composer-bin-plugin": "^1.8.2",
                 "ext-curl": "*",
-                "php-http/client-integration-tests": "^3.0",
-                "phpunit/phpunit": "^8.5.29 || ^9.5.23",
+                "guzzle/client-integration-tests": "3.0.2",
+                "php-http/message-factory": "^1.1",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20",
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
             },
             "suggest": {
@@ -229,15 +213,12 @@
                 "ext-intl": "Required for Internationalized Domain Name (IDN) support",
                 "psr/log": "Required for using the Log middleware"
             },
-            "time": "2022-08-28T15:39:27+00:00",
+            "time": "2024-07-24T11:22:20+00:00",
             "type": "library",
             "extra": {
                 "bamarni-bin": {
                     "bin-links": true,
                     "forward-command": false
-                },
-                "branch-alias": {
-                    "dev-master": "7.5-dev"
                 }
             },
             "installation-source": "dist",
@@ -304,7 +285,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.5.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.9.2"
             },
             "funding": [
                 {
@@ -324,37 +305,36 @@
         },
         {
             "name": "guzzlehttp/promises",
-            "version": "1.5.2",
-            "version_normalized": "1.5.2.0",
+            "version": "2.0.3",
+            "version_normalized": "2.0.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/promises.git",
-                "reference": "b94b2807d85443f9719887892882d0329d1e2598"
+                "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
-                "reference": "b94b2807d85443f9719887892882d0329d1e2598",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8",
+                "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.5"
+                "php": "^7.2.5 || ^8.0"
             },
             "require-dev": {
-                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20"
             },
-            "time": "2022-08-28T14:55:35+00:00",
+            "time": "2024-07-18T10:29:17+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-master": "1.5-dev"
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
                 }
             },
             "installation-source": "dist",
             "autoload": {
-                "files": [
-                    "src/functions_include.php"
-                ],
                 "psr-4": {
                     "GuzzleHttp\\Promise\\": "src/"
                 }
@@ -391,7 +371,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/promises/issues",
-                "source": "https://github.com/guzzle/promises/tree/1.5.2"
+                "source": "https://github.com/guzzle/promises/tree/2.0.3"
             },
             "funding": [
                 {
@@ -411,23 +391,23 @@
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.4.3",
-            "version_normalized": "2.4.3.0",
+            "version": "2.7.0",
+            "version_normalized": "2.7.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "67c26b443f348a51926030c83481b85718457d3d"
+                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d",
-                "reference": "67c26b443f348a51926030c83481b85718457d3d",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
+                "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-factory": "^1.0",
-                "psr/http-message": "^1.0",
+                "psr/http-message": "^1.1 || ^2.0",
                 "ralouphie/getallheaders": "^3.0"
             },
             "provide": {
@@ -435,22 +415,19 @@
                 "psr/http-message-implementation": "1.0"
             },
             "require-dev": {
-                "bamarni/composer-bin-plugin": "^1.8.1",
-                "http-interop/http-factory-tests": "^0.9",
-                "phpunit/phpunit": "^8.5.29 || ^9.5.23"
+                "bamarni/composer-bin-plugin": "^1.8.2",
+                "http-interop/http-factory-tests": "0.9.0",
+                "phpunit/phpunit": "^8.5.39 || ^9.6.20"
             },
             "suggest": {
                 "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
             },
-            "time": "2022-10-26T14:07:24+00:00",
+            "time": "2024-07-18T11:15:46+00:00",
             "type": "library",
             "extra": {
                 "bamarni-bin": {
                     "bin-links": true,
                     "forward-command": false
-                },
-                "branch-alias": {
-                    "dev-master": "2.4-dev"
                 }
             },
             "installation-source": "dist",
@@ -513,7 +490,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.4.3"
+                "source": "https://github.com/guzzle/psr7/tree/2.7.0"
             },
             "funding": [
                 {
@@ -533,35 +510,38 @@
         },
         {
             "name": "maennchen/zipstream-php",
-            "version": "2.2.6",
-            "version_normalized": "2.2.6.0",
+            "version": "3.1.0",
+            "version_normalized": "3.1.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/maennchen/ZipStream-PHP.git",
-                "reference": "30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f"
+                "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f",
-                "reference": "30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f",
+                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
+                "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
                 "shasum": ""
             },
             "require": {
-                "myclabs/php-enum": "^1.5",
-                "php": "^7.4 || ^8.0",
-                "psr/http-message": "^1.0",
-                "symfony/polyfill-mbstring": "^1.0"
+                "ext-mbstring": "*",
+                "ext-zlib": "*",
+                "php-64bit": "^8.1"
             },
             "require-dev": {
                 "ext-zip": "*",
-                "friendsofphp/php-cs-fixer": "^3.9",
-                "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+                "friendsofphp/php-cs-fixer": "^3.16",
+                "guzzlehttp/guzzle": "^7.5",
                 "mikey179/vfsstream": "^1.6",
-                "php-coveralls/php-coveralls": "^2.4",
-                "phpunit/phpunit": "^8.5.8 || ^9.4.2",
-                "vimeo/psalm": "^4.1"
+                "php-coveralls/php-coveralls": "^2.5",
+                "phpunit/phpunit": "^10.0",
+                "vimeo/psalm": "^5.0"
             },
-            "time": "2022-11-25T18:57:19+00:00",
+            "suggest": {
+                "guzzlehttp/psr7": "^2.4",
+                "psr/http-message": "^2.0"
+            },
+            "time": "2023-06-21T14:59:35+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -598,7 +578,7 @@
             ],
             "support": {
                 "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
-                "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.2.6"
+                "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0"
             },
             "funding": [
                 {
@@ -764,17 +744,17 @@
         },
         {
             "name": "myclabs/deep-copy",
-            "version": "1.11.0",
-            "version_normalized": "1.11.0.0",
+            "version": "1.12.0",
+            "version_normalized": "1.12.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
+                "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
-                "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
+                "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
                 "shasum": ""
             },
             "require": {
@@ -782,14 +762,15 @@
             },
             "conflict": {
                 "doctrine/collections": "<1.6.8",
-                "doctrine/common": "<2.13.3 || >=3,<3.2.2"
+                "doctrine/common": "<2.13.3 || >=3 <3.2.2"
             },
             "require-dev": {
                 "doctrine/collections": "^1.6.8",
                 "doctrine/common": "^2.13.3 || ^3.2.2",
+                "phpspec/prophecy": "^1.10",
                 "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
             },
-            "time": "2022-03-03T13:19:32+00:00",
+            "time": "2024-06-12T14:39:25+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -814,7 +795,7 @@
             ],
             "support": {
                 "issues": "https://github.com/myclabs/DeepCopy/issues",
-                "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
+                "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
             },
             "funding": [
                 {
@@ -824,72 +805,6 @@
             ],
             "install-path": "../myclabs/deep-copy"
         },
-        {
-            "name": "myclabs/php-enum",
-            "version": "1.8.4",
-            "version_normalized": "1.8.4.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/myclabs/php-enum.git",
-                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
-                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
-                "shasum": ""
-            },
-            "require": {
-                "ext-json": "*",
-                "php": "^7.3 || ^8.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.5",
-                "squizlabs/php_codesniffer": "1.*",
-                "vimeo/psalm": "^4.6.2"
-            },
-            "time": "2022-08-04T09:53:51+00:00",
-            "type": "library",
-            "installation-source": "dist",
-            "autoload": {
-                "psr-4": {
-                    "MyCLabs\\Enum\\": "src/"
-                },
-                "classmap": [
-                    "stubs/Stringable.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "PHP Enum contributors",
-                    "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
-                }
-            ],
-            "description": "PHP Enum implementation",
-            "homepage": "http://github.com/myclabs/php-enum",
-            "keywords": [
-                "enum"
-            ],
-            "support": {
-                "issues": "https://github.com/myclabs/php-enum/issues",
-                "source": "https://github.com/myclabs/php-enum/tree/1.8.4"
-            },
-            "funding": [
-                {
-                    "url": "https://github.com/mnapoli",
-                    "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
-                    "type": "tidelift"
-                }
-            ],
-            "install-path": "../myclabs/php-enum"
-        },
         {
             "name": "paragonie/random_compat",
             "version": "v9.99.100",
@@ -945,17 +860,17 @@
         },
         {
             "name": "paragonie/sodium_compat",
-            "version": "v1.19.0",
-            "version_normalized": "1.19.0.0",
+            "version": "v1.21.1",
+            "version_normalized": "1.21.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/paragonie/sodium_compat.git",
-                "reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933"
+                "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/cb15e403ecbe6a6cc515f855c310eb6b1872a933",
-                "reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933",
+                "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bb312875dcdd20680419564fe42ba1d9564b9e37",
+                "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37",
                 "shasum": ""
             },
             "require": {
@@ -969,7 +884,7 @@
                 "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.",
                 "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security."
             },
-            "time": "2022-09-26T03:40:35+00:00",
+            "time": "2024-04-22T22:05:04+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -1028,7 +943,7 @@
             ],
             "support": {
                 "issues": "https://github.com/paragonie/sodium_compat/issues",
-                "source": "https://github.com/paragonie/sodium_compat/tree/v1.19.0"
+                "source": "https://github.com/paragonie/sodium_compat/tree/v1.21.1"
             },
             "install-path": "../paragonie/sodium_compat"
         },
@@ -1090,31 +1005,38 @@
         },
         {
             "name": "phpdocumentor/reflection-docblock",
-            "version": "5.3.0",
-            "version_normalized": "5.3.0.0",
+            "version": "5.4.1",
+            "version_normalized": "5.4.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+                "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
-                "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
+                "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
                 "shasum": ""
             },
             "require": {
+                "doctrine/deprecations": "^1.1",
                 "ext-filter": "*",
-                "php": "^7.2 || ^8.0",
+                "php": "^7.4 || ^8.0",
                 "phpdocumentor/reflection-common": "^2.2",
-                "phpdocumentor/type-resolver": "^1.3",
+                "phpdocumentor/type-resolver": "^1.7",
+                "phpstan/phpdoc-parser": "^1.7",
                 "webmozart/assert": "^1.9.1"
             },
             "require-dev": {
-                "mockery/mockery": "~1.3.2",
-                "psalm/phar": "^4.8"
+                "mockery/mockery": "~1.3.5",
+                "phpstan/extension-installer": "^1.1",
+                "phpstan/phpstan": "^1.8",
+                "phpstan/phpstan-mockery": "^1.1",
+                "phpstan/phpstan-webmozart-assert": "^1.2",
+                "phpunit/phpunit": "^9.5",
+                "vimeo/psalm": "^5.13"
             },
-            "time": "2021-10-19T17:43:47+00:00",
+            "time": "2024-05-21T05:55:05+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -1138,37 +1060,40 @@
                 },
                 {
                     "name": "Jaap van Otterdijk",
-                    "email": "account@ijaap.nl"
+                    "email": "opensource@ijaap.nl"
                 }
             ],
             "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
             "support": {
                 "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
-                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+                "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1"
             },
             "install-path": "../phpdocumentor/reflection-docblock"
         },
         {
             "name": "phpdocumentor/type-resolver",
-            "version": "1.6.2",
-            "version_normalized": "1.6.2.0",
+            "version": "1.8.2",
+            "version_normalized": "1.8.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
+                "reference": "153ae662783729388a584b4361f2545e4d841e3c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
-                "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c",
+                "reference": "153ae662783729388a584b4361f2545e4d841e3c",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.4 || ^8.0",
-                "phpdocumentor/reflection-common": "^2.0"
+                "doctrine/deprecations": "^1.0",
+                "php": "^7.3 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.0",
+                "phpstan/phpdoc-parser": "^1.13"
             },
             "require-dev": {
                 "ext-tokenizer": "*",
+                "phpbench/phpbench": "^1.2",
                 "phpstan/extension-installer": "^1.1",
                 "phpstan/phpstan": "^1.8",
                 "phpstan/phpstan-phpunit": "^1.1",
@@ -1176,7 +1101,7 @@
                 "rector/rector": "^0.13.9",
                 "vimeo/psalm": "^4.25"
             },
-            "time": "2022-10-14T12:47:21+00:00",
+            "time": "2024-02-23T11:10:43+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -1202,23 +1127,23 @@
             "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
             "support": {
                 "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
-                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
+                "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2"
             },
             "install-path": "../phpdocumentor/type-resolver"
         },
         {
             "name": "phpoffice/phpspreadsheet",
-            "version": "1.26.0",
-            "version_normalized": "1.26.0.0",
+            "version": "2.2.2",
+            "version_normalized": "2.2.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
-                "reference": "5b6ceea9705b068f993e268e4debc566c2637063"
+                "reference": "ffbcee68069b073bff07a71eb321dcd9f2763513"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/5b6ceea9705b068f993e268e4debc566c2637063",
-                "reference": "5b6ceea9705b068f993e268e4debc566c2637063",
+                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/ffbcee68069b073bff07a71eb321dcd9f2763513",
+                "reference": "ffbcee68069b073bff07a71eb321dcd9f2763513",
                 "shasum": ""
             },
             "require": {
@@ -1235,25 +1160,24 @@
                 "ext-xmlwriter": "*",
                 "ext-zip": "*",
                 "ext-zlib": "*",
-                "ezyang/htmlpurifier": "^4.15",
-                "maennchen/zipstream-php": "^2.1",
+                "maennchen/zipstream-php": "^2.1 || ^3.0",
                 "markbaker/complex": "^3.0",
                 "markbaker/matrix": "^3.0",
-                "php": "^7.4 || ^8.0",
+                "php": "^8.1",
                 "psr/http-client": "^1.0",
                 "psr/http-factory": "^1.0",
                 "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
             },
             "require-dev": {
-                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
-                "dompdf/dompdf": "^1.0 || ^2.0",
+                "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+                "dompdf/dompdf": "^2.0 || ^3.0",
                 "friendsofphp/php-cs-fixer": "^3.2",
-                "mitoteam/jpgraph": "^10.2.4",
+                "mitoteam/jpgraph": "^10.3",
                 "mpdf/mpdf": "^8.1.1",
                 "phpcompatibility/php-compatibility": "^9.3",
                 "phpstan/phpstan": "^1.1",
                 "phpstan/phpstan-phpunit": "^1.0",
-                "phpunit/phpunit": "^8.5 || ^9.0",
+                "phpunit/phpunit": "^9.6 || ^10.5",
                 "squizlabs/php_codesniffer": "^3.7",
                 "tecnickcom/tcpdf": "^6.5"
             },
@@ -1264,7 +1188,7 @@
                 "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
                 "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
             },
-            "time": "2022-12-21T12:22:06+00:00",
+            "time": "2024-08-08T02:31:26+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -1310,7 +1234,7 @@
             ],
             "support": {
                 "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
-                "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.26.0"
+                "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.2.2"
             },
             "install-path": "../phpoffice/phpspreadsheet"
         },
@@ -1384,6 +1308,56 @@
             },
             "install-path": "../phpspec/prophecy"
         },
+        {
+            "name": "phpstan/phpdoc-parser",
+            "version": "1.31.0",
+            "version_normalized": "1.31.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpstan/phpdoc-parser.git",
+                "reference": "249f15fb843bf240cf058372dad29e100cee6c17"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/249f15fb843bf240cf058372dad29e100cee6c17",
+                "reference": "249f15fb843bf240cf058372dad29e100cee6c17",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "doctrine/annotations": "^2.0",
+                "nikic/php-parser": "^4.15",
+                "php-parallel-lint/php-parallel-lint": "^1.2",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^1.5",
+                "phpstan/phpstan-phpunit": "^1.1",
+                "phpstan/phpstan-strict-rules": "^1.0",
+                "phpunit/phpunit": "^9.5",
+                "symfony/process": "^5.2"
+            },
+            "time": "2024-09-22T11:32:18+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "PHPStan\\PhpDocParser\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "PHPDoc parser with support for nullable, intersection and generic types",
+            "support": {
+                "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+                "source": "https://github.com/phpstan/phpdoc-parser/tree/1.31.0"
+            },
+            "install-path": "../phpstan/phpdoc-parser"
+        },
         {
             "name": "phpunit/php-code-coverage",
             "version": "4.0.8",
@@ -1830,24 +1804,24 @@
         },
         {
             "name": "psr/http-client",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
+            "version": "1.0.3",
+            "version_normalized": "1.0.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-client.git",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
-                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+                "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.0 || ^8.0",
-                "psr/http-message": "^1.0"
+                "psr/http-message": "^1.0 || ^2.0"
             },
-            "time": "2020-06-29T06:28:15+00:00",
+            "time": "2023-09-23T14:17:50+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -1867,7 +1841,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP clients",
@@ -1879,30 +1853,30 @@
                 "psr-18"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-client/tree/master"
+                "source": "https://github.com/php-fig/http-client"
             },
             "install-path": "../psr/http-client"
         },
         {
             "name": "psr/http-factory",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
+            "version": "1.1.0",
+            "version_normalized": "1.1.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-factory.git",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
-                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.0.0",
-                "psr/http-message": "^1.0"
+                "php": ">=7.1",
+                "psr/http-message": "^1.0 || ^2.0"
             },
-            "time": "2019-04-30T12:38:16+00:00",
+            "time": "2024-04-15T12:06:14+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -1922,10 +1896,10 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
-            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
             "keywords": [
                 "factory",
                 "http",
@@ -1937,33 +1911,33 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-factory/tree/master"
+                "source": "https://github.com/php-fig/http-factory"
             },
             "install-path": "../psr/http-factory"
         },
         {
             "name": "psr/http-message",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
+            "version": "2.0",
+            "version_normalized": "2.0.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-message.git",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
-                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+                "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": "^7.2 || ^8.0"
             },
-            "time": "2016-08-06T14:39:51+00:00",
+            "time": "2023-04-04T09:54:51+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "installation-source": "dist",
@@ -1979,7 +1953,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for HTTP messages",
@@ -1993,39 +1967,39 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-message/tree/master"
+                "source": "https://github.com/php-fig/http-message/tree/2.0"
             },
             "install-path": "../psr/http-message"
         },
         {
             "name": "psr/log",
-            "version": "1.1.4",
-            "version_normalized": "1.1.4.0",
+            "version": "3.0.2",
+            "version_normalized": "3.0.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/log.git",
-                "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
-                "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+                "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": ">=8.0.0"
             },
-            "time": "2021-05-03T11:20:27+00:00",
+            "time": "2024-09-11T13:17:53+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1.x-dev"
+                    "dev-master": "3.x-dev"
                 }
             },
             "installation-source": "dist",
             "autoload": {
                 "psr-4": {
-                    "Psr\\Log\\": "Psr/Log/"
+                    "Psr\\Log\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -2046,33 +2020,33 @@
                 "psr-3"
             ],
             "support": {
-                "source": "https://github.com/php-fig/log/tree/1.1.4"
+                "source": "https://github.com/php-fig/log/tree/3.0.2"
             },
             "install-path": "../psr/log"
         },
         {
             "name": "psr/simple-cache",
-            "version": "1.0.1",
-            "version_normalized": "1.0.1.0",
+            "version": "3.0.0",
+            "version_normalized": "3.0.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/simple-cache.git",
-                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
-                "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0"
+                "php": ">=8.0.0"
             },
-            "time": "2017-10-23T01:57:42+00:00",
+            "time": "2021-10-29T13:26:27+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "3.0.x-dev"
                 }
             },
             "installation-source": "dist",
@@ -2088,7 +2062,7 @@
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interfaces for simple caching",
@@ -2100,23 +2074,23 @@
                 "simple-cache"
             ],
             "support": {
-                "source": "https://github.com/php-fig/simple-cache/tree/master"
+                "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
             },
             "install-path": "../psr/simple-cache"
         },
         {
             "name": "pusher/pusher-php-server",
-            "version": "7.2.2",
-            "version_normalized": "7.2.2.0",
+            "version": "7.2.4",
+            "version_normalized": "7.2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/pusher/pusher-http-php.git",
-                "reference": "4ace4873873b06c25cecb2dd6d9fdcbf2f20b640"
+                "reference": "de2f72296808f9cafa6a4462b15a768ff130cddb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/4ace4873873b06c25cecb2dd6d9fdcbf2f20b640",
-                "reference": "4ace4873873b06c25cecb2dd6d9fdcbf2f20b640",
+                "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/de2f72296808f9cafa6a4462b15a768ff130cddb",
+                "reference": "de2f72296808f9cafa6a4462b15a768ff130cddb",
                 "shasum": ""
             },
             "require": {
@@ -2131,7 +2105,7 @@
                 "overtrue/phplint": "^2.3",
                 "phpunit/phpunit": "^9.3"
             },
-            "time": "2022-12-20T19:52:36+00:00",
+            "time": "2023-12-15T10:58:53+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -2164,7 +2138,7 @@
             ],
             "support": {
                 "issues": "https://github.com/pusher/pusher-http-php/issues",
-                "source": "https://github.com/pusher/pusher-http-php/tree/7.2.2"
+                "source": "https://github.com/pusher/pusher-http-php/tree/7.2.4"
             },
             "install-path": "../pusher/pusher-php-server"
         },
@@ -2217,17 +2191,17 @@
         },
         {
             "name": "sebastian/code-unit-reverse-lookup",
-            "version": "1.0.2",
-            "version_normalized": "1.0.2.0",
+            "version": "1.0.3",
+            "version_normalized": "1.0.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
-                "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
+                "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
-                "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
+                "reference": "92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54",
                 "shasum": ""
             },
             "require": {
@@ -2236,7 +2210,7 @@
             "require-dev": {
                 "phpunit/phpunit": "^8.5"
             },
-            "time": "2020-11-30T08:15:22+00:00",
+            "time": "2024-03-01T13:45:45+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
@@ -2263,7 +2237,7 @@
             "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
             "support": {
                 "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
-                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
+                "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.3"
             },
             "funding": [
                 {
@@ -2806,27 +2780,27 @@
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v2.5.2",
-            "version_normalized": "2.5.2.0",
+            "version": "v3.5.0",
+            "version_normalized": "3.5.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
+                "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
-                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+                "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
+                "php": ">=8.1"
             },
-            "time": "2022-01-02T09:53:40+00:00",
+            "time": "2024-04-18T09:32:20+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "2.5-dev"
+                    "dev-main": "3.5-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -2856,7 +2830,7 @@
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
             },
             "funding": [
                 {
@@ -2876,21 +2850,21 @@
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.27.0",
-            "version_normalized": "1.27.0.0",
+            "version": "v1.31.0",
+            "version_normalized": "1.31.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
-                "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+                "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
+                "php": ">=7.2"
             },
             "provide": {
                 "ext-ctype": "*"
@@ -2898,12 +2872,9 @@
             "suggest": {
                 "ext-ctype": "For best performance"
             },
-            "time": "2022-11-03T14:55:06+00:00",
+            "time": "2024-09-09T11:45:10+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.27-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -2941,7 +2912,7 @@
                 "portable"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
             },
             "funding": [
                 {
@@ -2959,92 +2930,6 @@
             ],
             "install-path": "../symfony/polyfill-ctype"
         },
-        {
-            "name": "symfony/polyfill-mbstring",
-            "version": "v1.27.0",
-            "version_normalized": "1.27.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
-                "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=7.1"
-            },
-            "provide": {
-                "ext-mbstring": "*"
-            },
-            "suggest": {
-                "ext-mbstring": "For best performance"
-            },
-            "time": "2022-11-03T14:55:06+00:00",
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-main": "1.27-dev"
-                },
-                "thanks": {
-                    "name": "symfony/polyfill",
-                    "url": "https://github.com/symfony/polyfill"
-                }
-            },
-            "installation-source": "dist",
-            "autoload": {
-                "files": [
-                    "bootstrap.php"
-                ],
-                "psr-4": {
-                    "Symfony\\Polyfill\\Mbstring\\": ""
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Nicolas Grekas",
-                    "email": "p@tchwork.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "Symfony polyfill for the Mbstring extension",
-            "homepage": "https://symfony.com",
-            "keywords": [
-                "compatibility",
-                "mbstring",
-                "polyfill",
-                "portable",
-                "shim"
-            ],
-            "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
-            },
-            "funding": [
-                {
-                    "url": "https://symfony.com/sponsor",
-                    "type": "custom"
-                },
-                {
-                    "url": "https://github.com/fabpot",
-                    "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
-                    "type": "tidelift"
-                }
-            ],
-            "install-path": "../symfony/polyfill-mbstring"
-        },
         {
             "name": "symfony/yaml",
             "version": "v4.4.45",
@@ -3183,6 +3068,7 @@
     ],
     "dev": true,
     "dev-package-names": [
+        "doctrine/deprecations",
         "doctrine/instantiator",
         "mikey179/vfsstream",
         "myclabs/deep-copy",
@@ -3190,6 +3076,7 @@
         "phpdocumentor/reflection-docblock",
         "phpdocumentor/type-resolver",
         "phpspec/prophecy",
+        "phpstan/phpdoc-parser",
         "phpunit/php-code-coverage",
         "phpunit/php-file-iterator",
         "phpunit/php-text-template",
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 11d5e75..e03099f 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'codeigniter/framework',
-        'pretty_version' => '1.0.0+no-version-set',
-        'version' => '1.0.0.0',
-        'reference' => NULL,
+        'pretty_version' => 'dev-main',
+        'version' => 'dev-main',
+        'reference' => '497f567cba71733b286514954ef72c9fe5d46c51',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -11,14 +11,23 @@
     ),
     'versions' => array(
         'codeigniter/framework' => array(
-            'pretty_version' => '1.0.0+no-version-set',
-            'version' => '1.0.0.0',
-            'reference' => NULL,
+            'pretty_version' => 'dev-main',
+            'version' => 'dev-main',
+            'reference' => '497f567cba71733b286514954ef72c9fe5d46c51',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'doctrine/deprecations' => array(
+            'pretty_version' => '1.1.3',
+            'version' => '1.1.3.0',
+            'reference' => 'dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../doctrine/deprecations',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
         'doctrine/instantiator' => array(
             'pretty_version' => '1.5.0',
             'version' => '1.5.0.0',
@@ -29,54 +38,45 @@
             'dev_requirement' => true,
         ),
         'ernilambar/nepali-date' => array(
-            'pretty_version' => '1.0.5',
-            'version' => '1.0.5.0',
-            'reference' => '8ac91a49267e3821bf03f052d3cb6b7876af4a12',
+            'pretty_version' => '1.0.7',
+            'version' => '1.0.7.0',
+            'reference' => '886dcb25b10760b4f9c35083d0a1d3ef6fb98584',
             'type' => 'library',
             'install_path' => __DIR__ . '/../ernilambar/nepali-date',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
-        'ezyang/htmlpurifier' => array(
-            'pretty_version' => 'v4.16.0',
-            'version' => '4.16.0.0',
-            'reference' => '523407fb06eb9e5f3d59889b3978d5bfe94299c8',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../ezyang/htmlpurifier',
-            'aliases' => array(),
-            'dev_requirement' => false,
-        ),
         'guzzlehttp/guzzle' => array(
-            'pretty_version' => '7.5.0',
-            'version' => '7.5.0.0',
-            'reference' => 'b50a2a1251152e43f6a37f0fa053e730a67d25ba',
+            'pretty_version' => '7.9.2',
+            'version' => '7.9.2.0',
+            'reference' => 'd281ed313b989f213357e3be1a179f02196ac99b',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'guzzlehttp/promises' => array(
-            'pretty_version' => '1.5.2',
-            'version' => '1.5.2.0',
-            'reference' => 'b94b2807d85443f9719887892882d0329d1e2598',
+            'pretty_version' => '2.0.3',
+            'version' => '2.0.3.0',
+            'reference' => '6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/promises',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'guzzlehttp/psr7' => array(
-            'pretty_version' => '2.4.3',
-            'version' => '2.4.3.0',
-            'reference' => '67c26b443f348a51926030c83481b85718457d3d',
+            'pretty_version' => '2.7.0',
+            'version' => '2.7.0.0',
+            'reference' => 'a70f5c95fb43bc83f07c9c948baa0dc1829bf201',
             'type' => 'library',
             'install_path' => __DIR__ . '/../guzzlehttp/psr7',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'maennchen/zipstream-php' => array(
-            'pretty_version' => '2.2.6',
-            'version' => '2.2.6.0',
-            'reference' => '30ad6f93cf3efe4192bc7a4c9cad11ff8f4f237f',
+            'pretty_version' => '3.1.0',
+            'version' => '3.1.0.0',
+            'reference' => 'b8174494eda667f7d13876b4a7bfef0f62a7c0d1',
             'type' => 'library',
             'install_path' => __DIR__ . '/../maennchen/zipstream-php',
             'aliases' => array(),
@@ -110,23 +110,14 @@
             'dev_requirement' => true,
         ),
         'myclabs/deep-copy' => array(
-            'pretty_version' => '1.11.0',
-            'version' => '1.11.0.0',
-            'reference' => '14daed4296fae74d9e3201d2c4925d1acb7aa614',
+            'pretty_version' => '1.12.0',
+            'version' => '1.12.0.0',
+            'reference' => '3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c',
             'type' => 'library',
             'install_path' => __DIR__ . '/../myclabs/deep-copy',
             'aliases' => array(),
             'dev_requirement' => true,
         ),
-        'myclabs/php-enum' => array(
-            'pretty_version' => '1.8.4',
-            'version' => '1.8.4.0',
-            'reference' => 'a867478eae49c9f59ece437ae7f9506bfaa27483',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../myclabs/php-enum',
-            'aliases' => array(),
-            'dev_requirement' => false,
-        ),
         'paragonie/random_compat' => array(
             'pretty_version' => 'v9.99.100',
             'version' => '9.99.100.0',
@@ -137,9 +128,9 @@
             'dev_requirement' => false,
         ),
         'paragonie/sodium_compat' => array(
-            'pretty_version' => 'v1.19.0',
-            'version' => '1.19.0.0',
-            'reference' => 'cb15e403ecbe6a6cc515f855c310eb6b1872a933',
+            'pretty_version' => 'v1.21.1',
+            'version' => '1.21.1.0',
+            'reference' => 'bb312875dcdd20680419564fe42ba1d9564b9e37',
             'type' => 'library',
             'install_path' => __DIR__ . '/../paragonie/sodium_compat',
             'aliases' => array(),
@@ -155,27 +146,27 @@
             'dev_requirement' => true,
         ),
         'phpdocumentor/reflection-docblock' => array(
-            'pretty_version' => '5.3.0',
-            'version' => '5.3.0.0',
-            'reference' => '622548b623e81ca6d78b721c5e029f4ce664f170',
+            'pretty_version' => '5.4.1',
+            'version' => '5.4.1.0',
+            'reference' => '9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c',
             'type' => 'library',
             'install_path' => __DIR__ . '/../phpdocumentor/reflection-docblock',
             'aliases' => array(),
             'dev_requirement' => true,
         ),
         'phpdocumentor/type-resolver' => array(
-            'pretty_version' => '1.6.2',
-            'version' => '1.6.2.0',
-            'reference' => '48f445a408c131e38cab1c235aa6d2bb7a0bb20d',
+            'pretty_version' => '1.8.2',
+            'version' => '1.8.2.0',
+            'reference' => '153ae662783729388a584b4361f2545e4d841e3c',
             'type' => 'library',
             'install_path' => __DIR__ . '/../phpdocumentor/type-resolver',
             'aliases' => array(),
             'dev_requirement' => true,
         ),
         'phpoffice/phpspreadsheet' => array(
-            'pretty_version' => '1.26.0',
-            'version' => '1.26.0.0',
-            'reference' => '5b6ceea9705b068f993e268e4debc566c2637063',
+            'pretty_version' => '2.2.2',
+            'version' => '2.2.2.0',
+            'reference' => 'ffbcee68069b073bff07a71eb321dcd9f2763513',
             'type' => 'library',
             'install_path' => __DIR__ . '/../phpoffice/phpspreadsheet',
             'aliases' => array(),
@@ -190,6 +181,15 @@
             'aliases' => array(),
             'dev_requirement' => true,
         ),
+        'phpstan/phpdoc-parser' => array(
+            'pretty_version' => '1.31.0',
+            'version' => '1.31.0.0',
+            'reference' => '249f15fb843bf240cf058372dad29e100cee6c17',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpstan/phpdoc-parser',
+            'aliases' => array(),
+            'dev_requirement' => true,
+        ),
         'phpunit/php-code-coverage' => array(
             'pretty_version' => '4.0.8',
             'version' => '4.0.8.0',
@@ -254,9 +254,9 @@
             'dev_requirement' => true,
         ),
         'psr/http-client' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
-            'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
+            'pretty_version' => '1.0.3',
+            'version' => '1.0.3.0',
+            'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/http-client',
             'aliases' => array(),
@@ -269,9 +269,9 @@
             ),
         ),
         'psr/http-factory' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
-            'reference' => '12ac7fcd07e5b077433f5f2bee95b3a771bf61be',
+            'pretty_version' => '1.1.0',
+            'version' => '1.1.0.0',
+            'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/http-factory',
             'aliases' => array(),
@@ -284,9 +284,9 @@
             ),
         ),
         'psr/http-message' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
-            'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
+            'pretty_version' => '2.0',
+            'version' => '2.0.0.0',
+            'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/http-message',
             'aliases' => array(),
@@ -299,27 +299,27 @@
             ),
         ),
         'psr/log' => array(
-            'pretty_version' => '1.1.4',
-            'version' => '1.1.4.0',
-            'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11',
+            'pretty_version' => '3.0.2',
+            'version' => '3.0.2.0',
+            'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/log',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'psr/simple-cache' => array(
-            'pretty_version' => '1.0.1',
-            'version' => '1.0.1.0',
-            'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b',
+            'pretty_version' => '3.0.0',
+            'version' => '3.0.0.0',
+            'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865',
             'type' => 'library',
             'install_path' => __DIR__ . '/../psr/simple-cache',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'pusher/pusher-php-server' => array(
-            'pretty_version' => '7.2.2',
-            'version' => '7.2.2.0',
-            'reference' => '4ace4873873b06c25cecb2dd6d9fdcbf2f20b640',
+            'pretty_version' => '7.2.4',
+            'version' => '7.2.4.0',
+            'reference' => 'de2f72296808f9cafa6a4462b15a768ff130cddb',
             'type' => 'library',
             'install_path' => __DIR__ . '/../pusher/pusher-php-server',
             'aliases' => array(),
@@ -335,9 +335,9 @@
             'dev_requirement' => false,
         ),
         'sebastian/code-unit-reverse-lookup' => array(
-            'pretty_version' => '1.0.2',
-            'version' => '1.0.2.0',
-            'reference' => '1de8cd5c010cb153fcd68b8d0f64606f523f7619',
+            'pretty_version' => '1.0.3',
+            'version' => '1.0.3.0',
+            'reference' => '92a1a52e86d34cde6caa54f1b5ffa9fda18e5d54',
             'type' => 'library',
             'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup',
             'aliases' => array(),
@@ -425,32 +425,23 @@
             'dev_requirement' => true,
         ),
         'symfony/deprecation-contracts' => array(
-            'pretty_version' => 'v2.5.2',
-            'version' => '2.5.2.0',
-            'reference' => 'e8b495ea28c1d97b5e0c121748d6f9b53d075c66',
+            'pretty_version' => 'v3.5.0',
+            'version' => '3.5.0.0',
+            'reference' => '0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/polyfill-ctype' => array(
-            'pretty_version' => 'v1.27.0',
-            'version' => '1.27.0.0',
-            'reference' => '5bbc823adecdae860bb64756d639ecfec17b050a',
+            'pretty_version' => 'v1.31.0',
+            'version' => '1.31.0.0',
+            'reference' => 'a3cc8b044a6ea513310cbd48ef7333b384945638',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
             'aliases' => array(),
             'dev_requirement' => true,
         ),
-        'symfony/polyfill-mbstring' => array(
-            'pretty_version' => 'v1.27.0',
-            'version' => '1.27.0.0',
-            'reference' => '8ad114f6b39e2c98a8b0e3bd907732c207c2b534',
-            'type' => 'library',
-            'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
-            'aliases' => array(),
-            'dev_requirement' => false,
-        ),
         'symfony/yaml' => array(
             'pretty_version' => 'v4.4.45',
             'version' => '4.4.45.0',
diff --git a/vendor/doctrine/deprecations/LICENSE b/vendor/doctrine/deprecations/LICENSE
new file mode 100644
index 0000000..156905c
--- /dev/null
+++ b/vendor/doctrine/deprecations/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2020-2021 Doctrine Project
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/doctrine/deprecations/README.md b/vendor/doctrine/deprecations/README.md
new file mode 100644
index 0000000..93caf83
--- /dev/null
+++ b/vendor/doctrine/deprecations/README.md
@@ -0,0 +1,157 @@
+# Doctrine Deprecations
+
+A small (side-effect free by default) layer on top of
+`trigger_error(E_USER_DEPRECATED)` or PSR-3 logging.
+
+- no side-effects by default, making it a perfect fit for libraries that don't know how the error handler works they operate under
+- options to avoid having to rely on error handlers global state by using PSR-3 logging
+- deduplicate deprecation messages to avoid excessive triggering and reduce overhead
+
+We recommend to collect Deprecations using a PSR logger instead of relying on
+the global error handler.
+
+## Usage from consumer perspective:
+
+Enable Doctrine deprecations to be sent to a PSR3 logger:
+
+```php
+\Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
+```
+
+Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)`
+messages by setting the `DOCTRINE_DEPRECATIONS` environment variable to `trigger`.
+Alternatively, call:
+
+```php
+\Doctrine\Deprecations\Deprecation::enableWithTriggerError();
+```
+
+If you only want to enable deprecation tracking, without logging or calling `trigger_error`
+then set the `DOCTRINE_DEPRECATIONS` environment variable to `track`.
+Alternatively, call:
+
+```php
+\Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
+```
+
+Tracking is enabled with all three modes and provides access to all triggered
+deprecations and their individual count:
+
+```php
+$deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations();
+
+foreach ($deprecations as $identifier => $count) {
+    echo $identifier . " was triggered " . $count . " times\n";
+}
+```
+
+### Suppressing Specific Deprecations
+
+Disable triggering about specific deprecations:
+
+```php
+\Doctrine\Deprecations\Deprecation::ignoreDeprecations("https://link/to/deprecations-description-identifier");
+```
+
+Disable all deprecations from a package
+
+```php
+\Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm");
+```
+
+### Other Operations
+
+When used within PHPUnit or other tools that could collect multiple instances of the same deprecations
+the deduplication can be disabled:
+
+```php
+\Doctrine\Deprecations\Deprecation::withoutDeduplication();
+```
+
+Disable deprecation tracking again:
+
+```php
+\Doctrine\Deprecations\Deprecation::disable();
+```
+
+## Usage from a library/producer perspective:
+
+When you want to unconditionally trigger a deprecation even when called
+from the library itself then the `trigger` method is the way to go:
+
+```php
+\Doctrine\Deprecations\Deprecation::trigger(
+    "doctrine/orm",
+    "https://link/to/deprecations-description",
+    "message"
+);
+```
+
+If variable arguments are provided at the end, they are used with `sprintf` on
+the message.
+
+```php
+\Doctrine\Deprecations\Deprecation::trigger(
+    "doctrine/orm",
+    "https://github.com/doctrine/orm/issue/1234",
+    "message %s %d",
+    "foo",
+    1234
+);
+```
+
+When you want to trigger a deprecation only when it is called by a function
+outside of the current package, but not trigger when the package itself is the cause,
+then use:
+
+```php
+\Doctrine\Deprecations\Deprecation::triggerIfCalledFromOutside(
+    "doctrine/orm",
+    "https://link/to/deprecations-description",
+    "message"
+);
+```
+
+Based on the issue link each deprecation message is only triggered once per
+request.
+
+A limited stacktrace is included in the deprecation message to find the
+offending location.
+
+Note: A producer/library should never call `Deprecation::enableWith` methods
+and leave the decision how to handle deprecations to application and
+frameworks.
+
+## Usage in PHPUnit tests
+
+There is a `VerifyDeprecations` trait that you can use to make assertions on
+the occurrence of deprecations within a test.
+
+```php
+use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
+
+class MyTest extends TestCase
+{
+    use VerifyDeprecations;
+
+    public function testSomethingDeprecation()
+    {
+        $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
+
+        triggerTheCodeWithDeprecation();
+    }
+
+    public function testSomethingDeprecationFixed()
+    {
+        $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234');
+
+        triggerTheCodeWithoutDeprecation();
+    }
+}
+```
+
+## What is a deprecation identifier?
+
+An identifier for deprecations is just a link to any resource, most often a
+Github Issue or Pull Request explaining the deprecation and potentially its
+alternative.
diff --git a/vendor/doctrine/deprecations/composer.json b/vendor/doctrine/deprecations/composer.json
new file mode 100644
index 0000000..f8319f9
--- /dev/null
+++ b/vendor/doctrine/deprecations/composer.json
@@ -0,0 +1,38 @@
+{
+    "name": "doctrine/deprecations",
+    "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+    "license": "MIT",
+    "type": "library",
+    "homepage": "https://www.doctrine-project.org/",
+    "require": {
+        "php": "^7.1 || ^8.0"
+    },
+    "require-dev": {
+        "doctrine/coding-standard": "^9",
+        "phpstan/phpstan": "1.4.10 || 1.10.15",
+        "phpstan/phpstan-phpunit": "^1.0",
+        "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+        "psalm/plugin-phpunit": "0.18.4",
+        "psr/log": "^1 || ^2 || ^3",
+        "vimeo/psalm": "4.30.0 || 5.12.0"
+    },
+    "suggest": {
+        "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+    },
+    "autoload": {
+        "psr-4": {
+            "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "DeprecationTests\\": "test_fixtures/src",
+            "Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo"
+        }
+    },
+    "config": {
+        "allow-plugins": {
+            "dealerdirect/phpcodesniffer-composer-installer": true
+        }
+    }
+}
diff --git a/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php b/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php
new file mode 100644
index 0000000..bad5070
--- /dev/null
+++ b/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php
@@ -0,0 +1,313 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Deprecations;
+
+use Psr\Log\LoggerInterface;
+
+use function array_key_exists;
+use function array_reduce;
+use function assert;
+use function debug_backtrace;
+use function sprintf;
+use function str_replace;
+use function strpos;
+use function strrpos;
+use function substr;
+use function trigger_error;
+
+use const DEBUG_BACKTRACE_IGNORE_ARGS;
+use const DIRECTORY_SEPARATOR;
+use const E_USER_DEPRECATED;
+
+/**
+ * Manages Deprecation logging in different ways.
+ *
+ * By default triggered exceptions are not logged.
+ *
+ * To enable different deprecation logging mechanisms you can call the
+ * following methods:
+ *
+ *  - Minimal collection of deprecations via getTriggeredDeprecations()
+ *    \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
+ *
+ *  - Uses @trigger_error with E_USER_DEPRECATED
+ *    \Doctrine\Deprecations\Deprecation::enableWithTriggerError();
+ *
+ *  - Sends deprecation messages via a PSR-3 logger
+ *    \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
+ *
+ * Packages that trigger deprecations should use the `trigger()` or
+ * `triggerIfCalledFromOutside()` methods.
+ */
+class Deprecation
+{
+    private const TYPE_NONE               = 0;
+    private const TYPE_TRACK_DEPRECATIONS = 1;
+    private const TYPE_TRIGGER_ERROR      = 2;
+    private const TYPE_PSR_LOGGER         = 4;
+
+    /** @var int-mask-of<self::TYPE_*>|null */
+    private static $type;
+
+    /** @var LoggerInterface|null */
+    private static $logger;
+
+    /** @var array<string,bool> */
+    private static $ignoredPackages = [];
+
+    /** @var array<string,int> */
+    private static $triggeredDeprecations = [];
+
+    /** @var array<string,bool> */
+    private static $ignoredLinks = [];
+
+    /** @var bool */
+    private static $deduplication = true;
+
+    /**
+     * Trigger a deprecation for the given package and identfier.
+     *
+     * The link should point to a Github issue or Wiki entry detailing the
+     * deprecation. It is additionally used to de-duplicate the trigger of the
+     * same deprecation during a request.
+     *
+     * @param float|int|string $args
+     */
+    public static function trigger(string $package, string $link, string $message, ...$args): void
+    {
+        $type = self::$type ?? self::getTypeFromEnv();
+
+        if ($type === self::TYPE_NONE) {
+            return;
+        }
+
+        if (isset(self::$ignoredLinks[$link])) {
+            return;
+        }
+
+        if (array_key_exists($link, self::$triggeredDeprecations)) {
+            self::$triggeredDeprecations[$link]++;
+        } else {
+            self::$triggeredDeprecations[$link] = 1;
+        }
+
+        if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) {
+            return;
+        }
+
+        if (isset(self::$ignoredPackages[$package])) {
+            return;
+        }
+
+        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+        $message = sprintf($message, ...$args);
+
+        self::delegateTriggerToBackend($message, $backtrace, $link, $package);
+    }
+
+    /**
+     * Trigger a deprecation for the given package and identifier when called from outside.
+     *
+     * "Outside" means we assume that $package is currently installed as a
+     * dependency and the caller is not a file in that package. When $package
+     * is installed as a root package then deprecations triggered from the
+     * tests folder are also considered "outside".
+     *
+     * This deprecation method assumes that you are using Composer to install
+     * the dependency and are using the default /vendor/ folder and not a
+     * Composer plugin to change the install location. The assumption is also
+     * that $package is the exact composer packge name.
+     *
+     * Compared to {@link trigger()} this method causes some overhead when
+     * deprecation tracking is enabled even during deduplication, because it
+     * needs to call {@link debug_backtrace()}
+     *
+     * @param float|int|string $args
+     */
+    public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void
+    {
+        $type = self::$type ?? self::getTypeFromEnv();
+
+        if ($type === self::TYPE_NONE) {
+            return;
+        }
+
+        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+        // first check that the caller is not from a tests folder, in which case we always let deprecations pass
+        if (isset($backtrace[1]['file'], $backtrace[0]['file']) && strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) {
+            $path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $package) . DIRECTORY_SEPARATOR;
+
+            if (strpos($backtrace[0]['file'], $path) === false) {
+                return;
+            }
+
+            if (strpos($backtrace[1]['file'], $path) !== false) {
+                return;
+            }
+        }
+
+        if (isset(self::$ignoredLinks[$link])) {
+            return;
+        }
+
+        if (array_key_exists($link, self::$triggeredDeprecations)) {
+            self::$triggeredDeprecations[$link]++;
+        } else {
+            self::$triggeredDeprecations[$link] = 1;
+        }
+
+        if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) {
+            return;
+        }
+
+        if (isset(self::$ignoredPackages[$package])) {
+            return;
+        }
+
+        $message = sprintf($message, ...$args);
+
+        self::delegateTriggerToBackend($message, $backtrace, $link, $package);
+    }
+
+    /**
+     * @param list<array{function: string, line?: int, file?: string, class?: class-string, type?: string, args?: mixed[], object?: object}> $backtrace
+     */
+    private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
+    {
+        $type = self::$type ?? self::getTypeFromEnv();
+
+        if (($type & self::TYPE_PSR_LOGGER) > 0) {
+            $context = [
+                'file' => $backtrace[0]['file'] ?? null,
+                'line' => $backtrace[0]['line'] ?? null,
+                'package' => $package,
+                'link' => $link,
+            ];
+
+            assert(self::$logger !== null);
+
+            self::$logger->notice($message, $context);
+        }
+
+        if (! (($type & self::TYPE_TRIGGER_ERROR) > 0)) {
+            return;
+        }
+
+        $message .= sprintf(
+            ' (%s:%d called by %s:%d, %s, package %s)',
+            self::basename($backtrace[0]['file'] ?? 'native code'),
+            $backtrace[0]['line'] ?? 0,
+            self::basename($backtrace[1]['file'] ?? 'native code'),
+            $backtrace[1]['line'] ?? 0,
+            $link,
+            $package
+        );
+
+        @trigger_error($message, E_USER_DEPRECATED);
+    }
+
+    /**
+     * A non-local-aware version of PHPs basename function.
+     */
+    private static function basename(string $filename): string
+    {
+        $pos = strrpos($filename, DIRECTORY_SEPARATOR);
+
+        if ($pos === false) {
+            return $filename;
+        }
+
+        return substr($filename, $pos + 1);
+    }
+
+    public static function enableTrackingDeprecations(): void
+    {
+        self::$type  = self::$type ?? 0;
+        self::$type |= self::TYPE_TRACK_DEPRECATIONS;
+    }
+
+    public static function enableWithTriggerError(): void
+    {
+        self::$type  = self::$type ?? 0;
+        self::$type |= self::TYPE_TRIGGER_ERROR;
+    }
+
+    public static function enableWithPsrLogger(LoggerInterface $logger): void
+    {
+        self::$type   = self::$type ?? 0;
+        self::$type  |= self::TYPE_PSR_LOGGER;
+        self::$logger = $logger;
+    }
+
+    public static function withoutDeduplication(): void
+    {
+        self::$deduplication = false;
+    }
+
+    public static function disable(): void
+    {
+        self::$type          = self::TYPE_NONE;
+        self::$logger        = null;
+        self::$deduplication = true;
+        self::$ignoredLinks  = [];
+
+        foreach (self::$triggeredDeprecations as $link => $count) {
+            self::$triggeredDeprecations[$link] = 0;
+        }
+    }
+
+    public static function ignorePackage(string $packageName): void
+    {
+        self::$ignoredPackages[$packageName] = true;
+    }
+
+    public static function ignoreDeprecations(string ...$links): void
+    {
+        foreach ($links as $link) {
+            self::$ignoredLinks[$link] = true;
+        }
+    }
+
+    public static function getUniqueTriggeredDeprecationsCount(): int
+    {
+        return array_reduce(self::$triggeredDeprecations, static function (int $carry, int $count) {
+            return $carry + $count;
+        }, 0);
+    }
+
+    /**
+     * Returns each triggered deprecation link identifier and the amount of occurrences.
+     *
+     * @return array<string,int>
+     */
+    public static function getTriggeredDeprecations(): array
+    {
+        return self::$triggeredDeprecations;
+    }
+
+    /**
+     * @return int-mask-of<self::TYPE_*>
+     */
+    private static function getTypeFromEnv(): int
+    {
+        switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) {
+            case 'trigger':
+                self::$type = self::TYPE_TRIGGER_ERROR;
+                break;
+
+            case 'track':
+                self::$type = self::TYPE_TRACK_DEPRECATIONS;
+                break;
+
+            default:
+                self::$type = self::TYPE_NONE;
+                break;
+        }
+
+        return self::$type;
+    }
+}
diff --git a/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php b/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php
new file mode 100644
index 0000000..4c3366a
--- /dev/null
+++ b/vendor/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php
@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Deprecations\PHPUnit;
+
+use Doctrine\Deprecations\Deprecation;
+
+use function sprintf;
+
+trait VerifyDeprecations
+{
+    /** @var array<string,int> */
+    private $doctrineDeprecationsExpectations = [];
+
+    /** @var array<string,int> */
+    private $doctrineNoDeprecationsExpectations = [];
+
+    public function expectDeprecationWithIdentifier(string $identifier): void
+    {
+        $this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
+    }
+
+    public function expectNoDeprecationWithIdentifier(string $identifier): void
+    {
+        $this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
+    }
+
+    /**
+     * @before
+     */
+    public function enableDeprecationTracking(): void
+    {
+        Deprecation::enableTrackingDeprecations();
+    }
+
+    /**
+     * @after
+     */
+    public function verifyDeprecationsAreTriggered(): void
+    {
+        foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) {
+            $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
+
+            $this->assertTrue(
+                $actualCount > $expectation,
+                sprintf(
+                    "Expected deprecation with identifier '%s' was not triggered by code executed in test.",
+                    $identifier
+                )
+            );
+        }
+
+        foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) {
+            $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
+
+            $this->assertTrue(
+                $actualCount === $expectation,
+                sprintf(
+                    "Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.",
+                    $identifier
+                )
+            );
+        }
+    }
+}
diff --git a/vendor/ernilambar/nepali-date/composer.json b/vendor/ernilambar/nepali-date/composer.json
index ce4168d..7e2e72e 100644
--- a/vendor/ernilambar/nepali-date/composer.json
+++ b/vendor/ernilambar/nepali-date/composer.json
@@ -1,47 +1,60 @@
 {
-  "name"        : "ernilambar/nepali-date",
-  "description" : "Nepali Date",
-  "keywords"    : [ "date" ],
-  "homepage"    : "https://github.com/ernilambar/nepali-date",
-  "license"     : "MIT",
-  "authors"     : [
+  "name": "ernilambar/nepali-date",
+  "description": "Nepali Date",
+  "keywords": [
+    "nepali",
+    "bikram-sambat",
+    "date"
+  ],
+  "homepage": "https://github.com/ernilambar/nepali-date",
+  "license": "MIT",
+  "authors": [
     {
-      "name"     : "Nilambar Sharma",
-      "email"    : "nilambar@outlook.com",
-      "homepage" : "https://www.nilambar.net",
-      "role"     : "Developer"
+      "name": "Nilambar Sharma",
+      "email": "nilambar@outlook.com",
+      "homepage": "https://www.nilambar.net",
+      "role": "Developer"
     }
   ],
-  "support"     : {
-    "issues" : "https://github.com/ernilambar/nepali-date/issues"
+  "support": {
+    "issues": "https://github.com/ernilambar/nepali-date/issues"
+  },
+  "autoload": {
+    "psr-4": {
+      "Nilambar\\NepaliDate\\": "src/"
+    }
+  },
+  "autoload-dev": {
+    "psr-4": {
+      "Nilambar\\NepaliDate\\Test\\": "tests/"
+    }
+  },
+  "require": {
+    "php": ">=5.6"
+  },
+  "config": {
+    "sort-packages": true
   },
   "scripts": {
-    "test": "phpunit",
-    "config-cs": [
-      "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run",
-      "\"vendor/bin/phpcs\" --config-set default_standard PSR2"
+    "format": [
+      "composer --working-dir=build-cs update --no-interaction",
+      "build-cs/vendor/bin/phpcbf --standard=phpcs.xml.dist"
     ],
-    "post-install-cmd": "@config-cs",
-    "post-update-cmd": "@config-cs",
-    "lint": "\"vendor/bin/phpcs\" . ",
-    "lint-fix": "\"vendor/bin/phpcbf\" . "
-  },
-  "autoload"    : {
-    "psr-4": { 
-      "Nilambar\\NepaliDate\\": "src/" 
-    }
-  },
-  "autoload-dev"    : {
-    "psr-4": { 
-      "Nilambar\\NepaliDate\\Test\\": "tests/" 
-    }
-  },
-  "require"     : {
-    "php" : ">=5.6"
-  },
-  "require-dev": {
-    "phpunit/phpunit": "^9",
-    "squizlabs/php_codesniffer": "^3.5",
-    "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2"
+    "format:tests": [
+      "composer --working-dir=build-cs update --no-interaction",
+      "build-cs/vendor/bin/phpcbf --standard=phpcs.tests.xml.dist"
+    ],
+    "lint": [
+      "composer --working-dir=build-cs update --no-interaction",
+      "build-cs/vendor/bin/phpcs --standard=phpcs.xml.dist"
+    ],
+    "lint:tests": [
+      "composer --working-dir=build-cs update --no-interaction",
+      "build-cs/vendor/bin/phpcs --standard=phpcs.tests.xml.dist"
+    ],
+    "test": [
+      "composer --working-dir=build-phpunit update --no-interaction",
+      "build-phpunit/vendor/bin/phpunit --verbose"
+    ]
   }
 }
diff --git a/vendor/ernilambar/nepali-date/composer.lock b/vendor/ernilambar/nepali-date/composer.lock
index a513c61..8f6730f 100644
--- a/vendor/ernilambar/nepali-date/composer.lock
+++ b/vendor/ernilambar/nepali-date/composer.lock
@@ -4,1839 +4,9 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "37c04f977eb29d6fe3620acb0279eef2",
+    "content-hash": "33f27bd6b4b26396a4c99b1a64f9e10b",
     "packages": [],
-    "packages-dev": [
-        {
-            "name": "dealerdirect/phpcodesniffer-composer-installer",
-            "version": "v0.6.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
-                "reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/8001af8eb107fbfcedc31a8b51e20b07d85b457a",
-                "reference": "8001af8eb107fbfcedc31a8b51e20b07d85b457a",
-                "shasum": ""
-            },
-            "require": {
-                "composer-plugin-api": "^1.0",
-                "php": "^5.3|^7",
-                "squizlabs/php_codesniffer": "^2|^3"
-            },
-            "require-dev": {
-                "composer/composer": "*",
-                "phpcompatibility/php-compatibility": "^9.0",
-                "sensiolabs/security-checker": "^4.1.0"
-            },
-            "type": "composer-plugin",
-            "extra": {
-                "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
-            },
-            "autoload": {
-                "psr-4": {
-                    "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Franck Nijhof",
-                    "email": "franck.nijhof@dealerdirect.com",
-                    "homepage": "http://www.frenck.nl",
-                    "role": "Developer / IT Manager"
-                }
-            ],
-            "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
-            "homepage": "http://www.dealerdirect.com",
-            "keywords": [
-                "PHPCodeSniffer",
-                "PHP_CodeSniffer",
-                "code quality",
-                "codesniffer",
-                "composer",
-                "installer",
-                "phpcs",
-                "plugin",
-                "qa",
-                "quality",
-                "standard",
-                "standards",
-                "style guide",
-                "stylecheck",
-                "tests"
-            ],
-            "time": "2020-01-29T20:22:20+00:00"
-        },
-        {
-            "name": "doctrine/instantiator",
-            "version": "1.3.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/doctrine/instantiator.git",
-                "reference": "f350df0268e904597e3bd9c4685c53e0e333feea"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea",
-                "reference": "f350df0268e904597e3bd9c4685c53e0e333feea",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.1 || ^8.0"
-            },
-            "require-dev": {
-                "doctrine/coding-standard": "^6.0",
-                "ext-pdo": "*",
-                "ext-phar": "*",
-                "phpbench/phpbench": "^0.13",
-                "phpstan/phpstan-phpunit": "^0.11",
-                "phpstan/phpstan-shim": "^0.11",
-                "phpunit/phpunit": "^7.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.2.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Marco Pivetta",
-                    "email": "ocramius@gmail.com",
-                    "homepage": "http://ocramius.github.com/"
-                }
-            ],
-            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
-            "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
-            "keywords": [
-                "constructor",
-                "instantiate"
-            ],
-            "funding": [
-                {
-                    "url": "https://www.doctrine-project.org/sponsorship.html",
-                    "type": "custom"
-                },
-                {
-                    "url": "https://www.patreon.com/phpdoctrine",
-                    "type": "patreon"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
-                    "type": "tidelift"
-                }
-            ],
-            "time": "2020-05-29T17:27:14+00:00"
-        },
-        {
-            "name": "myclabs/deep-copy",
-            "version": "1.9.5",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
-                "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.1"
-            },
-            "replace": {
-                "myclabs/deep-copy": "self.version"
-            },
-            "require-dev": {
-                "doctrine/collections": "^1.0",
-                "doctrine/common": "^2.6",
-                "phpunit/phpunit": "^7.1"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "DeepCopy\\": "src/DeepCopy/"
-                },
-                "files": [
-                    "src/DeepCopy/deep_copy.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "description": "Create deep copies (clones) of your objects",
-            "keywords": [
-                "clone",
-                "copy",
-                "duplicate",
-                "object",
-                "object graph"
-            ],
-            "time": "2020-01-17T21:11:47+00:00"
-        },
-        {
-            "name": "phar-io/manifest",
-            "version": "1.0.3",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phar-io/manifest.git",
-                "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
-                "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4",
-                "shasum": ""
-            },
-            "require": {
-                "ext-dom": "*",
-                "ext-phar": "*",
-                "phar-io/version": "^2.0",
-                "php": "^5.6 || ^7.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0.x-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
-                },
-                {
-                    "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
-                },
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
-                }
-            ],
-            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
-            "time": "2018-07-08T19:23:20+00:00"
-        },
-        {
-            "name": "phar-io/version",
-            "version": "2.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phar-io/version.git",
-                "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6",
-                "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.6 || ^7.0"
-            },
-            "type": "library",
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
-                },
-                {
-                    "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
-                },
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
-                }
-            ],
-            "description": "Library for handling version information and constraints",
-            "time": "2018-07-08T19:19:57+00:00"
-        },
-        {
-            "name": "phpdocumentor/reflection-common",
-            "version": "2.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
-                "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
-                "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=7.1"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "phpDocumentor\\Reflection\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Jaap van Otterdijk",
-                    "email": "opensource@ijaap.nl"
-                }
-            ],
-            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
-            "homepage": "http://www.phpdoc.org",
-            "keywords": [
-                "FQSEN",
-                "phpDocumentor",
-                "phpdoc",
-                "reflection",
-                "static analysis"
-            ],
-            "time": "2020-04-27T09:25:28+00:00"
-        },
-        {
-            "name": "phpdocumentor/reflection-docblock",
-            "version": "5.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e",
-                "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e",
-                "shasum": ""
-            },
-            "require": {
-                "ext-filter": "^7.1",
-                "php": "^7.2",
-                "phpdocumentor/reflection-common": "^2.0",
-                "phpdocumentor/type-resolver": "^1.0",
-                "webmozart/assert": "^1"
-            },
-            "require-dev": {
-                "doctrine/instantiator": "^1",
-                "mockery/mockery": "^1"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "5.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "phpDocumentor\\Reflection\\": "src"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Mike van Riel",
-                    "email": "me@mikevanriel.com"
-                },
-                {
-                    "name": "Jaap van Otterdijk",
-                    "email": "account@ijaap.nl"
-                }
-            ],
-            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
-            "time": "2020-02-22T12:28:44+00:00"
-        },
-        {
-            "name": "phpdocumentor/type-resolver",
-            "version": "1.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "7462d5f123dfc080dfdf26897032a6513644fc95"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95",
-                "reference": "7462d5f123dfc080dfdf26897032a6513644fc95",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.2",
-                "phpdocumentor/reflection-common": "^2.0"
-            },
-            "require-dev": {
-                "ext-tokenizer": "^7.2",
-                "mockery/mockery": "~1"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "phpDocumentor\\Reflection\\": "src"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Mike van Riel",
-                    "email": "me@mikevanriel.com"
-                }
-            ],
-            "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
-            "time": "2020-02-18T18:59:58+00:00"
-        },
-        {
-            "name": "phpspec/prophecy",
-            "version": "v1.10.3",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "451c3cd1418cf640de218914901e51b064abb093"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
-                "reference": "451c3cd1418cf640de218914901e51b064abb093",
-                "shasum": ""
-            },
-            "require": {
-                "doctrine/instantiator": "^1.0.2",
-                "php": "^5.3|^7.0",
-                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
-                "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
-                "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
-            },
-            "require-dev": {
-                "phpspec/phpspec": "^2.5 || ^3.2",
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.10.x-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Prophecy\\": "src/Prophecy"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Konstantin Kudryashov",
-                    "email": "ever.zet@gmail.com",
-                    "homepage": "http://everzet.com"
-                },
-                {
-                    "name": "Marcello Duarte",
-                    "email": "marcello.duarte@gmail.com"
-                }
-            ],
-            "description": "Highly opinionated mocking framework for PHP 5.3+",
-            "homepage": "https://github.com/phpspec/prophecy",
-            "keywords": [
-                "Double",
-                "Dummy",
-                "fake",
-                "mock",
-                "spy",
-                "stub"
-            ],
-            "time": "2020-03-05T15:02:03+00:00"
-        },
-        {
-            "name": "phpunit/php-code-coverage",
-            "version": "8.0.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "ca6647ffddd2add025ab3f21644a441d7c146cdc"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca6647ffddd2add025ab3f21644a441d7c146cdc",
-                "reference": "ca6647ffddd2add025ab3f21644a441d7c146cdc",
-                "shasum": ""
-            },
-            "require": {
-                "ext-dom": "*",
-                "ext-xmlwriter": "*",
-                "php": "^7.3",
-                "phpunit/php-file-iterator": "^3.0",
-                "phpunit/php-text-template": "^2.0",
-                "phpunit/php-token-stream": "^4.0",
-                "sebastian/code-unit-reverse-lookup": "^2.0",
-                "sebastian/environment": "^5.0",
-                "sebastian/version": "^3.0",
-                "theseer/tokenizer": "^1.1.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "suggest": {
-                "ext-pcov": "*",
-                "ext-xdebug": "*"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "8.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
-            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
-            "keywords": [
-                "coverage",
-                "testing",
-                "xunit"
-            ],
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-05-23T08:02:54+00:00"
-        },
-        {
-            "name": "phpunit/php-file-iterator",
-            "version": "3.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4",
-                "reference": "4ac5b3e13df14829daa60a2eb4fdd2f2b7d33cf4",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
-            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
-            "keywords": [
-                "filesystem",
-                "iterator"
-            ],
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-04-18T05:02:12+00:00"
-        },
-        {
-            "name": "phpunit/php-invoker",
-            "version": "3.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-invoker.git",
-                "reference": "7579d5a1ba7f3ac11c80004d205877911315ae7a"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/7579d5a1ba7f3ac11c80004d205877911315ae7a",
-                "reference": "7579d5a1ba7f3ac11c80004d205877911315ae7a",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "ext-pcntl": "*",
-                "phpunit/phpunit": "^9.0"
-            },
-            "suggest": {
-                "ext-pcntl": "*"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Invoke callables with a timeout",
-            "homepage": "https://github.com/sebastianbergmann/php-invoker/",
-            "keywords": [
-                "process"
-            ],
-            "time": "2020-02-07T06:06:11+00:00"
-        },
-        {
-            "name": "phpunit/php-text-template",
-            "version": "2.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-text-template.git",
-                "reference": "526dc996cc0ebdfa428cd2dfccd79b7b53fee346"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/526dc996cc0ebdfa428cd2dfccd79b7b53fee346",
-                "reference": "526dc996cc0ebdfa428cd2dfccd79b7b53fee346",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Simple template engine.",
-            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
-            "keywords": [
-                "template"
-            ],
-            "time": "2020-02-01T07:43:44+00:00"
-        },
-        {
-            "name": "phpunit/php-timer",
-            "version": "5.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-timer.git",
-                "reference": "b0d089de001ba60ffa3be36b23e1b8150d072238"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/b0d089de001ba60ffa3be36b23e1b8150d072238",
-                "reference": "b0d089de001ba60ffa3be36b23e1b8150d072238",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.2"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "5.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Utility class for timing",
-            "homepage": "https://github.com/sebastianbergmann/php-timer/",
-            "keywords": [
-                "timer"
-            ],
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-06-07T12:05:53+00:00"
-        },
-        {
-            "name": "phpunit/php-token-stream",
-            "version": "4.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "cdc0db5aed8fbfaf475fbd95bfd7bab83c7a779c"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/cdc0db5aed8fbfaf475fbd95bfd7bab83c7a779c",
-                "reference": "cdc0db5aed8fbfaf475fbd95bfd7bab83c7a779c",
-                "shasum": ""
-            },
-            "require": {
-                "ext-tokenizer": "*",
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Wrapper around PHP's tokenizer extension.",
-            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
-            "keywords": [
-                "tokenizer"
-            ],
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-05-06T09:56:31+00:00"
-        },
-        {
-            "name": "phpunit/phpunit",
-            "version": "9.2.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "8fd0d8f80029682da89516a554f4d5f5a030345c"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8fd0d8f80029682da89516a554f4d5f5a030345c",
-                "reference": "8fd0d8f80029682da89516a554f4d5f5a030345c",
-                "shasum": ""
-            },
-            "require": {
-                "doctrine/instantiator": "^1.2.0",
-                "ext-dom": "*",
-                "ext-json": "*",
-                "ext-libxml": "*",
-                "ext-mbstring": "*",
-                "ext-xml": "*",
-                "ext-xmlwriter": "*",
-                "myclabs/deep-copy": "^1.9.1",
-                "phar-io/manifest": "^1.0.3",
-                "phar-io/version": "^2.0.1",
-                "php": "^7.3",
-                "phpspec/prophecy": "^1.8.1",
-                "phpunit/php-code-coverage": "^8.0.1",
-                "phpunit/php-file-iterator": "^3.0",
-                "phpunit/php-invoker": "^3.0",
-                "phpunit/php-text-template": "^2.0",
-                "phpunit/php-timer": "^5.0",
-                "sebastian/code-unit": "^1.0.2",
-                "sebastian/comparator": "^4.0",
-                "sebastian/diff": "^4.0",
-                "sebastian/environment": "^5.0.1",
-                "sebastian/exporter": "^4.0",
-                "sebastian/global-state": "^4.0",
-                "sebastian/object-enumerator": "^4.0",
-                "sebastian/resource-operations": "^3.0",
-                "sebastian/type": "^2.1",
-                "sebastian/version": "^3.0"
-            },
-            "require-dev": {
-                "ext-pdo": "*",
-                "phpspec/prophecy-phpunit": "^2.0"
-            },
-            "suggest": {
-                "ext-soap": "*",
-                "ext-xdebug": "*"
-            },
-            "bin": [
-                "phpunit"
-            ],
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "9.2-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ],
-                "files": [
-                    "src/Framework/Assert/Functions.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "The PHP Unit Testing framework.",
-            "homepage": "https://phpunit.de/",
-            "keywords": [
-                "phpunit",
-                "testing",
-                "xunit"
-            ],
-            "funding": [
-                {
-                    "url": "https://phpunit.de/donate.html",
-                    "type": "custom"
-                },
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-06-07T14:14:21+00:00"
-        },
-        {
-            "name": "sebastian/code-unit",
-            "version": "1.0.2",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/code-unit.git",
-                "reference": "ac958085bc19fcd1d36425c781ef4cbb5b06e2a5"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ac958085bc19fcd1d36425c781ef4cbb5b06e2a5",
-                "reference": "ac958085bc19fcd1d36425c781ef4cbb5b06e2a5",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Collection of value objects that represent the PHP code units",
-            "homepage": "https://github.com/sebastianbergmann/code-unit",
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-04-30T05:58:10+00:00"
-        },
-        {
-            "name": "sebastian/code-unit-reverse-lookup",
-            "version": "2.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
-                "reference": "5b5dbe0044085ac41df47e79d34911a15b96d82e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5b5dbe0044085ac41df47e79d34911a15b96d82e",
-                "reference": "5b5dbe0044085ac41df47e79d34911a15b96d82e",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Looks up which function or method a line of code belongs to",
-            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
-            "time": "2020-02-07T06:20:13+00:00"
-        },
-        {
-            "name": "sebastian/comparator",
-            "version": "4.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/comparator.git",
-                "reference": "85b3435da967696ed618ff745f32be3ff4a2b8e8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85b3435da967696ed618ff745f32be3ff4a2b8e8",
-                "reference": "85b3435da967696ed618ff745f32be3ff4a2b8e8",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3",
-                "sebastian/diff": "^4.0",
-                "sebastian/exporter": "^4.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Jeff Welch",
-                    "email": "whatthejeff@gmail.com"
-                },
-                {
-                    "name": "Volker Dusch",
-                    "email": "github@wallbash.com"
-                },
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@2bepublished.at"
-                }
-            ],
-            "description": "Provides the functionality to compare PHP values for equality",
-            "homepage": "https://github.com/sebastianbergmann/comparator",
-            "keywords": [
-                "comparator",
-                "compare",
-                "equality"
-            ],
-            "time": "2020-02-07T06:08:51+00:00"
-        },
-        {
-            "name": "sebastian/diff",
-            "version": "4.0.1",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/diff.git",
-                "reference": "3e523c576f29dacecff309f35e4cc5a5c168e78a"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3e523c576f29dacecff309f35e4cc5a5c168e78a",
-                "reference": "3e523c576f29dacecff309f35e4cc5a5c168e78a",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0",
-                "symfony/process": "^4.2 || ^5"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Kore Nordmann",
-                    "email": "mail@kore-nordmann.de"
-                }
-            ],
-            "description": "Diff implementation",
-            "homepage": "https://github.com/sebastianbergmann/diff",
-            "keywords": [
-                "diff",
-                "udiff",
-                "unidiff",
-                "unified diff"
-            ],
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-05-08T05:01:12+00:00"
-        },
-        {
-            "name": "sebastian/environment",
-            "version": "5.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/environment.git",
-                "reference": "c753f04d68cd489b6973cf9b4e505e191af3b05c"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/c753f04d68cd489b6973cf9b4e505e191af3b05c",
-                "reference": "c753f04d68cd489b6973cf9b4e505e191af3b05c",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "suggest": {
-                "ext-posix": "*"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "5.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Provides functionality to handle HHVM/PHP environments",
-            "homepage": "http://www.github.com/sebastianbergmann/environment",
-            "keywords": [
-                "Xdebug",
-                "environment",
-                "hhvm"
-            ],
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-04-14T13:36:52+00:00"
-        },
-        {
-            "name": "sebastian/exporter",
-            "version": "4.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "80c26562e964016538f832f305b2286e1ec29566"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/80c26562e964016538f832f305b2286e1ec29566",
-                "reference": "80c26562e964016538f832f305b2286e1ec29566",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3",
-                "sebastian/recursion-context": "^4.0"
-            },
-            "require-dev": {
-                "ext-mbstring": "*",
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Jeff Welch",
-                    "email": "whatthejeff@gmail.com"
-                },
-                {
-                    "name": "Volker Dusch",
-                    "email": "github@wallbash.com"
-                },
-                {
-                    "name": "Adam Harvey",
-                    "email": "aharvey@php.net"
-                },
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@gmail.com"
-                }
-            ],
-            "description": "Provides the functionality to export PHP variables for visualization",
-            "homepage": "http://www.github.com/sebastianbergmann/exporter",
-            "keywords": [
-                "export",
-                "exporter"
-            ],
-            "time": "2020-02-07T06:10:52+00:00"
-        },
-        {
-            "name": "sebastian/global-state",
-            "version": "4.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/global-state.git",
-                "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bdb1e7c79e592b8c82cb1699be3c8743119b8a72",
-                "reference": "bdb1e7c79e592b8c82cb1699be3c8743119b8a72",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3",
-                "sebastian/object-reflector": "^2.0",
-                "sebastian/recursion-context": "^4.0"
-            },
-            "require-dev": {
-                "ext-dom": "*",
-                "phpunit/phpunit": "^9.0"
-            },
-            "suggest": {
-                "ext-uopz": "*"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Snapshotting of global state",
-            "homepage": "http://www.github.com/sebastianbergmann/global-state",
-            "keywords": [
-                "global state"
-            ],
-            "time": "2020-02-07T06:11:37+00:00"
-        },
-        {
-            "name": "sebastian/object-enumerator",
-            "version": "4.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
-                "reference": "e67516b175550abad905dc952f43285957ef4363"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67516b175550abad905dc952f43285957ef4363",
-                "reference": "e67516b175550abad905dc952f43285957ef4363",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3",
-                "sebastian/object-reflector": "^2.0",
-                "sebastian/recursion-context": "^4.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
-            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
-            "time": "2020-02-07T06:12:23+00:00"
-        },
-        {
-            "name": "sebastian/object-reflector",
-            "version": "2.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/object-reflector.git",
-                "reference": "f4fd0835cabb0d4a6546d9fe291e5740037aa1e7"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/f4fd0835cabb0d4a6546d9fe291e5740037aa1e7",
-                "reference": "f4fd0835cabb0d4a6546d9fe291e5740037aa1e7",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Allows reflection of object attributes, including inherited and non-public ones",
-            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
-            "time": "2020-02-07T06:19:40+00:00"
-        },
-        {
-            "name": "sebastian/recursion-context",
-            "version": "4.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/recursion-context.git",
-                "reference": "cdd86616411fc3062368b720b0425de10bd3d579"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cdd86616411fc3062368b720b0425de10bd3d579",
-                "reference": "cdd86616411fc3062368b720b0425de10bd3d579",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                },
-                {
-                    "name": "Jeff Welch",
-                    "email": "whatthejeff@gmail.com"
-                },
-                {
-                    "name": "Adam Harvey",
-                    "email": "aharvey@php.net"
-                }
-            ],
-            "description": "Provides functionality to recursively process PHP variables",
-            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
-            "time": "2020-02-07T06:18:20+00:00"
-        },
-        {
-            "name": "sebastian/resource-operations",
-            "version": "3.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/resource-operations.git",
-                "reference": "8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98",
-                "reference": "8c98bf0dfa1f9256d0468b9803a1e1df31b6fa98",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.0"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de"
-                }
-            ],
-            "description": "Provides a list of PHP built-in functions that operate on resources",
-            "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
-            "time": "2020-02-07T06:13:02+00:00"
-        },
-        {
-            "name": "sebastian/type",
-            "version": "2.1.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/type.git",
-                "reference": "bad49207c6f854e7a25cef0ea948ac8ebe3ef9d8"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/bad49207c6f854e7a25cef0ea948ac8ebe3ef9d8",
-                "reference": "bad49207c6f854e7a25cef0ea948ac8ebe3ef9d8",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^9.2"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "2.1-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Collection of value objects that represent the types of the PHP type system",
-            "homepage": "https://github.com/sebastianbergmann/type",
-            "funding": [
-                {
-                    "url": "https://github.com/sebastianbergmann",
-                    "type": "github"
-                }
-            ],
-            "time": "2020-06-01T12:21:09+00:00"
-        },
-        {
-            "name": "sebastian/version",
-            "version": "3.0.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/sebastianbergmann/version.git",
-                "reference": "0411bde656dce64202b39c2f4473993a9081d39e"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/0411bde656dce64202b39c2f4473993a9081d39e",
-                "reference": "0411bde656dce64202b39c2f4473993a9081d39e",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^7.3"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.0-dev"
-                }
-            },
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
-                }
-            ],
-            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
-            "homepage": "https://github.com/sebastianbergmann/version",
-            "time": "2020-01-21T06:36:37+00:00"
-        },
-        {
-            "name": "squizlabs/php_codesniffer",
-            "version": "3.5.5",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
-                "reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
-                "shasum": ""
-            },
-            "require": {
-                "ext-simplexml": "*",
-                "ext-tokenizer": "*",
-                "ext-xmlwriter": "*",
-                "php": ">=5.4.0"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
-            },
-            "bin": [
-                "bin/phpcs",
-                "bin/phpcbf"
-            ],
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "3.x-dev"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Greg Sherwood",
-                    "role": "lead"
-                }
-            ],
-            "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
-            "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
-            "keywords": [
-                "phpcs",
-                "standards"
-            ],
-            "time": "2020-04-17T01:09:41+00:00"
-        },
-        {
-            "name": "symfony/polyfill-ctype",
-            "version": "v1.17.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
-                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
-                "shasum": ""
-            },
-            "require": {
-                "php": ">=5.3.3"
-            },
-            "suggest": {
-                "ext-ctype": "For best performance"
-            },
-            "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.17-dev"
-                }
-            },
-            "autoload": {
-                "psr-4": {
-                    "Symfony\\Polyfill\\Ctype\\": ""
-                },
-                "files": [
-                    "bootstrap.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Gert de Pagter",
-                    "email": "BackEndTea@gmail.com"
-                },
-                {
-                    "name": "Symfony Community",
-                    "homepage": "https://symfony.com/contributors"
-                }
-            ],
-            "description": "Symfony polyfill for ctype functions",
-            "homepage": "https://symfony.com",
-            "keywords": [
-                "compatibility",
-                "ctype",
-                "polyfill",
-                "portable"
-            ],
-            "funding": [
-                {
-                    "url": "https://symfony.com/sponsor",
-                    "type": "custom"
-                },
-                {
-                    "url": "https://github.com/fabpot",
-                    "type": "github"
-                },
-                {
-                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
-                    "type": "tidelift"
-                }
-            ],
-            "time": "2020-05-12T16:14:59+00:00"
-        },
-        {
-            "name": "theseer/tokenizer",
-            "version": "1.1.3",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/theseer/tokenizer.git",
-                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
-                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
-                "shasum": ""
-            },
-            "require": {
-                "ext-dom": "*",
-                "ext-tokenizer": "*",
-                "ext-xmlwriter": "*",
-                "php": "^7.0"
-            },
-            "type": "library",
-            "autoload": {
-                "classmap": [
-                    "src/"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "BSD-3-Clause"
-            ],
-            "authors": [
-                {
-                    "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
-                }
-            ],
-            "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
-            "time": "2019-06-13T22:48:21+00:00"
-        },
-        {
-            "name": "webmozart/assert",
-            "version": "1.8.0",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/webmozart/assert.git",
-                "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
-                "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
-                "shasum": ""
-            },
-            "require": {
-                "php": "^5.3.3 || ^7.0",
-                "symfony/polyfill-ctype": "^1.8"
-            },
-            "conflict": {
-                "vimeo/psalm": "<3.9.1"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "^4.8.36 || ^7.5.13"
-            },
-            "type": "library",
-            "autoload": {
-                "psr-4": {
-                    "Webmozart\\Assert\\": "src/"
-                }
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Bernhard Schussek",
-                    "email": "bschussek@gmail.com"
-                }
-            ],
-            "description": "Assertions to validate method input/output with nice error messages.",
-            "keywords": [
-                "assert",
-                "check",
-                "validate"
-            ],
-            "time": "2020-04-18T12:12:48+00:00"
-        }
-    ],
+    "packages-dev": [],
     "aliases": [],
     "minimum-stability": "stable",
     "stability-flags": [],
@@ -1846,5 +16,5 @@
         "php": ">=5.6"
     },
     "platform-dev": [],
-    "plugin-api-version": "1.1.0"
+    "plugin-api-version": "2.6.0"
 }
diff --git a/vendor/ernilambar/nepali-date/phpcs.tests.xml.dist b/vendor/ernilambar/nepali-date/phpcs.tests.xml.dist
new file mode 100644
index 0000000..c3ba156
--- /dev/null
+++ b/vendor/ernilambar/nepali-date/phpcs.tests.xml.dist
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<ruleset name="project-default">
+	<description>A custom set of code standard rules to check for tests.</description>
+
+	<!-- Only check the PHP files. -->
+	<arg name="extensions" value="php"/>
+
+	<!-- Check all files in this directory and the directories below it. -->
+	<file>tests/</file>
+
+	<exclude-pattern>*/phpunit.xml*</exclude-pattern>
+
+	<!-- Third-party code -->
+	<exclude-pattern>*/vendor/*</exclude-pattern>
+
+	<!-- For CI, don't fail on warnings -->
+	<config name="ignore_warnings_on_exit" value="1"/>
+
+	<config name="testVersion" value="7.4-"/>
+
+	<!-- Enforce PSR12 standards -->
+	<rule ref="PSR12" />
+</ruleset>
diff --git a/vendor/ernilambar/nepali-date/src/NepaliCalendar.php b/vendor/ernilambar/nepali-date/src/NepaliCalendar.php
index 6271ab8..adc0e82 100644
--- a/vendor/ernilambar/nepali-date/src/NepaliCalendar.php
+++ b/vendor/ernilambar/nepali-date/src/NepaliCalendar.php
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * Calendar class
  *
@@ -103,7 +104,7 @@ class NepaliCalendar
         78 => array( 2078, 31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30 ),
         79 => array( 2079, 31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30 ),
         80 => array( 2080, 31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30 ),
-        81 => array( 2081, 31, 31, 32, 32, 31, 30, 30, 30, 29, 30, 30, 30 ),
+        81 => array( 2081, 31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31 ),
         82 => array( 2082, 30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 30, 30 ),
         83 => array( 2083, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30 ),
         84 => array( 2084, 31, 31, 32, 31, 31, 30, 30, 30, 29, 30, 30, 30 ),
@@ -411,4 +412,4 @@ class NepaliCalendar
             return $output;
         }
     }
-};
+}
diff --git a/vendor/ernilambar/nepali-date/src/NepaliDate.php b/vendor/ernilambar/nepali-date/src/NepaliDate.php
index db11ac8..fe8b75f 100644
--- a/vendor/ernilambar/nepali-date/src/NepaliDate.php
+++ b/vendor/ernilambar/nepali-date/src/NepaliDate.php
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * Nepali Date class
  *
@@ -17,7 +18,6 @@ namespace Nilambar\NepaliDate;
  */
 class NepaliDate
 {
-
     /**
      * NepaliCalendar object.
      *
@@ -94,7 +94,7 @@ class NepaliDate
      */
     public function getFormattedDate($date, $format)
     {
-    	return strtr($format, $date);
+        return strtr($format, $date);
     }
 
     /**
@@ -158,9 +158,11 @@ class NepaliDate
                 );
 
                 if (is_array($temp_date) && ! empty($temp_date)) {
-                    if (intval($y) === intval($temp_date['year'])
+                    if (
+                        intval($y) === intval($temp_date['year'])
                         && intval($m) === intval($temp_date['month'])
-                        && intval($d) === intval($temp_date['day'])) {
+                        && intval($d) === intval($temp_date['day'])
+                    ) {
                         $output = $temp_date;
                     }
                 }
@@ -176,9 +178,11 @@ class NepaliDate
                 );
 
                 if (is_array($temp_date) && ! empty($temp_date)) {
-                    if (intval($y) === intval($temp_date['year'])
+                    if (
+                        intval($y) === intval($temp_date['year'])
                         && intval($m) === intval($temp_date['month'])
-                        && intval($d) === intval($temp_date['day'])) {
+                        && intval($d) === intval($temp_date['day'])
+                    ) {
                         $output = $temp_date;
                     }
                 }
@@ -344,38 +348,38 @@ class NepaliDate
         $output = array(
             '1'  => array(
                 'en' => 'Baishakh',
-                'np' => 'बैसाख',
+                'np' => 'वैशाख',
             ),
             '2'  => array(
-                'en' => 'Jeth',
+                'en' => 'Jestha',
                 'np' => 'जेठ',
             ),
             '3'  => array(
-                'en' => 'Ashar',
+                'en' => 'Ashadh',
                 'np' => 'असार',
             ),
             '4'  => array(
                 'en' => 'Shrawan',
-                'np' => 'श्रावन',
+                'np' => 'साउन',
             ),
             '5'  => array(
                 'en' => 'Bhadra',
-                'np' => 'भाद्र',
+                'np' => 'भदौ',
             ),
             '6'  => array(
-                'en' => 'Ashoj',
+                'en' => 'Ashwin',
                 'np' => 'असोज',
             ),
             '7'  => array(
                 'en' => 'Kartik',
-                'np' => 'कार्तिक',
+                'np' => 'कात्तिक',
             ),
             '8'  => array(
-                'en' => 'Mangshir',
+                'en' => 'Mangsir',
                 'np' => 'मंसिर',
             ),
             '9'  => array(
-                'en' => 'Poush',
+                'en' => 'Paush',
                 'np' => 'पुष',
             ),
             '10' => array(
@@ -384,11 +388,11 @@ class NepaliDate
             ),
             '11' => array(
                 'en' => 'Falgun',
-                'np' => 'फाल्गुण',
+                'np' => 'फागुन',
             ),
             '12' => array(
                 'en' => 'Chaitra',
-                'np' => 'चैत्र',
+                'np' => 'चैत',
             ),
         );
 
diff --git a/vendor/ezyang/htmlpurifier/CHANGELOG.md b/vendor/ezyang/htmlpurifier/CHANGELOG.md
deleted file mode 100644
index 55cb902..0000000
--- a/vendor/ezyang/htmlpurifier/CHANGELOG.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# [4.16.0](https://github.com/ezyang/htmlpurifier/compare/v4.15.0...v4.16.0) (2022-09-18)
-
-
-### Features
-
-* add semantic release ([#307](https://github.com/ezyang/htmlpurifier/issues/307)) ([db31243](https://github.com/ezyang/htmlpurifier/commit/db312435cb9d8d73395f75f9642a43ba6de5e903)), closes [#322](https://github.com/ezyang/htmlpurifier/issues/322) [#323](https://github.com/ezyang/htmlpurifier/issues/323) [#326](https://github.com/ezyang/htmlpurifier/issues/326) [#327](https://github.com/ezyang/htmlpurifier/issues/327) [#328](https://github.com/ezyang/htmlpurifier/issues/328) [#329](https://github.com/ezyang/htmlpurifier/issues/329) [#330](https://github.com/ezyang/htmlpurifier/issues/330) [#331](https://github.com/ezyang/htmlpurifier/issues/331) [#332](https://github.com/ezyang/htmlpurifier/issues/332) [#333](https://github.com/ezyang/htmlpurifier/issues/333) [#337](https://github.com/ezyang/htmlpurifier/issues/337) [#335](https://github.com/ezyang/htmlpurifier/issues/335) [ezyang/htmlpurifier#334](https://github.com/ezyang/htmlpurifier/issues/334) [#336](https://github.com/ezyang/htmlpurifier/issues/336) [#338](https://github.com/ezyang/htmlpurifier/issues/338)
diff --git a/vendor/ezyang/htmlpurifier/CREDITS b/vendor/ezyang/htmlpurifier/CREDITS
deleted file mode 100644
index 7921b45..0000000
--- a/vendor/ezyang/htmlpurifier/CREDITS
+++ /dev/null
@@ -1,9 +0,0 @@
-
-CREDITS
-
-Almost everything written by Edward Z. Yang (Ambush Commander).  Lots of thanks
-to the DevNetwork Community for their help (see docs/ref-devnetwork.html for
-more details), Feyd especially (namely IPv6 and optimization).  Thanks to RSnake
-for letting me package his fantastic XSS cheatsheet for a smoketest.
-
-    vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/LICENSE b/vendor/ezyang/htmlpurifier/LICENSE
deleted file mode 100644
index 8c88a20..0000000
--- a/vendor/ezyang/htmlpurifier/LICENSE
+++ /dev/null
@@ -1,504 +0,0 @@
-		  GNU LESSER GENERAL PUBLIC LICENSE
-		       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
-		  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-			    NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-           How to Apply These Terms to Your New Libraries
-
-  If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.  It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the library's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  <signature of Ty Coon>, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
-
-    vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/README.md b/vendor/ezyang/htmlpurifier/README.md
deleted file mode 100644
index e6b7199..0000000
--- a/vendor/ezyang/htmlpurifier/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-HTML Purifier [![Build Status](https://github.com/ezyang/htmlpurifier/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/ezyang/htmlpurifier/actions/workflows/ci.yml)
-=============
-
-HTML Purifier is an HTML filtering solution that uses a unique combination
-of robust whitelists and aggressive parsing to ensure that not only are
-XSS attacks thwarted, but the resulting HTML is standards compliant.
-
-HTML Purifier is oriented towards richly formatted documents from
-untrusted sources that require CSS and a full tag-set.  This library can
-be configured to accept a more restrictive set of tags, but it won't be
-as efficient as more bare-bones parsers. It will, however, do the job
-right, which may be more important.
-
-Places to go:
-
-* See INSTALL for a quick installation guide
-* See docs/ for developer-oriented documentation, code examples and
-  an in-depth installation guide.
-* See WYSIWYG for information on editors like TinyMCE and FCKeditor
-
-HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/)
-
-## Installation
-
-Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier).
-
-If you're using Composer to manage dependencies, you can use
-
-    $ composer require ezyang/htmlpurifier
diff --git a/vendor/ezyang/htmlpurifier/VERSION b/vendor/ezyang/htmlpurifier/VERSION
deleted file mode 100644
index f029ee5..0000000
--- a/vendor/ezyang/htmlpurifier/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-4.15.0
\ No newline at end of file
diff --git a/vendor/ezyang/htmlpurifier/composer.json b/vendor/ezyang/htmlpurifier/composer.json
deleted file mode 100644
index d755829..0000000
--- a/vendor/ezyang/htmlpurifier/composer.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
-    "name": "ezyang/htmlpurifier",
-    "description": "Standards compliant HTML filter written in PHP",
-    "type": "library",
-    "keywords": ["html"],
-    "homepage": "http://htmlpurifier.org/",
-    "license": "LGPL-2.1-or-later",
-    "authors": [
-        {
-            "name": "Edward Z. Yang",
-            "email": "admin@htmlpurifier.org",
-            "homepage": "http://ezyang.com"
-        }
-    ],
-    "require": {
-        "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
-    },
-    "require-dev": {
-        "cerdic/css-tidy": "^1.7 || ^2.0",
-        "simpletest/simpletest": "dev-master"
-    },
-    "autoload": {
-        "psr-0": { "HTMLPurifier": "library/" },
-        "files": ["library/HTMLPurifier.composer.php"],
-        "exclude-from-classmap": [
-            "/library/HTMLPurifier/Language/"
-        ]
-    },
-    "suggest": {
-        "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
-        "ext-iconv": "Converts text to and from non-UTF-8 encodings",
-        "ext-bcmath": "Used for unit conversion and imagecrash protection",
-        "ext-tidy": "Used for pretty-printing HTML"
-    },
-    "config": {
-        "sort-packages": true
-    },
-    "repositories": [
-        {
-            "type": "vcs",
-            "url": "https://github.com/ezyang/simpletest.git"
-        }
-    ]
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php
deleted file mode 100644
index 1960c39..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.auto.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * This is a stub include that automatically configures the include path.
- */
-
-set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
-require_once 'HTMLPurifier/Bootstrap.php';
-require_once 'HTMLPurifier.autoload.php';
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload-legacy.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload-legacy.php
deleted file mode 100644
index 0a17ee4..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload-legacy.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-/**
- * @file
- * Legacy autoloader for systems lacking spl_autoload_register
- *
- */
-
-spl_autoload_register(function($class)
-{
-     return HTMLPurifier_Bootstrap::autoload($class);
-});
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload.php
deleted file mode 100644
index 7a69113..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.autoload.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/**
- * @file
- * Convenience file that registers autoload handler for HTML Purifier.
- * It also does some sanity checks.
- */
-
-if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) {
-    // We need unregister for our pre-registering functionality
-    HTMLPurifier_Bootstrap::registerAutoload();
-    if (function_exists('__autoload')) {
-        // Be polite and ensure that userland autoload gets retained
-        spl_autoload_register('__autoload');
-    }
-} elseif (!function_exists('__autoload')) {
-    require dirname(__FILE__) . '/HTMLPurifier.autoload-legacy.php';
-}
-
-// phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.zend_ze1_compatibility_modeRemoved
-if (ini_get('zend.ze1_compatibility_mode')) {
-    trigger_error("HTML Purifier is not compatible with zend.ze1_compatibility_mode; please turn it off", E_USER_ERROR);
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.composer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.composer.php
deleted file mode 100644
index 52acc56..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.composer.php
+++ /dev/null
@@ -1,4 +0,0 @@
-<?php
-if (!defined('HTMLPURIFIER_PREFIX')) {
-    define('HTMLPURIFIER_PREFIX', dirname(__FILE__));
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.func.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.func.php
deleted file mode 100644
index 64b140b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.func.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/**
- * @file
- * Defines a function wrapper for HTML Purifier for quick use.
- * @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
- */
-
-/**
- * Purify HTML.
- * @param string $html String HTML to purify
- * @param mixed $config Configuration to use, can be any value accepted by
- *        HTMLPurifier_Config::create()
- * @return string
- */
-function HTMLPurifier($html, $config = null)
-{
-    static $purifier = false;
-    if (!$purifier) {
-        $purifier = new HTMLPurifier();
-    }
-    return $purifier->purify($html, $config);
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.includes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.includes.php
deleted file mode 100644
index 47ee013..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.includes.php
+++ /dev/null
@@ -1,235 +0,0 @@
-<?php
-
-/**
- * @file
- * This file was auto-generated by generate-includes.php and includes all of
- * the core files required by HTML Purifier. Use this if performance is a
- * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
- * FILE, changes will be overwritten the next time the script is run.
- *
- * @version 4.15.0
- *
- * @warning
- *      You must *not* include any other HTML Purifier files before this file,
- *      because 'require' not 'require_once' is used.
- *
- * @warning
- *      This file requires that the include path contains the HTML Purifier
- *      library directory; this is not auto-set.
- */
-
-require 'HTMLPurifier.php';
-require 'HTMLPurifier/Arborize.php';
-require 'HTMLPurifier/AttrCollections.php';
-require 'HTMLPurifier/AttrDef.php';
-require 'HTMLPurifier/AttrTransform.php';
-require 'HTMLPurifier/AttrTypes.php';
-require 'HTMLPurifier/AttrValidator.php';
-require 'HTMLPurifier/Bootstrap.php';
-require 'HTMLPurifier/Definition.php';
-require 'HTMLPurifier/CSSDefinition.php';
-require 'HTMLPurifier/ChildDef.php';
-require 'HTMLPurifier/Config.php';
-require 'HTMLPurifier/ConfigSchema.php';
-require 'HTMLPurifier/ContentSets.php';
-require 'HTMLPurifier/Context.php';
-require 'HTMLPurifier/DefinitionCache.php';
-require 'HTMLPurifier/DefinitionCacheFactory.php';
-require 'HTMLPurifier/Doctype.php';
-require 'HTMLPurifier/DoctypeRegistry.php';
-require 'HTMLPurifier/ElementDef.php';
-require 'HTMLPurifier/Encoder.php';
-require 'HTMLPurifier/EntityLookup.php';
-require 'HTMLPurifier/EntityParser.php';
-require 'HTMLPurifier/ErrorCollector.php';
-require 'HTMLPurifier/ErrorStruct.php';
-require 'HTMLPurifier/Exception.php';
-require 'HTMLPurifier/Filter.php';
-require 'HTMLPurifier/Generator.php';
-require 'HTMLPurifier/HTMLDefinition.php';
-require 'HTMLPurifier/HTMLModule.php';
-require 'HTMLPurifier/HTMLModuleManager.php';
-require 'HTMLPurifier/IDAccumulator.php';
-require 'HTMLPurifier/Injector.php';
-require 'HTMLPurifier/Language.php';
-require 'HTMLPurifier/LanguageFactory.php';
-require 'HTMLPurifier/Length.php';
-require 'HTMLPurifier/Lexer.php';
-require 'HTMLPurifier/Node.php';
-require 'HTMLPurifier/PercentEncoder.php';
-require 'HTMLPurifier/PropertyList.php';
-require 'HTMLPurifier/PropertyListIterator.php';
-require 'HTMLPurifier/Queue.php';
-require 'HTMLPurifier/Strategy.php';
-require 'HTMLPurifier/StringHash.php';
-require 'HTMLPurifier/StringHashParser.php';
-require 'HTMLPurifier/TagTransform.php';
-require 'HTMLPurifier/Token.php';
-require 'HTMLPurifier/TokenFactory.php';
-require 'HTMLPurifier/URI.php';
-require 'HTMLPurifier/URIDefinition.php';
-require 'HTMLPurifier/URIFilter.php';
-require 'HTMLPurifier/URIParser.php';
-require 'HTMLPurifier/URIScheme.php';
-require 'HTMLPurifier/URISchemeRegistry.php';
-require 'HTMLPurifier/UnitConverter.php';
-require 'HTMLPurifier/VarParser.php';
-require 'HTMLPurifier/VarParserException.php';
-require 'HTMLPurifier/Zipper.php';
-require 'HTMLPurifier/AttrDef/CSS.php';
-require 'HTMLPurifier/AttrDef/Clone.php';
-require 'HTMLPurifier/AttrDef/Enum.php';
-require 'HTMLPurifier/AttrDef/Integer.php';
-require 'HTMLPurifier/AttrDef/Lang.php';
-require 'HTMLPurifier/AttrDef/Switch.php';
-require 'HTMLPurifier/AttrDef/Text.php';
-require 'HTMLPurifier/AttrDef/URI.php';
-require 'HTMLPurifier/AttrDef/CSS/Number.php';
-require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
-require 'HTMLPurifier/AttrDef/CSS/Background.php';
-require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
-require 'HTMLPurifier/AttrDef/CSS/Border.php';
-require 'HTMLPurifier/AttrDef/CSS/Color.php';
-require 'HTMLPurifier/AttrDef/CSS/Composite.php';
-require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
-require 'HTMLPurifier/AttrDef/CSS/Filter.php';
-require 'HTMLPurifier/AttrDef/CSS/Font.php';
-require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
-require 'HTMLPurifier/AttrDef/CSS/Ident.php';
-require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
-require 'HTMLPurifier/AttrDef/CSS/Length.php';
-require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
-require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
-require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
-require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
-require 'HTMLPurifier/AttrDef/CSS/URI.php';
-require 'HTMLPurifier/AttrDef/HTML/Bool.php';
-require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
-require 'HTMLPurifier/AttrDef/HTML/Class.php';
-require 'HTMLPurifier/AttrDef/HTML/Color.php';
-require 'HTMLPurifier/AttrDef/HTML/ContentEditable.php';
-require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
-require 'HTMLPurifier/AttrDef/HTML/ID.php';
-require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
-require 'HTMLPurifier/AttrDef/HTML/Length.php';
-require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
-require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
-require 'HTMLPurifier/AttrDef/URI/Email.php';
-require 'HTMLPurifier/AttrDef/URI/Host.php';
-require 'HTMLPurifier/AttrDef/URI/IPv4.php';
-require 'HTMLPurifier/AttrDef/URI/IPv6.php';
-require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
-require 'HTMLPurifier/AttrTransform/Background.php';
-require 'HTMLPurifier/AttrTransform/BdoDir.php';
-require 'HTMLPurifier/AttrTransform/BgColor.php';
-require 'HTMLPurifier/AttrTransform/BoolToCSS.php';
-require 'HTMLPurifier/AttrTransform/Border.php';
-require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
-require 'HTMLPurifier/AttrTransform/ImgRequired.php';
-require 'HTMLPurifier/AttrTransform/ImgSpace.php';
-require 'HTMLPurifier/AttrTransform/Input.php';
-require 'HTMLPurifier/AttrTransform/Lang.php';
-require 'HTMLPurifier/AttrTransform/Length.php';
-require 'HTMLPurifier/AttrTransform/Name.php';
-require 'HTMLPurifier/AttrTransform/NameSync.php';
-require 'HTMLPurifier/AttrTransform/Nofollow.php';
-require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
-require 'HTMLPurifier/AttrTransform/SafeObject.php';
-require 'HTMLPurifier/AttrTransform/SafeParam.php';
-require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
-require 'HTMLPurifier/AttrTransform/TargetBlank.php';
-require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
-require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
-require 'HTMLPurifier/AttrTransform/Textarea.php';
-require 'HTMLPurifier/ChildDef/Chameleon.php';
-require 'HTMLPurifier/ChildDef/Custom.php';
-require 'HTMLPurifier/ChildDef/Empty.php';
-require 'HTMLPurifier/ChildDef/List.php';
-require 'HTMLPurifier/ChildDef/Required.php';
-require 'HTMLPurifier/ChildDef/Optional.php';
-require 'HTMLPurifier/ChildDef/StrictBlockquote.php';
-require 'HTMLPurifier/ChildDef/Table.php';
-require 'HTMLPurifier/DefinitionCache/Decorator.php';
-require 'HTMLPurifier/DefinitionCache/Null.php';
-require 'HTMLPurifier/DefinitionCache/Serializer.php';
-require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
-require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
-require 'HTMLPurifier/HTMLModule/Bdo.php';
-require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
-require 'HTMLPurifier/HTMLModule/Edit.php';
-require 'HTMLPurifier/HTMLModule/Forms.php';
-require 'HTMLPurifier/HTMLModule/Hypertext.php';
-require 'HTMLPurifier/HTMLModule/Iframe.php';
-require 'HTMLPurifier/HTMLModule/Image.php';
-require 'HTMLPurifier/HTMLModule/Legacy.php';
-require 'HTMLPurifier/HTMLModule/List.php';
-require 'HTMLPurifier/HTMLModule/Name.php';
-require 'HTMLPurifier/HTMLModule/Nofollow.php';
-require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
-require 'HTMLPurifier/HTMLModule/Object.php';
-require 'HTMLPurifier/HTMLModule/Presentation.php';
-require 'HTMLPurifier/HTMLModule/Proprietary.php';
-require 'HTMLPurifier/HTMLModule/Ruby.php';
-require 'HTMLPurifier/HTMLModule/SafeEmbed.php';
-require 'HTMLPurifier/HTMLModule/SafeObject.php';
-require 'HTMLPurifier/HTMLModule/SafeScripting.php';
-require 'HTMLPurifier/HTMLModule/Scripting.php';
-require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
-require 'HTMLPurifier/HTMLModule/Tables.php';
-require 'HTMLPurifier/HTMLModule/Target.php';
-require 'HTMLPurifier/HTMLModule/TargetBlank.php';
-require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
-require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
-require 'HTMLPurifier/HTMLModule/Text.php';
-require 'HTMLPurifier/HTMLModule/Tidy.php';
-require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
-require 'HTMLPurifier/HTMLModule/Tidy/Name.php';
-require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
-require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
-require 'HTMLPurifier/HTMLModule/Tidy/Strict.php';
-require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php';
-require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php';
-require 'HTMLPurifier/Injector/AutoParagraph.php';
-require 'HTMLPurifier/Injector/DisplayLinkURI.php';
-require 'HTMLPurifier/Injector/Linkify.php';
-require 'HTMLPurifier/Injector/PurifierLinkify.php';
-require 'HTMLPurifier/Injector/RemoveEmpty.php';
-require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
-require 'HTMLPurifier/Injector/SafeObject.php';
-require 'HTMLPurifier/Lexer/DOMLex.php';
-require 'HTMLPurifier/Lexer/DirectLex.php';
-require 'HTMLPurifier/Node/Comment.php';
-require 'HTMLPurifier/Node/Element.php';
-require 'HTMLPurifier/Node/Text.php';
-require 'HTMLPurifier/Strategy/Composite.php';
-require 'HTMLPurifier/Strategy/Core.php';
-require 'HTMLPurifier/Strategy/FixNesting.php';
-require 'HTMLPurifier/Strategy/MakeWellFormed.php';
-require 'HTMLPurifier/Strategy/RemoveForeignElements.php';
-require 'HTMLPurifier/Strategy/ValidateAttributes.php';
-require 'HTMLPurifier/TagTransform/Font.php';
-require 'HTMLPurifier/TagTransform/Simple.php';
-require 'HTMLPurifier/Token/Comment.php';
-require 'HTMLPurifier/Token/Tag.php';
-require 'HTMLPurifier/Token/Empty.php';
-require 'HTMLPurifier/Token/End.php';
-require 'HTMLPurifier/Token/Start.php';
-require 'HTMLPurifier/Token/Text.php';
-require 'HTMLPurifier/URIFilter/DisableExternal.php';
-require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
-require 'HTMLPurifier/URIFilter/DisableResources.php';
-require 'HTMLPurifier/URIFilter/HostBlacklist.php';
-require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
-require 'HTMLPurifier/URIFilter/Munge.php';
-require 'HTMLPurifier/URIFilter/SafeIframe.php';
-require 'HTMLPurifier/URIScheme/data.php';
-require 'HTMLPurifier/URIScheme/file.php';
-require 'HTMLPurifier/URIScheme/ftp.php';
-require 'HTMLPurifier/URIScheme/http.php';
-require 'HTMLPurifier/URIScheme/https.php';
-require 'HTMLPurifier/URIScheme/mailto.php';
-require 'HTMLPurifier/URIScheme/news.php';
-require 'HTMLPurifier/URIScheme/nntp.php';
-require 'HTMLPurifier/URIScheme/tel.php';
-require 'HTMLPurifier/VarParser/Flexible.php';
-require 'HTMLPurifier/VarParser/Native.php';
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.kses.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.kses.php
deleted file mode 100644
index 7522900..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.kses.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * @file
- * Emulation layer for code that used kses(), substituting in HTML Purifier.
- */
-
-require_once dirname(__FILE__) . '/HTMLPurifier.auto.php';
-
-function kses($string, $allowed_html, $allowed_protocols = null)
-{
-    $config = HTMLPurifier_Config::createDefault();
-    $allowed_elements = array();
-    $allowed_attributes = array();
-    foreach ($allowed_html as $element => $attributes) {
-        $allowed_elements[$element] = true;
-        foreach ($attributes as $attribute => $x) {
-            $allowed_attributes["$element.$attribute"] = true;
-        }
-    }
-    $config->set('HTML.AllowedElements', $allowed_elements);
-    $config->set('HTML.AllowedAttributes', $allowed_attributes);
-    if ($allowed_protocols !== null) {
-        $config->set('URI.AllowedSchemes', $allowed_protocols);
-    }
-    $purifier = new HTMLPurifier($config);
-    return $purifier->purify($string);
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.path.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.path.php
deleted file mode 100644
index 39b1b65..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.path.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * @file
- * Convenience stub file that adds HTML Purifier's library file to the path
- * without any other side-effects.
- */
-
-set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.php
deleted file mode 100644
index 26f0612..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.php
+++ /dev/null
@@ -1,297 +0,0 @@
-<?php
-
-/*! @mainpage
- *
- * HTML Purifier is an HTML filter that will take an arbitrary snippet of
- * HTML and rigorously test, validate and filter it into a version that
- * is safe for output onto webpages. It achieves this by:
- *
- *  -# Lexing (parsing into tokens) the document,
- *  -# Executing various strategies on the tokens:
- *      -# Removing all elements not in the whitelist,
- *      -# Making the tokens well-formed,
- *      -# Fixing the nesting of the nodes, and
- *      -# Validating attributes of the nodes; and
- *  -# Generating HTML from the purified tokens.
- *
- * However, most users will only need to interface with the HTMLPurifier
- * and HTMLPurifier_Config.
- */
-
-/*
-    HTML Purifier 4.15.0 - Standards Compliant HTML Filtering
-    Copyright (C) 2006-2008 Edward Z. Yang
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-/**
- * Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
- *
- * @note There are several points in which configuration can be specified
- *       for HTML Purifier.  The precedence of these (from lowest to
- *       highest) is as follows:
- *          -# Instance: new HTMLPurifier($config)
- *          -# Invocation: purify($html, $config)
- *       These configurations are entirely independent of each other and
- *       are *not* merged (this behavior may change in the future).
- *
- * @todo We need an easier way to inject strategies using the configuration
- *       object.
- */
-class HTMLPurifier
-{
-
-    /**
-     * Version of HTML Purifier.
-     * @type string
-     */
-    public $version = '4.15.0';
-
-    /**
-     * Constant with version of HTML Purifier.
-     */
-    const VERSION = '4.15.0';
-
-    /**
-     * Global configuration object.
-     * @type HTMLPurifier_Config
-     */
-    public $config;
-
-    /**
-     * Array of extra filter objects to run on HTML,
-     * for backwards compatibility.
-     * @type HTMLPurifier_Filter[]
-     */
-    private $filters = array();
-
-    /**
-     * Single instance of HTML Purifier.
-     * @type HTMLPurifier
-     */
-    private static $instance;
-
-    /**
-     * @type HTMLPurifier_Strategy_Core
-     */
-    protected $strategy;
-
-    /**
-     * @type HTMLPurifier_Generator
-     */
-    protected $generator;
-
-    /**
-     * Resultant context of last run purification.
-     * Is an array of contexts if the last called method was purifyArray().
-     * @type HTMLPurifier_Context
-     */
-    public $context;
-
-    /**
-     * Initializes the purifier.
-     *
-     * @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object
-     *                for all instances of the purifier, if omitted, a default
-     *                configuration is supplied (which can be overridden on a
-     *                per-use basis).
-     *                The parameter can also be any type that
-     *                HTMLPurifier_Config::create() supports.
-     */
-    public function __construct($config = null)
-    {
-        $this->config = HTMLPurifier_Config::create($config);
-        $this->strategy = new HTMLPurifier_Strategy_Core();
-    }
-
-    /**
-     * Adds a filter to process the output. First come first serve
-     *
-     * @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object
-     */
-    public function addFilter($filter)
-    {
-        trigger_error(
-            'HTMLPurifier->addFilter() is deprecated, use configuration directives' .
-            ' in the Filter namespace or Filter.Custom',
-            E_USER_WARNING
-        );
-        $this->filters[] = $filter;
-    }
-
-    /**
-     * Filters an HTML snippet/document to be XSS-free and standards-compliant.
-     *
-     * @param string $html String of HTML to purify
-     * @param HTMLPurifier_Config $config Config object for this operation,
-     *                if omitted, defaults to the config object specified during this
-     *                object's construction. The parameter can also be any type
-     *                that HTMLPurifier_Config::create() supports.
-     *
-     * @return string Purified HTML
-     */
-    public function purify($html, $config = null)
-    {
-        // :TODO: make the config merge in, instead of replace
-        $config = $config ? HTMLPurifier_Config::create($config) : $this->config;
-
-        // implementation is partially environment dependant, partially
-        // configuration dependant
-        $lexer = HTMLPurifier_Lexer::create($config);
-
-        $context = new HTMLPurifier_Context();
-
-        // setup HTML generator
-        $this->generator = new HTMLPurifier_Generator($config, $context);
-        $context->register('Generator', $this->generator);
-
-        // set up global context variables
-        if ($config->get('Core.CollectErrors')) {
-            // may get moved out if other facilities use it
-            $language_factory = HTMLPurifier_LanguageFactory::instance();
-            $language = $language_factory->create($config, $context);
-            $context->register('Locale', $language);
-
-            $error_collector = new HTMLPurifier_ErrorCollector($context);
-            $context->register('ErrorCollector', $error_collector);
-        }
-
-        // setup id_accumulator context, necessary due to the fact that
-        // AttrValidator can be called from many places
-        $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
-        $context->register('IDAccumulator', $id_accumulator);
-
-        $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
-
-        // setup filters
-        $filter_flags = $config->getBatch('Filter');
-        $custom_filters = $filter_flags['Custom'];
-        unset($filter_flags['Custom']);
-        $filters = array();
-        foreach ($filter_flags as $filter => $flag) {
-            if (!$flag) {
-                continue;
-            }
-            if (strpos($filter, '.') !== false) {
-                continue;
-            }
-            $class = "HTMLPurifier_Filter_$filter";
-            $filters[] = new $class;
-        }
-        foreach ($custom_filters as $filter) {
-            // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
-            $filters[] = $filter;
-        }
-        $filters = array_merge($filters, $this->filters);
-        // maybe prepare(), but later
-
-        for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
-            $html = $filters[$i]->preFilter($html, $config, $context);
-        }
-
-        // purified HTML
-        $html =
-            $this->generator->generateFromTokens(
-                // list of tokens
-                $this->strategy->execute(
-                    // list of un-purified tokens
-                    $lexer->tokenizeHTML(
-                        // un-purified HTML
-                        $html,
-                        $config,
-                        $context
-                    ),
-                    $config,
-                    $context
-                )
-            );
-
-        for ($i = $filter_size - 1; $i >= 0; $i--) {
-            $html = $filters[$i]->postFilter($html, $config, $context);
-        }
-
-        $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
-        $this->context =& $context;
-        return $html;
-    }
-
-    /**
-     * Filters an array of HTML snippets
-     *
-     * @param string[] $array_of_html Array of html snippets
-     * @param HTMLPurifier_Config $config Optional config object for this operation.
-     *                See HTMLPurifier::purify() for more details.
-     *
-     * @return string[] Array of purified HTML
-     */
-    public function purifyArray($array_of_html, $config = null)
-    {
-        $context_array = array();
-        $array = array();
-        foreach($array_of_html as $key=>$value){
-            if (is_array($value)) {
-                $array[$key] = $this->purifyArray($value, $config);
-            } else {
-                $array[$key] = $this->purify($value, $config);
-            }
-            $context_array[$key] = $this->context;
-        }
-        $this->context = $context_array;
-        return $array;
-    }
-
-    /**
-     * Singleton for enforcing just one HTML Purifier in your system
-     *
-     * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
-     *                   HTMLPurifier instance to overload singleton with,
-     *                   or HTMLPurifier_Config instance to configure the
-     *                   generated version with.
-     *
-     * @return HTMLPurifier
-     */
-    public static function instance($prototype = null)
-    {
-        if (!self::$instance || $prototype) {
-            if ($prototype instanceof HTMLPurifier) {
-                self::$instance = $prototype;
-            } elseif ($prototype) {
-                self::$instance = new HTMLPurifier($prototype);
-            } else {
-                self::$instance = new HTMLPurifier();
-            }
-        }
-        return self::$instance;
-    }
-
-    /**
-     * Singleton for enforcing just one HTML Purifier in your system
-     *
-     * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
-     *                   HTMLPurifier instance to overload singleton with,
-     *                   or HTMLPurifier_Config instance to configure the
-     *                   generated version with.
-     *
-     * @return HTMLPurifier
-     * @note Backwards compatibility, see instance()
-     */
-    public static function getInstance($prototype = null)
-    {
-        return HTMLPurifier::instance($prototype);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php
deleted file mode 100644
index 94543f5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier.safe-includes.php
+++ /dev/null
@@ -1,229 +0,0 @@
-<?php
-
-/**
- * @file
- * This file was auto-generated by generate-includes.php and includes all of
- * the core files required by HTML Purifier. This is a convenience stub that
- * includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT
- * EDIT THIS FILE, changes will be overwritten the next time the script is run.
- *
- * Changes to include_path are not necessary.
- */
-
-$__dir = dirname(__FILE__);
-
-require_once $__dir . '/HTMLPurifier.php';
-require_once $__dir . '/HTMLPurifier/Arborize.php';
-require_once $__dir . '/HTMLPurifier/AttrCollections.php';
-require_once $__dir . '/HTMLPurifier/AttrDef.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform.php';
-require_once $__dir . '/HTMLPurifier/AttrTypes.php';
-require_once $__dir . '/HTMLPurifier/AttrValidator.php';
-require_once $__dir . '/HTMLPurifier/Bootstrap.php';
-require_once $__dir . '/HTMLPurifier/Definition.php';
-require_once $__dir . '/HTMLPurifier/CSSDefinition.php';
-require_once $__dir . '/HTMLPurifier/ChildDef.php';
-require_once $__dir . '/HTMLPurifier/Config.php';
-require_once $__dir . '/HTMLPurifier/ConfigSchema.php';
-require_once $__dir . '/HTMLPurifier/ContentSets.php';
-require_once $__dir . '/HTMLPurifier/Context.php';
-require_once $__dir . '/HTMLPurifier/DefinitionCache.php';
-require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php';
-require_once $__dir . '/HTMLPurifier/Doctype.php';
-require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php';
-require_once $__dir . '/HTMLPurifier/ElementDef.php';
-require_once $__dir . '/HTMLPurifier/Encoder.php';
-require_once $__dir . '/HTMLPurifier/EntityLookup.php';
-require_once $__dir . '/HTMLPurifier/EntityParser.php';
-require_once $__dir . '/HTMLPurifier/ErrorCollector.php';
-require_once $__dir . '/HTMLPurifier/ErrorStruct.php';
-require_once $__dir . '/HTMLPurifier/Exception.php';
-require_once $__dir . '/HTMLPurifier/Filter.php';
-require_once $__dir . '/HTMLPurifier/Generator.php';
-require_once $__dir . '/HTMLPurifier/HTMLDefinition.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule.php';
-require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php';
-require_once $__dir . '/HTMLPurifier/IDAccumulator.php';
-require_once $__dir . '/HTMLPurifier/Injector.php';
-require_once $__dir . '/HTMLPurifier/Language.php';
-require_once $__dir . '/HTMLPurifier/LanguageFactory.php';
-require_once $__dir . '/HTMLPurifier/Length.php';
-require_once $__dir . '/HTMLPurifier/Lexer.php';
-require_once $__dir . '/HTMLPurifier/Node.php';
-require_once $__dir . '/HTMLPurifier/PercentEncoder.php';
-require_once $__dir . '/HTMLPurifier/PropertyList.php';
-require_once $__dir . '/HTMLPurifier/PropertyListIterator.php';
-require_once $__dir . '/HTMLPurifier/Queue.php';
-require_once $__dir . '/HTMLPurifier/Strategy.php';
-require_once $__dir . '/HTMLPurifier/StringHash.php';
-require_once $__dir . '/HTMLPurifier/StringHashParser.php';
-require_once $__dir . '/HTMLPurifier/TagTransform.php';
-require_once $__dir . '/HTMLPurifier/Token.php';
-require_once $__dir . '/HTMLPurifier/TokenFactory.php';
-require_once $__dir . '/HTMLPurifier/URI.php';
-require_once $__dir . '/HTMLPurifier/URIDefinition.php';
-require_once $__dir . '/HTMLPurifier/URIFilter.php';
-require_once $__dir . '/HTMLPurifier/URIParser.php';
-require_once $__dir . '/HTMLPurifier/URIScheme.php';
-require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php';
-require_once $__dir . '/HTMLPurifier/UnitConverter.php';
-require_once $__dir . '/HTMLPurifier/VarParser.php';
-require_once $__dir . '/HTMLPurifier/VarParserException.php';
-require_once $__dir . '/HTMLPurifier/Zipper.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/Clone.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/Text.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/URI.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Ident.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ContentEditable.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php';
-require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Background.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Nofollow.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php';
-require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/List.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/Required.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php';
-require_once $__dir . '/HTMLPurifier/ChildDef/Table.php';
-require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php';
-require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php';
-require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php';
-require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
-require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Iframe.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/List.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Nofollow.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/SafeScripting.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Name.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php';
-require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php';
-require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php';
-require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php';
-require_once $__dir . '/HTMLPurifier/Injector/Linkify.php';
-require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php';
-require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php';
-require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
-require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php';
-require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php';
-require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php';
-require_once $__dir . '/HTMLPurifier/Node/Comment.php';
-require_once $__dir . '/HTMLPurifier/Node/Element.php';
-require_once $__dir . '/HTMLPurifier/Node/Text.php';
-require_once $__dir . '/HTMLPurifier/Strategy/Composite.php';
-require_once $__dir . '/HTMLPurifier/Strategy/Core.php';
-require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php';
-require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php';
-require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php';
-require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php';
-require_once $__dir . '/HTMLPurifier/TagTransform/Font.php';
-require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php';
-require_once $__dir . '/HTMLPurifier/Token/Comment.php';
-require_once $__dir . '/HTMLPurifier/Token/Tag.php';
-require_once $__dir . '/HTMLPurifier/Token/Empty.php';
-require_once $__dir . '/HTMLPurifier/Token/End.php';
-require_once $__dir . '/HTMLPurifier/Token/Start.php';
-require_once $__dir . '/HTMLPurifier/Token/Text.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/SafeIframe.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/data.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/file.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
-require_once $__dir . '/HTMLPurifier/URIScheme/tel.php';
-require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
-require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php
deleted file mode 100644
index d2e9d22..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Arborize.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * Converts a stream of HTMLPurifier_Token into an HTMLPurifier_Node,
- * and back again.
- *
- * @note This transformation is not an equivalence.  We mutate the input
- * token stream to make it so; see all [MUT] markers in code.
- */
-class HTMLPurifier_Arborize
-{
-    public static function arborize($tokens, $config, $context) {
-        $definition = $config->getHTMLDefinition();
-        $parent = new HTMLPurifier_Token_Start($definition->info_parent);
-        $stack = array($parent->toNode());
-        foreach ($tokens as $token) {
-            $token->skip = null; // [MUT]
-            $token->carryover = null; // [MUT]
-            if ($token instanceof HTMLPurifier_Token_End) {
-                $token->start = null; // [MUT]
-                $r = array_pop($stack);
-                //assert($r->name === $token->name);
-                //assert(empty($token->attr));
-                $r->endCol = $token->col;
-                $r->endLine = $token->line;
-                $r->endArmor = $token->armor;
-                continue;
-            }
-            $node = $token->toNode();
-            $stack[count($stack)-1]->children[] = $node;
-            if ($token instanceof HTMLPurifier_Token_Start) {
-                $stack[] = $node;
-            }
-        }
-        //assert(count($stack) == 1);
-        return $stack[0];
-    }
-
-    public static function flatten($node, $config, $context) {
-        $level = 0;
-        $nodes = array($level => new HTMLPurifier_Queue(array($node)));
-        $closingTokens = array();
-        $tokens = array();
-        do {
-            while (!$nodes[$level]->isEmpty()) {
-                $node = $nodes[$level]->shift(); // FIFO
-                list($start, $end) = $node->toTokenPair();
-                if ($level > 0) {
-                    $tokens[] = $start;
-                }
-                if ($end !== NULL) {
-                    $closingTokens[$level][] = $end;
-                }
-                if ($node instanceof HTMLPurifier_Node_Element) {
-                    $level++;
-                    $nodes[$level] = new HTMLPurifier_Queue();
-                    foreach ($node->children as $childNode) {
-                        $nodes[$level]->push($childNode);
-                    }
-                }
-            }
-            $level--;
-            if ($level && isset($closingTokens[$level])) {
-                while ($token = array_pop($closingTokens[$level])) {
-                    $tokens[] = $token;
-                }
-            }
-        } while ($level > 0);
-        return $tokens;
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php
deleted file mode 100644
index c7b17cf..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrCollections.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-
-/**
- * Defines common attribute collections that modules reference
- */
-
-class HTMLPurifier_AttrCollections
-{
-
-    /**
-     * Associative array of attribute collections, indexed by name.
-     * @type array
-     */
-    public $info = array();
-
-    /**
-     * Performs all expansions on internal data for use by other inclusions
-     * It also collects all attribute collection extensions from
-     * modules
-     * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
-     * @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
-     */
-    public function __construct($attr_types, $modules)
-    {
-        $this->doConstruct($attr_types, $modules);
-    }
-
-    public function doConstruct($attr_types, $modules)
-    {
-        // load extensions from the modules
-        foreach ($modules as $module) {
-            foreach ($module->attr_collections as $coll_i => $coll) {
-                if (!isset($this->info[$coll_i])) {
-                    $this->info[$coll_i] = array();
-                }
-                foreach ($coll as $attr_i => $attr) {
-                    if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
-                        // merge in includes
-                        $this->info[$coll_i][$attr_i] = array_merge(
-                            $this->info[$coll_i][$attr_i],
-                            $attr
-                        );
-                        continue;
-                    }
-                    $this->info[$coll_i][$attr_i] = $attr;
-                }
-            }
-        }
-        // perform internal expansions and inclusions
-        foreach ($this->info as $name => $attr) {
-            // merge attribute collections that include others
-            $this->performInclusions($this->info[$name]);
-            // replace string identifiers with actual attribute objects
-            $this->expandIdentifiers($this->info[$name], $attr_types);
-        }
-    }
-
-    /**
-     * Takes a reference to an attribute associative array and performs
-     * all inclusions specified by the zero index.
-     * @param array &$attr Reference to attribute array
-     */
-    public function performInclusions(&$attr)
-    {
-        if (!isset($attr[0])) {
-            return;
-        }
-        $merge = $attr[0];
-        $seen  = array(); // recursion guard
-        // loop through all the inclusions
-        for ($i = 0; isset($merge[$i]); $i++) {
-            if (isset($seen[$merge[$i]])) {
-                continue;
-            }
-            $seen[$merge[$i]] = true;
-            // foreach attribute of the inclusion, copy it over
-            if (!isset($this->info[$merge[$i]])) {
-                continue;
-            }
-            foreach ($this->info[$merge[$i]] as $key => $value) {
-                if (isset($attr[$key])) {
-                    continue;
-                } // also catches more inclusions
-                $attr[$key] = $value;
-            }
-            if (isset($this->info[$merge[$i]][0])) {
-                // recursion
-                $merge = array_merge($merge, $this->info[$merge[$i]][0]);
-            }
-        }
-        unset($attr[0]);
-    }
-
-    /**
-     * Expands all string identifiers in an attribute array by replacing
-     * them with the appropriate values inside HTMLPurifier_AttrTypes
-     * @param array &$attr Reference to attribute array
-     * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
-     */
-    public function expandIdentifiers(&$attr, $attr_types)
-    {
-        // because foreach will process new elements we add, make sure we
-        // skip duplicates
-        $processed = array();
-
-        foreach ($attr as $def_i => $def) {
-            // skip inclusions
-            if ($def_i === 0) {
-                continue;
-            }
-
-            if (isset($processed[$def_i])) {
-                continue;
-            }
-
-            // determine whether or not attribute is required
-            if ($required = (strpos($def_i, '*') !== false)) {
-                // rename the definition
-                unset($attr[$def_i]);
-                $def_i = trim($def_i, '*');
-                $attr[$def_i] = $def;
-            }
-
-            $processed[$def_i] = true;
-
-            // if we've already got a literal object, move on
-            if (is_object($def)) {
-                // preserve previous required
-                $attr[$def_i]->required = ($required || $attr[$def_i]->required);
-                continue;
-            }
-
-            if ($def === false) {
-                unset($attr[$def_i]);
-                continue;
-            }
-
-            if ($t = $attr_types->get($def)) {
-                $attr[$def_i] = $t;
-                $attr[$def_i]->required = $required;
-            } else {
-                unset($attr[$def_i]);
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php
deleted file mode 100644
index 739646f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef.php
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-
-/**
- * Base class for all validating attribute definitions.
- *
- * This family of classes forms the core for not only HTML attribute validation,
- * but also any sort of string that needs to be validated or cleaned (which
- * means CSS properties and composite definitions are defined here too).
- * Besides defining (through code) what precisely makes the string valid,
- * subclasses are also responsible for cleaning the code if possible.
- */
-
-abstract class HTMLPurifier_AttrDef
-{
-
-    /**
-     * Tells us whether or not an HTML attribute is minimized.
-     * Has no meaning in other contexts.
-     * @type bool
-     */
-    public $minimized = false;
-
-    /**
-     * Tells us whether or not an HTML attribute is required.
-     * Has no meaning in other contexts
-     * @type bool
-     */
-    public $required = false;
-
-    /**
-     * Validates and cleans passed string according to a definition.
-     *
-     * @param string $string String to be validated and cleaned.
-     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
-     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object.
-     */
-    abstract public function validate($string, $config, $context);
-
-    /**
-     * Convenience method that parses a string as if it were CDATA.
-     *
-     * This method process a string in the manner specified at
-     * <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
-     * leading and trailing whitespace, ignoring line feeds, and replacing
-     * carriage returns and tabs with spaces.  While most useful for HTML
-     * attributes specified as CDATA, it can also be applied to most CSS
-     * values.
-     *
-     * @note This method is not entirely standards compliant, as trim() removes
-     *       more types of whitespace than specified in the spec. In practice,
-     *       this is rarely a problem, as those extra characters usually have
-     *       already been removed by HTMLPurifier_Encoder.
-     *
-     * @warning This processing is inconsistent with XML's whitespace handling
-     *          as specified by section 3.3.3 and referenced XHTML 1.0 section
-     *          4.7.  However, note that we are NOT necessarily
-     *          parsing XML, thus, this behavior may still be correct. We
-     *          assume that newlines have been normalized.
-     */
-    public function parseCDATA($string)
-    {
-        $string = trim($string);
-        $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
-        return $string;
-    }
-
-    /**
-     * Factory method for creating this class from a string.
-     * @param string $string String construction info
-     * @return HTMLPurifier_AttrDef Created AttrDef object corresponding to $string
-     */
-    public function make($string)
-    {
-        // default implementation, return a flyweight of this object.
-        // If $string has an effect on the returned object (i.e. you
-        // need to overload this method), it is best
-        // to clone or instantiate new copies. (Instantiation is safer.)
-        return $this;
-    }
-
-    /**
-     * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
-     * properly. THIS IS A HACK!
-     * @param string $string a CSS colour definition
-     * @return string
-     */
-    protected function mungeRgb($string)
-    {
-        $p = '\s*(\d+(\.\d+)?([%]?))\s*';
-
-        if (preg_match('/(rgba|hsla)\(/', $string)) {
-            return preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8,\11)', $string);
-        }
-
-        return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8)', $string);
-    }
-
-    /**
-     * Parses a possibly escaped CSS string and returns the "pure"
-     * version of it.
-     */
-    protected function expandCSSEscape($string)
-    {
-        // flexibly parse it
-        $ret = '';
-        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
-            if ($string[$i] === '\\') {
-                $i++;
-                if ($i >= $c) {
-                    $ret .= '\\';
-                    break;
-                }
-                if (ctype_xdigit($string[$i])) {
-                    $code = $string[$i];
-                    for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
-                        if (!ctype_xdigit($string[$i])) {
-                            break;
-                        }
-                        $code .= $string[$i];
-                    }
-                    // We have to be extremely careful when adding
-                    // new characters, to make sure we're not breaking
-                    // the encoding.
-                    $char = HTMLPurifier_Encoder::unichr(hexdec($code));
-                    if (HTMLPurifier_Encoder::cleanUTF8($char) === '') {
-                        continue;
-                    }
-                    $ret .= $char;
-                    if ($i < $c && trim($string[$i]) !== '') {
-                        $i--;
-                    }
-                    continue;
-                }
-                if ($string[$i] === "\n") {
-                    continue;
-                }
-            }
-            $ret .= $string[$i];
-        }
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php
deleted file mode 100644
index ad2cb90..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * Validates the HTML attribute style, otherwise known as CSS.
- * @note We don't implement the whole CSS specification, so it might be
- *       difficult to reuse this component in the context of validating
- *       actual stylesheet declarations.
- * @note If we were really serious about validating the CSS, we would
- *       tokenize the styles and then parse the tokens. Obviously, we
- *       are not doing that. Doing that could seriously harm performance,
- *       but would make these components a lot more viable for a CSS
- *       filtering solution.
- */
-class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @param string $css
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($css, $config, $context)
-    {
-        $css = $this->parseCDATA($css);
-
-        $definition = $config->getCSSDefinition();
-        $allow_duplicates = $config->get("CSS.AllowDuplicates");
-
-
-        // According to the CSS2.1 spec, the places where a
-        // non-delimiting semicolon can appear are in strings
-        // escape sequences.   So here is some dumb hack to
-        // handle quotes.
-        $len = strlen($css);
-        $accum = "";
-        $declarations = array();
-        $quoted = false;
-        for ($i = 0; $i < $len; $i++) {
-            $c = strcspn($css, ";'\"", $i);
-            $accum .= substr($css, $i, $c);
-            $i += $c;
-            if ($i == $len) break;
-            $d = $css[$i];
-            if ($quoted) {
-                $accum .= $d;
-                if ($d == $quoted) {
-                    $quoted = false;
-                }
-            } else {
-                if ($d == ";") {
-                    $declarations[] = $accum;
-                    $accum = "";
-                } else {
-                    $accum .= $d;
-                    $quoted = $d;
-                }
-            }
-        }
-        if ($accum != "") $declarations[] = $accum;
-
-        $propvalues = array();
-        $new_declarations = '';
-
-        /**
-         * Name of the current CSS property being validated.
-         */
-        $property = false;
-        $context->register('CurrentCSSProperty', $property);
-
-        foreach ($declarations as $declaration) {
-            if (!$declaration) {
-                continue;
-            }
-            if (!strpos($declaration, ':')) {
-                continue;
-            }
-            list($property, $value) = explode(':', $declaration, 2);
-            $property = trim($property);
-            $value = trim($value);
-            $ok = false;
-            do {
-                if (isset($definition->info[$property])) {
-                    $ok = true;
-                    break;
-                }
-                if (ctype_lower($property)) {
-                    break;
-                }
-                $property = strtolower($property);
-                if (isset($definition->info[$property])) {
-                    $ok = true;
-                    break;
-                }
-            } while (0);
-            if (!$ok) {
-                continue;
-            }
-            // inefficient call, since the validator will do this again
-            if (strtolower(trim($value)) !== 'inherit') {
-                // inherit works for everything (but only on the base property)
-                $result = $definition->info[$property]->validate(
-                    $value,
-                    $config,
-                    $context
-                );
-            } else {
-                $result = 'inherit';
-            }
-            if ($result === false) {
-                continue;
-            }
-            if ($allow_duplicates) {
-                $new_declarations .= "$property:$result;";
-            } else {
-                $propvalues[$property] = $result;
-            }
-        }
-
-        $context->destroy('CurrentCSSProperty');
-
-        // procedure does not write the new CSS simultaneously, so it's
-        // slightly inefficient, but it's the only way of getting rid of
-        // duplicates. Perhaps config to optimize it, but not now.
-
-        foreach ($propvalues as $prop => $value) {
-            $new_declarations .= "$prop:$value;";
-        }
-
-        return $new_declarations ? $new_declarations : false;
-
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php
deleted file mode 100644
index af2b83d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number
-{
-
-    public function __construct()
-    {
-        parent::__construct(false); // opacity is non-negative, but we will clamp it
-    }
-
-    /**
-     * @param string $number
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public function validate($number, $config, $context)
-    {
-        $result = parent::validate($number, $config, $context);
-        if ($result === false) {
-            return $result;
-        }
-        $float = (float)$result;
-        if ($float < 0.0) {
-            $result = '0';
-        }
-        if ($float > 1.0) {
-            $result = '1';
-        }
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php
deleted file mode 100644
index 28c4988..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Background.php
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-/**
- * Validates shorthand CSS property background.
- * @warning Does not support url tokens that have internal spaces.
- */
-class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of component validators.
-     * @type HTMLPurifier_AttrDef[]
-     * @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
-     */
-    protected $info;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function __construct($config)
-    {
-        $def = $config->getCSSDefinition();
-        $this->info['background-color'] = $def->info['background-color'];
-        $this->info['background-image'] = $def->info['background-image'];
-        $this->info['background-repeat'] = $def->info['background-repeat'];
-        $this->info['background-attachment'] = $def->info['background-attachment'];
-        $this->info['background-position'] = $def->info['background-position'];
-        $this->info['background-size'] = $def->info['background-size'];
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        // regular pre-processing
-        $string = $this->parseCDATA($string);
-        if ($string === '') {
-            return false;
-        }
-
-        // munge rgb() decl if necessary
-        $string = $this->mungeRgb($string);
-
-        // assumes URI doesn't have spaces in it
-        $bits = explode(' ', $string); // bits to process
-
-        $caught = array();
-        $caught['color'] = false;
-        $caught['image'] = false;
-        $caught['repeat'] = false;
-        $caught['attachment'] = false;
-        $caught['position'] = false;
-        $caught['size'] = false;
-
-        $i = 0; // number of catches
-
-        foreach ($bits as $bit) {
-            if ($bit === '') {
-                continue;
-            }
-            foreach ($caught as $key => $status) {
-                if ($key != 'position') {
-                    if ($status !== false) {
-                        continue;
-                    }
-                    $r = $this->info['background-' . $key]->validate($bit, $config, $context);
-                } else {
-                    $r = $bit;
-                }
-                if ($r === false) {
-                    continue;
-                }
-                if ($key == 'position') {
-                    if ($caught[$key] === false) {
-                        $caught[$key] = '';
-                    }
-                    $caught[$key] .= $r . ' ';
-                } else {
-                    $caught[$key] = $r;
-                }
-                $i++;
-                break;
-            }
-        }
-
-        if (!$i) {
-            return false;
-        }
-        if ($caught['position'] !== false) {
-            $caught['position'] = $this->info['background-position']->
-                validate($caught['position'], $config, $context);
-        }
-
-        $ret = array();
-        foreach ($caught as $value) {
-            if ($value === false) {
-                continue;
-            }
-            $ret[] = $value;
-        }
-
-        if (empty($ret)) {
-            return false;
-        }
-        return implode(' ', $ret);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php
deleted file mode 100644
index 4580ef5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php
+++ /dev/null
@@ -1,157 +0,0 @@
-<?php
-
-/* W3C says:
-    [ // adjective and number must be in correct order, even if
-      // you could switch them without introducing ambiguity.
-      // some browsers support that syntax
-        [
-            <percentage> | <length> | left | center | right
-        ]
-        [
-            <percentage> | <length> | top | center | bottom
-        ]?
-    ] |
-    [ // this signifies that the vertical and horizontal adjectives
-      // can be arbitrarily ordered, however, there can only be two,
-      // one of each, or none at all
-        [
-            left | center | right
-        ] ||
-        [
-            top | center | bottom
-        ]
-    ]
-    top, left = 0%
-    center, (none) = 50%
-    bottom, right = 100%
-*/
-
-/* QuirksMode says:
-    keyword + length/percentage must be ordered correctly, as per W3C
-
-    Internet Explorer and Opera, however, support arbitrary ordering. We
-    should fix it up.
-
-    Minor issue though, not strictly necessary.
-*/
-
-// control freaks may appreciate the ability to convert these to
-// percentages or something, but it's not necessary
-
-/**
- * Validates the value of background-position.
- */
-class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @type HTMLPurifier_AttrDef_CSS_Length
-     */
-    protected $length;
-
-    /**
-     * @type HTMLPurifier_AttrDef_CSS_Percentage
-     */
-    protected $percentage;
-
-    public function __construct()
-    {
-        $this->length = new HTMLPurifier_AttrDef_CSS_Length();
-        $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = $this->parseCDATA($string);
-        $bits = explode(' ', $string);
-
-        $keywords = array();
-        $keywords['h'] = false; // left, right
-        $keywords['v'] = false; // top, bottom
-        $keywords['ch'] = false; // center (first word)
-        $keywords['cv'] = false; // center (second word)
-        $measures = array();
-
-        $i = 0;
-
-        $lookup = array(
-            'top' => 'v',
-            'bottom' => 'v',
-            'left' => 'h',
-            'right' => 'h',
-            'center' => 'c'
-        );
-
-        foreach ($bits as $bit) {
-            if ($bit === '') {
-                continue;
-            }
-
-            // test for keyword
-            $lbit = ctype_lower($bit) ? $bit : strtolower($bit);
-            if (isset($lookup[$lbit])) {
-                $status = $lookup[$lbit];
-                if ($status == 'c') {
-                    if ($i == 0) {
-                        $status = 'ch';
-                    } else {
-                        $status = 'cv';
-                    }
-                }
-                $keywords[$status] = $lbit;
-                $i++;
-            }
-
-            // test for length
-            $r = $this->length->validate($bit, $config, $context);
-            if ($r !== false) {
-                $measures[] = $r;
-                $i++;
-            }
-
-            // test for percentage
-            $r = $this->percentage->validate($bit, $config, $context);
-            if ($r !== false) {
-                $measures[] = $r;
-                $i++;
-            }
-        }
-
-        if (!$i) {
-            return false;
-        } // no valid values were caught
-
-        $ret = array();
-
-        // first keyword
-        if ($keywords['h']) {
-            $ret[] = $keywords['h'];
-        } elseif ($keywords['ch']) {
-            $ret[] = $keywords['ch'];
-            $keywords['cv'] = false; // prevent re-use: center = center center
-        } elseif (count($measures)) {
-            $ret[] = array_shift($measures);
-        }
-
-        if ($keywords['v']) {
-            $ret[] = $keywords['v'];
-        } elseif ($keywords['cv']) {
-            $ret[] = $keywords['cv'];
-        } elseif (count($measures)) {
-            $ret[] = array_shift($measures);
-        }
-
-        if (empty($ret)) {
-            return false;
-        }
-        return implode(' ', $ret);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php
deleted file mode 100644
index 16243ba..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Border.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Validates the border property as defined by CSS.
- */
-class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of properties this property is shorthand for.
-     * @type HTMLPurifier_AttrDef[]
-     */
-    protected $info = array();
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function __construct($config)
-    {
-        $def = $config->getCSSDefinition();
-        $this->info['border-width'] = $def->info['border-width'];
-        $this->info['border-style'] = $def->info['border-style'];
-        $this->info['border-top-color'] = $def->info['border-top-color'];
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = $this->parseCDATA($string);
-        $string = $this->mungeRgb($string);
-        $bits = explode(' ', $string);
-        $done = array(); // segments we've finished
-        $ret = ''; // return value
-        foreach ($bits as $bit) {
-            foreach ($this->info as $propname => $validator) {
-                if (isset($done[$propname])) {
-                    continue;
-                }
-                $r = $validator->validate($bit, $config, $context);
-                if ($r !== false) {
-                    $ret .= $r . ' ';
-                    $done[$propname] = true;
-                    break;
-                }
-            }
-        }
-        return rtrim($ret);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php
deleted file mode 100644
index d7287a0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Color.php
+++ /dev/null
@@ -1,161 +0,0 @@
-<?php
-
-/**
- * Validates Color as defined by CSS.
- */
-class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @type HTMLPurifier_AttrDef_CSS_AlphaValue
-     */
-    protected $alpha;
-
-    public function __construct()
-    {
-        $this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
-    }
-
-    /**
-     * @param string $color
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($color, $config, $context)
-    {
-        static $colors = null;
-        if ($colors === null) {
-            $colors = $config->get('Core.ColorKeywords');
-        }
-
-        $color = trim($color);
-        if ($color === '') {
-            return false;
-        }
-
-        $lower = strtolower($color);
-        if (isset($colors[$lower])) {
-            return $colors[$lower];
-        }
-
-        if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) {
-            $length = strlen($color);
-            if (strpos($color, ')') !== $length - 1) {
-                return false;
-            }
-
-            // get used function : rgb, rgba, hsl or hsla
-            $function = $matches[1];
-
-            $parameters_size = 3;
-            $alpha_channel = false;
-            if (substr($function, -1) === 'a') {
-                $parameters_size = 4;
-                $alpha_channel = true;
-            }
-
-            /*
-             * Allowed types for values :
-             * parameter_position => [type => max_value]
-             */
-            $allowed_types = array(
-                1 => array('percentage' => 100, 'integer' => 255),
-                2 => array('percentage' => 100, 'integer' => 255),
-                3 => array('percentage' => 100, 'integer' => 255),
-            );
-            $allow_different_types = false;
-
-            if (strpos($function, 'hsl') !== false) {
-                $allowed_types = array(
-                    1 => array('integer' => 360),
-                    2 => array('percentage' => 100),
-                    3 => array('percentage' => 100),
-                );
-                $allow_different_types = true;
-            }
-
-            $values = trim(str_replace($function, '', $color), ' ()');
-
-            $parts = explode(',', $values);
-            if (count($parts) !== $parameters_size) {
-                return false;
-            }
-
-            $type = false;
-            $new_parts = array();
-            $i = 0;
-
-            foreach ($parts as $part) {
-                $i++;
-                $part = trim($part);
-
-                if ($part === '') {
-                    return false;
-                }
-
-                // different check for alpha channel
-                if ($alpha_channel === true && $i === count($parts)) {
-                    $result = $this->alpha->validate($part, $config, $context);
-
-                    if ($result === false) {
-                        return false;
-                    }
-
-                    $new_parts[] = (string)$result;
-                    continue;
-                }
-
-                if (substr($part, -1) === '%') {
-                    $current_type = 'percentage';
-                } else {
-                    $current_type = 'integer';
-                }
-
-                if (!array_key_exists($current_type, $allowed_types[$i])) {
-                    return false;
-                }
-
-                if (!$type) {
-                    $type = $current_type;
-                }
-
-                if ($allow_different_types === false && $type != $current_type) {
-                    return false;
-                }
-
-                $max_value = $allowed_types[$i][$current_type];
-
-                if ($current_type == 'integer') {
-                    // Return value between range 0 -> $max_value
-                    $new_parts[] = (int)max(min($part, $max_value), 0);
-                } elseif ($current_type == 'percentage') {
-                    $new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%';
-                }
-            }
-
-            $new_values = implode(',', $new_parts);
-
-            $color = $function . '(' . $new_values . ')';
-        } else {
-            // hexadecimal handling
-            if ($color[0] === '#') {
-                $hex = substr($color, 1);
-            } else {
-                $hex = $color;
-                $color = '#' . $color;
-            }
-            $length = strlen($hex);
-            if ($length !== 3 && $length !== 6) {
-                return false;
-            }
-            if (!ctype_xdigit($hex)) {
-                return false;
-            }
-        }
-        return $color;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php
deleted file mode 100644
index 9c17505..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Composite.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Allows multiple validators to attempt to validate attribute.
- *
- * Composite is just what it sounds like: a composite of many validators.
- * This means that multiple HTMLPurifier_AttrDef objects will have a whack
- * at the string.  If one of them passes, that's what is returned.  This is
- * especially useful for CSS values, which often are a choice between
- * an enumerated set of predefined values or a flexible data type.
- */
-class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * List of objects that may process strings.
-     * @type HTMLPurifier_AttrDef[]
-     * @todo Make protected
-     */
-    public $defs;
-
-    /**
-     * @param HTMLPurifier_AttrDef[] $defs List of HTMLPurifier_AttrDef objects
-     */
-    public function __construct($defs)
-    {
-        $this->defs = $defs;
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        foreach ($this->defs as $i => $def) {
-            $result = $this->defs[$i]->validate($string, $config, $context);
-            if ($result !== false) {
-                return $result;
-            }
-        }
-        return false;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php
deleted file mode 100644
index 9d77cc9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Decorator which enables CSS properties to be disabled for specific elements.
- */
-class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
-{
-    /**
-     * @type HTMLPurifier_AttrDef
-     */
-    public $def;
-    /**
-     * @type string
-     */
-    public $element;
-
-    /**
-     * @param HTMLPurifier_AttrDef $def Definition to wrap
-     * @param string $element Element to deny
-     */
-    public function __construct($def, $element)
-    {
-        $this->def = $def;
-        $this->element = $element;
-    }
-
-    /**
-     * Checks if CurrentToken is set and equal to $this->element
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $token = $context->get('CurrentToken', true);
-        if ($token && $token->name == $this->element) {
-            return false;
-        }
-        return $this->def->validate($string, $config, $context);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php
deleted file mode 100644
index bde4c33..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Filter.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * Microsoft's proprietary filter: CSS property
- * @note Currently supports the alpha filter. In the future, this will
- *       probably need an extensible framework
- */
-class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef
-{
-    /**
-     * @type HTMLPurifier_AttrDef_Integer
-     */
-    protected $intValidator;
-
-    public function __construct()
-    {
-        $this->intValidator = new HTMLPurifier_AttrDef_Integer();
-    }
-
-    /**
-     * @param string $value
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($value, $config, $context)
-    {
-        $value = $this->parseCDATA($value);
-        if ($value === 'none') {
-            return $value;
-        }
-        // if we looped this we could support multiple filters
-        $function_length = strcspn($value, '(');
-        $function = trim(substr($value, 0, $function_length));
-        if ($function !== 'alpha' &&
-            $function !== 'Alpha' &&
-            $function !== 'progid:DXImageTransform.Microsoft.Alpha'
-        ) {
-            return false;
-        }
-        $cursor = $function_length + 1;
-        $parameters_length = strcspn($value, ')', $cursor);
-        $parameters = substr($value, $cursor, $parameters_length);
-        $params = explode(',', $parameters);
-        $ret_params = array();
-        $lookup = array();
-        foreach ($params as $param) {
-            list($key, $value) = explode('=', $param);
-            $key = trim($key);
-            $value = trim($value);
-            if (isset($lookup[$key])) {
-                continue;
-            }
-            if ($key !== 'opacity') {
-                continue;
-            }
-            $value = $this->intValidator->validate($value, $config, $context);
-            if ($value === false) {
-                continue;
-            }
-            $int = (int)$value;
-            if ($int > 100) {
-                $value = '100';
-            }
-            if ($int < 0) {
-                $value = '0';
-            }
-            $ret_params[] = "$key=$value";
-            $lookup[$key] = true;
-        }
-        $ret_parameters = implode(',', $ret_params);
-        $ret_function = "$function($ret_parameters)";
-        return $ret_function;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php
deleted file mode 100644
index 579b97e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Font.php
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-
-/**
- * Validates shorthand CSS property font.
- */
-class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of validators
-     * @type HTMLPurifier_AttrDef[]
-     * @note If we moved specific CSS property definitions to their own
-     *       classes instead of having them be assembled at run time by
-     *       CSSDefinition, this wouldn't be necessary.  We'd instantiate
-     *       our own copies.
-     */
-    protected $info = array();
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function __construct($config)
-    {
-        $def = $config->getCSSDefinition();
-        $this->info['font-style'] = $def->info['font-style'];
-        $this->info['font-variant'] = $def->info['font-variant'];
-        $this->info['font-weight'] = $def->info['font-weight'];
-        $this->info['font-size'] = $def->info['font-size'];
-        $this->info['line-height'] = $def->info['line-height'];
-        $this->info['font-family'] = $def->info['font-family'];
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        static $system_fonts = array(
-            'caption' => true,
-            'icon' => true,
-            'menu' => true,
-            'message-box' => true,
-            'small-caption' => true,
-            'status-bar' => true
-        );
-
-        // regular pre-processing
-        $string = $this->parseCDATA($string);
-        if ($string === '') {
-            return false;
-        }
-
-        // check if it's one of the keywords
-        $lowercase_string = strtolower($string);
-        if (isset($system_fonts[$lowercase_string])) {
-            return $lowercase_string;
-        }
-
-        $bits = explode(' ', $string); // bits to process
-        $stage = 0; // this indicates what we're looking for
-        $caught = array(); // which stage 0 properties have we caught?
-        $stage_1 = array('font-style', 'font-variant', 'font-weight');
-        $final = ''; // output
-
-        for ($i = 0, $size = count($bits); $i < $size; $i++) {
-            if ($bits[$i] === '') {
-                continue;
-            }
-            switch ($stage) {
-                case 0: // attempting to catch font-style, font-variant or font-weight
-                    foreach ($stage_1 as $validator_name) {
-                        if (isset($caught[$validator_name])) {
-                            continue;
-                        }
-                        $r = $this->info[$validator_name]->validate(
-                            $bits[$i],
-                            $config,
-                            $context
-                        );
-                        if ($r !== false) {
-                            $final .= $r . ' ';
-                            $caught[$validator_name] = true;
-                            break;
-                        }
-                    }
-                    // all three caught, continue on
-                    if (count($caught) >= 3) {
-                        $stage = 1;
-                    }
-                    if ($r !== false) {
-                        break;
-                    }
-                case 1: // attempting to catch font-size and perhaps line-height
-                    $found_slash = false;
-                    if (strpos($bits[$i], '/') !== false) {
-                        list($font_size, $line_height) =
-                            explode('/', $bits[$i]);
-                        if ($line_height === '') {
-                            // ooh, there's a space after the slash!
-                            $line_height = false;
-                            $found_slash = true;
-                        }
-                    } else {
-                        $font_size = $bits[$i];
-                        $line_height = false;
-                    }
-                    $r = $this->info['font-size']->validate(
-                        $font_size,
-                        $config,
-                        $context
-                    );
-                    if ($r !== false) {
-                        $final .= $r;
-                        // attempt to catch line-height
-                        if ($line_height === false) {
-                            // we need to scroll forward
-                            for ($j = $i + 1; $j < $size; $j++) {
-                                if ($bits[$j] === '') {
-                                    continue;
-                                }
-                                if ($bits[$j] === '/') {
-                                    if ($found_slash) {
-                                        return false;
-                                    } else {
-                                        $found_slash = true;
-                                        continue;
-                                    }
-                                }
-                                $line_height = $bits[$j];
-                                break;
-                            }
-                        } else {
-                            // slash already found
-                            $found_slash = true;
-                            $j = $i;
-                        }
-                        if ($found_slash) {
-                            $i = $j;
-                            $r = $this->info['line-height']->validate(
-                                $line_height,
-                                $config,
-                                $context
-                            );
-                            if ($r !== false) {
-                                $final .= '/' . $r;
-                            }
-                        }
-                        $final .= ' ';
-                        $stage = 2;
-                        break;
-                    }
-                    return false;
-                case 2: // attempting to catch font-family
-                    $font_family =
-                        implode(' ', array_slice($bits, $i, $size - $i));
-                    $r = $this->info['font-family']->validate(
-                        $font_family,
-                        $config,
-                        $context
-                    );
-                    if ($r !== false) {
-                        $final .= $r . ' ';
-                        // processing completed successfully
-                        return rtrim($final);
-                    }
-                    return false;
-            }
-        }
-        return false;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php
deleted file mode 100644
index 74e24c8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/FontFamily.php
+++ /dev/null
@@ -1,219 +0,0 @@
-<?php
-
-/**
- * Validates a font family list according to CSS spec
- */
-class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
-{
-
-    protected $mask = null;
-
-    public function __construct()
-    {
-        $this->mask = '_- ';
-        for ($c = 'a'; $c <= 'z'; $c++) {
-            $this->mask .= $c;
-        }
-        for ($c = 'A'; $c <= 'Z'; $c++) {
-            $this->mask .= $c;
-        }
-        for ($c = '0'; $c <= '9'; $c++) {
-            $this->mask .= $c;
-        } // cast-y, but should be fine
-        // special bytes used by UTF-8
-        for ($i = 0x80; $i <= 0xFF; $i++) {
-            // We don't bother excluding invalid bytes in this range,
-            // because the our restriction of well-formed UTF-8 will
-            // prevent these from ever occurring.
-            $this->mask .= chr($i);
-        }
-
-        /*
-            PHP's internal strcspn implementation is
-            O(length of string * length of mask), making it inefficient
-            for large masks.  However, it's still faster than
-            preg_match 8)
-          for (p = s1;;) {
-            spanp = s2;
-            do {
-              if (*spanp == c || p == s1_end) {
-                return p - s1;
-              }
-            } while (spanp++ < (s2_end - 1));
-            c = *++p;
-          }
-         */
-        // possible optimization: invert the mask.
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        static $generic_names = array(
-            'serif' => true,
-            'sans-serif' => true,
-            'monospace' => true,
-            'fantasy' => true,
-            'cursive' => true
-        );
-        $allowed_fonts = $config->get('CSS.AllowedFonts');
-
-        // assume that no font names contain commas in them
-        $fonts = explode(',', $string);
-        $final = '';
-        foreach ($fonts as $font) {
-            $font = trim($font);
-            if ($font === '') {
-                continue;
-            }
-            // match a generic name
-            if (isset($generic_names[$font])) {
-                if ($allowed_fonts === null || isset($allowed_fonts[$font])) {
-                    $final .= $font . ', ';
-                }
-                continue;
-            }
-            // match a quoted name
-            if ($font[0] === '"' || $font[0] === "'") {
-                $length = strlen($font);
-                if ($length <= 2) {
-                    continue;
-                }
-                $quote = $font[0];
-                if ($font[$length - 1] !== $quote) {
-                    continue;
-                }
-                $font = substr($font, 1, $length - 2);
-            }
-
-            $font = $this->expandCSSEscape($font);
-
-            // $font is a pure representation of the font name
-
-            if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) {
-                continue;
-            }
-
-            if (ctype_alnum($font) && $font !== '') {
-                // very simple font, allow it in unharmed
-                $final .= $font . ', ';
-                continue;
-            }
-
-            // bugger out on whitespace.  form feed (0C) really
-            // shouldn't show up regardless
-            $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
-
-            // Here, there are various classes of characters which need
-            // to be treated differently:
-            //  - Alphanumeric characters are essentially safe.  We
-            //    handled these above.
-            //  - Spaces require quoting, though most parsers will do
-            //    the right thing if there aren't any characters that
-            //    can be misinterpreted
-            //  - Dashes rarely occur, but they fairly unproblematic
-            //    for parsing/rendering purposes.
-            //  The above characters cover the majority of Western font
-            //  names.
-            //  - Arbitrary Unicode characters not in ASCII.  Because
-            //    most parsers give little thought to Unicode, treatment
-            //    of these codepoints is basically uniform, even for
-            //    punctuation-like codepoints.  These characters can
-            //    show up in non-Western pages and are supported by most
-            //    major browsers, for example: "MS 明朝" is a
-            //    legitimate font-name
-            //    <http://ja.wikipedia.org/wiki/MS_明朝>.  See
-            //    the CSS3 spec for more examples:
-            //    <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
-            //    You can see live samples of these on the Internet:
-            //    <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック>
-            //    However, most of these fonts have ASCII equivalents:
-            //    for example, 'MS Mincho', and it's considered
-            //    professional to use ASCII font names instead of
-            //    Unicode font names.  Thanks Takeshi Terada for
-            //    providing this information.
-            //  The following characters, to my knowledge, have not been
-            //  used to name font names.
-            //  - Single quote.  While theoretically you might find a
-            //    font name that has a single quote in its name (serving
-            //    as an apostrophe, e.g. Dave's Scribble), I haven't
-            //    been able to find any actual examples of this.
-            //    Internet Explorer's cssText translation (which I
-            //    believe is invoked by innerHTML) normalizes any
-            //    quoting to single quotes, and fails to escape single
-            //    quotes.  (Note that this is not IE's behavior for all
-            //    CSS properties, just some sort of special casing for
-            //    font-family).  So a single quote *cannot* be used
-            //    safely in the font-family context if there will be an
-            //    innerHTML/cssText translation.  Note that Firefox 3.x
-            //    does this too.
-            //  - Double quote.  In IE, these get normalized to
-            //    single-quotes, no matter what the encoding.  (Fun
-            //    fact, in IE8, the 'content' CSS property gained
-            //    support, where they special cased to preserve encoded
-            //    double quotes, but still translate unadorned double
-            //    quotes into single quotes.)  So, because their
-            //    fixpoint behavior is identical to single quotes, they
-            //    cannot be allowed either.  Firefox 3.x displays
-            //    single-quote style behavior.
-            //  - Backslashes are reduced by one (so \\ -> \) every
-            //    iteration, so they cannot be used safely.  This shows
-            //    up in IE7, IE8 and FF3
-            //  - Semicolons, commas and backticks are handled properly.
-            //  - The rest of the ASCII punctuation is handled properly.
-            // We haven't checked what browsers do to unadorned
-            // versions, but this is not important as long as the
-            // browser doesn't /remove/ surrounding quotes (as IE does
-            // for HTML).
-            //
-            // With these results in hand, we conclude that there are
-            // various levels of safety:
-            //  - Paranoid: alphanumeric, spaces and dashes(?)
-            //  - International: Paranoid + non-ASCII Unicode
-            //  - Edgy: Everything except quotes, backslashes
-            //  - NoJS: Standards compliance, e.g. sod IE. Note that
-            //    with some judicious character escaping (since certain
-            //    types of escaping doesn't work) this is theoretically
-            //    OK as long as innerHTML/cssText is not called.
-            // We believe that international is a reasonable default
-            // (that we will implement now), and once we do more
-            // extensive research, we may feel comfortable with dropping
-            // it down to edgy.
-
-            // Edgy: alphanumeric, spaces, dashes, underscores and Unicode.  Use of
-            // str(c)spn assumes that the string was already well formed
-            // Unicode (which of course it is).
-            if (strspn($font, $this->mask) !== strlen($font)) {
-                continue;
-            }
-
-            // Historical:
-            // In the absence of innerHTML/cssText, these ugly
-            // transforms don't pose a security risk (as \\ and \"
-            // might--these escapes are not supported by most browsers).
-            // We could try to be clever and use single-quote wrapping
-            // when there is a double quote present, but I have choosen
-            // not to implement that.  (NOTE: you can reduce the amount
-            // of escapes by one depending on what quoting style you use)
-            // $font = str_replace('\\', '\\5C ', $font);
-            // $font = str_replace('"',  '\\22 ', $font);
-            // $font = str_replace("'",  '\\27 ', $font);
-
-            // font possibly with spaces, requires quoting
-            $final .= "'$font', ";
-        }
-        $final = rtrim($final, ', ');
-        if ($final === '') {
-            return false;
-        }
-        return $final;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php
deleted file mode 100644
index 973002c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Ident.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Validates based on {ident} CSS grammar production
- */
-class HTMLPurifier_AttrDef_CSS_Ident extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = trim($string);
-
-        // early abort: '' and '0' (strings that convert to false) are invalid
-        if (!$string) {
-            return false;
-        }
-
-        $pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/';
-        if (!preg_match($pattern, $string)) {
-            return false;
-        }
-        return $string;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php
deleted file mode 100644
index ffc989f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Decorator which enables !important to be used in CSS values.
- */
-class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef
-{
-    /**
-     * @type HTMLPurifier_AttrDef
-     */
-    public $def;
-    /**
-     * @type bool
-     */
-    public $allow;
-
-    /**
-     * @param HTMLPurifier_AttrDef $def Definition to wrap
-     * @param bool $allow Whether or not to allow !important
-     */
-    public function __construct($def, $allow = false)
-    {
-        $this->def = $def;
-        $this->allow = $allow;
-    }
-
-    /**
-     * Intercepts and removes !important if necessary
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        // test for ! and important tokens
-        $string = trim($string);
-        $is_important = false;
-        // :TODO: optimization: test directly for !important and ! important
-        if (strlen($string) >= 9 && substr($string, -9) === 'important') {
-            $temp = rtrim(substr($string, 0, -9));
-            // use a temp, because we might want to restore important
-            if (strlen($temp) >= 1 && substr($temp, -1) === '!') {
-                $string = rtrim(substr($temp, 0, -1));
-                $is_important = true;
-            }
-        }
-        $string = $this->def->validate($string, $config, $context);
-        if ($this->allow && $is_important) {
-            $string .= ' !important';
-        }
-        return $string;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php
deleted file mode 100644
index f12453a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Length.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * Represents a Length as defined by CSS.
- */
-class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @type HTMLPurifier_Length|string
-     */
-    protected $min;
-
-    /**
-     * @type HTMLPurifier_Length|string
-     */
-    protected $max;
-
-    /**
-     * @param HTMLPurifier_Length|string $min Minimum length, or null for no bound. String is also acceptable.
-     * @param HTMLPurifier_Length|string $max Maximum length, or null for no bound. String is also acceptable.
-     */
-    public function __construct($min = null, $max = null)
-    {
-        $this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
-        $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = $this->parseCDATA($string);
-
-        // Optimizations
-        if ($string === '') {
-            return false;
-        }
-        if ($string === '0') {
-            return '0';
-        }
-        if (strlen($string) === 1) {
-            return false;
-        }
-
-        $length = HTMLPurifier_Length::make($string);
-        if (!$length->isValid()) {
-            return false;
-        }
-
-        if ($this->min) {
-            $c = $length->compareTo($this->min);
-            if ($c === false) {
-                return false;
-            }
-            if ($c < 0) {
-                return false;
-            }
-        }
-        if ($this->max) {
-            $c = $length->compareTo($this->max);
-            if ($c === false) {
-                return false;
-            }
-            if ($c > 0) {
-                return false;
-            }
-        }
-        return $length->toString();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php
deleted file mode 100644
index e74d426..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/ListStyle.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/**
- * Validates shorthand CSS property list-style.
- * @warning Does not support url tokens that have internal spaces.
- */
-class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Local copy of validators.
-     * @type HTMLPurifier_AttrDef[]
-     * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
-     */
-    protected $info;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function __construct($config)
-    {
-        $def = $config->getCSSDefinition();
-        $this->info['list-style-type'] = $def->info['list-style-type'];
-        $this->info['list-style-position'] = $def->info['list-style-position'];
-        $this->info['list-style-image'] = $def->info['list-style-image'];
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        // regular pre-processing
-        $string = $this->parseCDATA($string);
-        if ($string === '') {
-            return false;
-        }
-
-        // assumes URI doesn't have spaces in it
-        $bits = explode(' ', strtolower($string)); // bits to process
-
-        $caught = array();
-        $caught['type'] = false;
-        $caught['position'] = false;
-        $caught['image'] = false;
-
-        $i = 0; // number of catches
-        $none = false;
-
-        foreach ($bits as $bit) {
-            if ($i >= 3) {
-                return;
-            } // optimization bit
-            if ($bit === '') {
-                continue;
-            }
-            foreach ($caught as $key => $status) {
-                if ($status !== false) {
-                    continue;
-                }
-                $r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
-                if ($r === false) {
-                    continue;
-                }
-                if ($r === 'none') {
-                    if ($none) {
-                        continue;
-                    } else {
-                        $none = true;
-                    }
-                    if ($key == 'image') {
-                        continue;
-                    }
-                }
-                $caught[$key] = $r;
-                $i++;
-                break;
-            }
-        }
-
-        if (!$i) {
-            return false;
-        }
-
-        $ret = array();
-
-        // construct type
-        if ($caught['type']) {
-            $ret[] = $caught['type'];
-        }
-
-        // construct image
-        if ($caught['image']) {
-            $ret[] = $caught['image'];
-        }
-
-        // construct position
-        if ($caught['position']) {
-            $ret[] = $caught['position'];
-        }
-
-        if (empty($ret)) {
-            return false;
-        }
-        return implode(' ', $ret);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php
deleted file mode 100644
index e707f87..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Multiple.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * Framework class for strings that involve multiple values.
- *
- * Certain CSS properties such as border-width and margin allow multiple
- * lengths to be specified.  This class can take a vanilla border-width
- * definition and multiply it, usually into a max of four.
- *
- * @note Even though the CSS specification isn't clear about it, inherit
- *       can only be used alone: it will never manifest as part of a multi
- *       shorthand declaration.  Thus, this class does not allow inherit.
- */
-class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
-{
-    /**
-     * Instance of component definition to defer validation to.
-     * @type HTMLPurifier_AttrDef
-     * @todo Make protected
-     */
-    public $single;
-
-    /**
-     * Max number of values allowed.
-     * @todo Make protected
-     */
-    public $max;
-
-    /**
-     * @param HTMLPurifier_AttrDef $single HTMLPurifier_AttrDef to multiply
-     * @param int $max Max number of values allowed (usually four)
-     */
-    public function __construct($single, $max = 4)
-    {
-        $this->single = $single;
-        $this->max = $max;
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = $this->mungeRgb($this->parseCDATA($string));
-        if ($string === '') {
-            return false;
-        }
-        $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n
-        $length = count($parts);
-        $final = '';
-        for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {
-            if (ctype_space($parts[$i])) {
-                continue;
-            }
-            $result = $this->single->validate($parts[$i], $config, $context);
-            if ($result !== false) {
-                $final .= $result . ' ';
-                $num++;
-            }
-        }
-        if ($final === '') {
-            return false;
-        }
-        return rtrim($final);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php
deleted file mode 100644
index ef49d20..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Number.php
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-/**
- * Validates a number as defined by the CSS spec.
- */
-class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Indicates whether or not only positive values are allowed.
-     * @type bool
-     */
-    protected $non_negative = false;
-
-    /**
-     * @param bool $non_negative indicates whether negatives are forbidden
-     */
-    public function __construct($non_negative = false)
-    {
-        $this->non_negative = $non_negative;
-    }
-
-    /**
-     * @param string $number
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string|bool
-     * @warning Some contexts do not pass $config, $context. These
-     *          variables should not be used without checking HTMLPurifier_Length
-     */
-    public function validate($number, $config, $context)
-    {
-        $number = $this->parseCDATA($number);
-
-        if ($number === '') {
-            return false;
-        }
-        if ($number === '0') {
-            return '0';
-        }
-
-        $sign = '';
-        switch ($number[0]) {
-            case '-':
-                if ($this->non_negative) {
-                    return false;
-                }
-                $sign = '-';
-            case '+':
-                $number = substr($number, 1);
-        }
-
-        if (ctype_digit($number)) {
-            $number = ltrim($number, '0');
-            return $number ? $sign . $number : '0';
-        }
-
-        // Period is the only non-numeric character allowed
-        if (strpos($number, '.') === false) {
-            return false;
-        }
-
-        list($left, $right) = explode('.', $number, 2);
-
-        if ($left === '' && $right === '') {
-            return false;
-        }
-        if ($left !== '' && !ctype_digit($left)) {
-            return false;
-        }
-
-        // Remove leading zeros until positive number or a zero stays left
-        if (ltrim($left, '0') != '') {
-            $left = ltrim($left, '0');
-        } else {
-            $left = '0';
-        }
-
-        $right = rtrim($right, '0');
-
-        if ($right === '') {
-            return $left ? $sign . $left : '0';
-        } elseif (!ctype_digit($right)) {
-            return false;
-        }
-        return $sign . $left . '.' . $right;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php
deleted file mode 100644
index f0f25c5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/Percentage.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * Validates a Percentage as defined by the CSS spec.
- */
-class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Instance to defer number validation to.
-     * @type HTMLPurifier_AttrDef_CSS_Number
-     */
-    protected $number_def;
-
-    /**
-     * @param bool $non_negative Whether to forbid negative values
-     */
-    public function __construct($non_negative = false)
-    {
-        $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = $this->parseCDATA($string);
-
-        if ($string === '') {
-            return false;
-        }
-        $length = strlen($string);
-        if ($length === 1) {
-            return false;
-        }
-        if ($string[$length - 1] !== '%') {
-            return false;
-        }
-
-        $number = substr($string, 0, $length - 1);
-        $number = $this->number_def->validate($number, $config, $context);
-
-        if ($number === false) {
-            return false;
-        }
-        return "$number%";
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php
deleted file mode 100644
index 5fd4b7f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * Validates the value for the CSS property text-decoration
- * @note This class could be generalized into a version that acts sort of
- *       like Enum except you can compound the allowed values.
- */
-class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        static $allowed_values = array(
-            'line-through' => true,
-            'overline' => true,
-            'underline' => true,
-        );
-
-        $string = strtolower($this->parseCDATA($string));
-
-        if ($string === 'none') {
-            return $string;
-        }
-
-        $parts = explode(' ', $string);
-        $final = '';
-        foreach ($parts as $part) {
-            if (isset($allowed_values[$part])) {
-                $final .= $part . ' ';
-            }
-        }
-        $final = rtrim($final);
-        if ($final === '') {
-            return false;
-        }
-        return $final;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php
deleted file mode 100644
index 6617aca..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/URI.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * Validates a URI in CSS syntax, which uses url('http://example.com')
- * @note While theoretically speaking a URI in a CSS document could
- *       be non-embedded, as of CSS2 there is no such usage so we're
- *       generalizing it. This may need to be changed in the future.
- * @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as
- *          the separator, you cannot put a literal semicolon in
- *          in the URI. Try percent encoding it, in that case.
- */
-class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
-{
-
-    public function __construct()
-    {
-        parent::__construct(true); // always embedded
-    }
-
-    /**
-     * @param string $uri_string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($uri_string, $config, $context)
-    {
-        // parse the URI out of the string and then pass it onto
-        // the parent object
-
-        $uri_string = $this->parseCDATA($uri_string);
-        if (strpos($uri_string, 'url(') !== 0) {
-            return false;
-        }
-        $uri_string = substr($uri_string, 4);
-        if (strlen($uri_string) == 0) {
-            return false;
-        }
-        $new_length = strlen($uri_string) - 1;
-        if ($uri_string[$new_length] != ')') {
-            return false;
-        }
-        $uri = trim(substr($uri_string, 0, $new_length));
-
-        if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) {
-            $quote = $uri[0];
-            $new_length = strlen($uri) - 1;
-            if ($uri[$new_length] !== $quote) {
-                return false;
-            }
-            $uri = substr($uri, 1, $new_length - 1);
-        }
-
-        $uri = $this->expandCSSEscape($uri);
-
-        $result = parent::validate($uri, $config, $context);
-
-        if ($result === false) {
-            return false;
-        }
-
-        // extra sanity check; should have been done by URI
-        $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result);
-
-        // suspicious characters are ()'; we're going to percent encode
-        // them for safety.
-        $result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result);
-
-        // there's an extra bug where ampersands lose their escaping on
-        // an innerHTML cycle, so a very unlucky query parameter could
-        // then change the meaning of the URL.  Unfortunately, there's
-        // not much we can do about that...
-        return "url(\"$result\")";
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php
deleted file mode 100644
index 6698a00..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Clone.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Dummy AttrDef that mimics another AttrDef, BUT it generates clones
- * with make.
- */
-class HTMLPurifier_AttrDef_Clone extends HTMLPurifier_AttrDef
-{
-    /**
-     * What we're cloning.
-     * @type HTMLPurifier_AttrDef
-     */
-    protected $clone;
-
-    /**
-     * @param HTMLPurifier_AttrDef $clone
-     */
-    public function __construct($clone)
-    {
-        $this->clone = $clone;
-    }
-
-    /**
-     * @param string $v
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($v, $config, $context)
-    {
-        return $this->clone->validate($v, $config, $context);
-    }
-
-    /**
-     * @param string $string
-     * @return HTMLPurifier_AttrDef
-     */
-    public function make($string)
-    {
-        return clone $this->clone;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php
deleted file mode 100644
index 8abda7f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Enum.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-// Enum = Enumerated
-/**
- * Validates a keyword against a list of valid values.
- * @warning The case-insensitive compare of this function uses PHP's
- *          built-in strtolower and ctype_lower functions, which may
- *          cause problems with international comparisons
- */
-class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Lookup table of valid values.
-     * @type array
-     * @todo Make protected
-     */
-    public $valid_values = array();
-
-    /**
-     * Bool indicating whether or not enumeration is case sensitive.
-     * @note In general this is always case insensitive.
-     */
-    protected $case_sensitive = false; // values according to W3C spec
-
-    /**
-     * @param array $valid_values List of valid values
-     * @param bool $case_sensitive Whether or not case sensitive
-     */
-    public function __construct($valid_values = array(), $case_sensitive = false)
-    {
-        $this->valid_values = array_flip($valid_values);
-        $this->case_sensitive = $case_sensitive;
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = trim($string);
-        if (!$this->case_sensitive) {
-            // we may want to do full case-insensitive libraries
-            $string = ctype_lower($string) ? $string : strtolower($string);
-        }
-        $result = isset($this->valid_values[$string]);
-
-        return $result ? $string : false;
-    }
-
-    /**
-     * @param string $string In form of comma-delimited list of case-insensitive
-     *      valid values. Example: "foo,bar,baz". Prepend "s:" to make
-     *      case sensitive
-     * @return HTMLPurifier_AttrDef_Enum
-     */
-    public function make($string)
-    {
-        if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') {
-            $string = substr($string, 2);
-            $sensitive = true;
-        } else {
-            $sensitive = false;
-        }
-        $values = explode(',', $string);
-        return new HTMLPurifier_AttrDef_Enum($values, $sensitive);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php
deleted file mode 100644
index be3bbc8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Bool.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Validates a boolean attribute
- */
-class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @type string
-     */
-    protected $name;
-
-    /**
-     * @type bool
-     */
-    public $minimized = true;
-
-    /**
-     * @param bool|string $name
-     */
-    public function __construct($name = false)
-    {
-        $this->name = $name;
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        return $this->name;
-    }
-
-    /**
-     * @param string $string Name of attribute
-     * @return HTMLPurifier_AttrDef_HTML_Bool
-     */
-    public function make($string)
-    {
-        return new HTMLPurifier_AttrDef_HTML_Bool($string);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php
deleted file mode 100644
index d501348..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Class.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Implements special behavior for class attribute (normally NMTOKENS)
- */
-class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens
-{
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    protected function split($string, $config, $context)
-    {
-        // really, this twiddle should be lazy loaded
-        $name = $config->getDefinition('HTML')->doctype->name;
-        if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {
-            return parent::split($string, $config, $context);
-        } else {
-            return preg_split('/\s+/', $string);
-        }
-    }
-
-    /**
-     * @param array $tokens
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    protected function filter($tokens, $config, $context)
-    {
-        $allowed = $config->get('Attr.AllowedClasses');
-        $forbidden = $config->get('Attr.ForbiddenClasses');
-        $ret = array();
-        foreach ($tokens as $token) {
-            if (($allowed === null || isset($allowed[$token])) &&
-                !isset($forbidden[$token]) &&
-                // We need this O(n) check because of PHP's array
-                // implementation that casts -0 to 0.
-                !in_array($token, $ret, true)
-            ) {
-                $ret[] = $token;
-            }
-        }
-        return $ret;
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php
deleted file mode 100644
index 946ebb7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Color.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * Validates a color according to the HTML spec.
- */
-class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        static $colors = null;
-        if ($colors === null) {
-            $colors = $config->get('Core.ColorKeywords');
-        }
-
-        $string = trim($string);
-
-        if (empty($string)) {
-            return false;
-        }
-        $lower = strtolower($string);
-        if (isset($colors[$lower])) {
-            return $colors[$lower];
-        }
-        if ($string[0] === '#') {
-            $hex = substr($string, 1);
-        } else {
-            $hex = $string;
-        }
-
-        $length = strlen($hex);
-        if ($length !== 3 && $length !== 6) {
-            return false;
-        }
-        if (!ctype_xdigit($hex)) {
-            return false;
-        }
-        if ($length === 3) {
-            $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
-        }
-        return "#$hex";
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ContentEditable.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ContentEditable.php
deleted file mode 100644
index 5b03d3e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ContentEditable.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-class HTMLPurifier_AttrDef_HTML_ContentEditable extends HTMLPurifier_AttrDef
-{
-    public function validate($string, $config, $context)
-    {
-        $allowed = array('false');
-        if ($config->get('HTML.Trusted')) {
-            $allowed = array('', 'true', 'false');
-        }
-
-        $enum = new HTMLPurifier_AttrDef_Enum($allowed);
-
-        return $enum->validate($string, $config, $context);
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php
deleted file mode 100644
index d79ba12..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * Special-case enum attribute definition that lazy loads allowed frame targets
- */
-class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
-{
-
-    /**
-     * @type array
-     */
-    public $valid_values = false; // uninitialized value
-
-    /**
-     * @type bool
-     */
-    protected $case_sensitive = false;
-
-    public function __construct()
-    {
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        if ($this->valid_values === false) {
-            $this->valid_values = $config->get('Attr.AllowedFrameTargets');
-        }
-        return parent::validate($string, $config, $context);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php
deleted file mode 100644
index 4ba4561..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/ID.php
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-/**
- * Validates the HTML attribute ID.
- * @warning Even though this is the id processor, it
- *          will ignore the directive Attr:IDBlacklist, since it will only
- *          go according to the ID accumulator. Since the accumulator is
- *          automatically generated, it will have already absorbed the
- *          blacklist. If you're hacking around, make sure you use load()!
- */
-
-class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
-{
-
-    // selector is NOT a valid thing to use for IDREFs, because IDREFs
-    // *must* target IDs that exist, whereas selector #ids do not.
-
-    /**
-     * Determines whether or not we're validating an ID in a CSS
-     * selector context.
-     * @type bool
-     */
-    protected $selector;
-
-    /**
-     * @param bool $selector
-     */
-    public function __construct($selector = false)
-    {
-        $this->selector = $selector;
-    }
-
-    /**
-     * @param string $id
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($id, $config, $context)
-    {
-        if (!$this->selector && !$config->get('Attr.EnableID')) {
-            return false;
-        }
-
-        $id = trim($id); // trim it first
-
-        if ($id === '') {
-            return false;
-        }
-
-        $prefix = $config->get('Attr.IDPrefix');
-        if ($prefix !== '') {
-            $prefix .= $config->get('Attr.IDPrefixLocal');
-            // prevent re-appending the prefix
-            if (strpos($id, $prefix) !== 0) {
-                $id = $prefix . $id;
-            }
-        } elseif ($config->get('Attr.IDPrefixLocal') !== '') {
-            trigger_error(
-                '%Attr.IDPrefixLocal cannot be used unless ' .
-                '%Attr.IDPrefix is set',
-                E_USER_WARNING
-            );
-        }
-
-        if (!$this->selector) {
-            $id_accumulator =& $context->get('IDAccumulator');
-            if (isset($id_accumulator->ids[$id])) {
-                return false;
-            }
-        }
-
-        // we purposely avoid using regex, hopefully this is faster
-
-        if ($config->get('Attr.ID.HTML5') === true) {
-            if (preg_match('/[\t\n\x0b\x0c ]/', $id)) {
-                return false;
-            }
-        } else {
-            if (ctype_alpha($id)) {
-                // OK
-            } else {
-                if (!ctype_alpha(@$id[0])) {
-                    return false;
-                }
-                // primitive style of regexps, I suppose
-                $trim = trim(
-                    $id,
-                    'A..Za..z0..9:-._'
-                );
-                if ($trim !== '') {
-                    return false;
-                }
-            }
-        }
-
-        $regexp = $config->get('Attr.IDBlacklistRegexp');
-        if ($regexp && preg_match($regexp, $id)) {
-            return false;
-        }
-
-        if (!$this->selector) {
-            $id_accumulator->add($id);
-        }
-
-        // if no change was made to the ID, return the result
-        // else, return the new id if stripping whitespace made it
-        //     valid, or return false.
-        return $id;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php
deleted file mode 100644
index 1c4006f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Length.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Validates the HTML type length (not to be confused with CSS's length).
- *
- * This accepts integer pixels or percentages as lengths for certain
- * HTML attributes.
- */
-
-class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = trim($string);
-        if ($string === '') {
-            return false;
-        }
-
-        $parent_result = parent::validate($string, $config, $context);
-        if ($parent_result !== false) {
-            return $parent_result;
-        }
-
-        $length = strlen($string);
-        $last_char = $string[$length - 1];
-
-        if ($last_char !== '%') {
-            return false;
-        }
-
-        $points = substr($string, 0, $length - 1);
-
-        if (!is_numeric($points)) {
-            return false;
-        }
-
-        $points = (int)$points;
-
-        if ($points < 0) {
-            return '0%';
-        }
-        if ($points > 100) {
-            return '100%';
-        }
-        return ((string)$points) . '%';
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php
deleted file mode 100644
index 63fa04c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-/**
- * Validates a rel/rev link attribute against a directive of allowed values
- * @note We cannot use Enum because link types allow multiple
- *       values.
- * @note Assumes link types are ASCII text
- */
-class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Name config attribute to pull.
-     * @type string
-     */
-    protected $name;
-
-    /**
-     * @param string $name
-     */
-    public function __construct($name)
-    {
-        $configLookup = array(
-            'rel' => 'AllowedRel',
-            'rev' => 'AllowedRev'
-        );
-        if (!isset($configLookup[$name])) {
-            trigger_error(
-                'Unrecognized attribute name for link ' .
-                'relationship.',
-                E_USER_ERROR
-            );
-            return;
-        }
-        $this->name = $configLookup[$name];
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $allowed = $config->get('Attr.' . $this->name);
-        if (empty($allowed)) {
-            return false;
-        }
-
-        $string = $this->parseCDATA($string);
-        $parts = explode(' ', $string);
-
-        // lookup to prevent duplicates
-        $ret_lookup = array();
-        foreach ($parts as $part) {
-            $part = strtolower(trim($part));
-            if (!isset($allowed[$part])) {
-                continue;
-            }
-            $ret_lookup[$part] = true;
-        }
-
-        if (empty($ret_lookup)) {
-            return false;
-        }
-        $string = implode(' ', array_keys($ret_lookup));
-        return $string;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php
deleted file mode 100644
index bbb20f2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/MultiLength.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-/**
- * Validates a MultiLength as defined by the HTML spec.
- *
- * A multilength is either a integer (pixel count), a percentage, or
- * a relative number.
- */
-class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = trim($string);
-        if ($string === '') {
-            return false;
-        }
-
-        $parent_result = parent::validate($string, $config, $context);
-        if ($parent_result !== false) {
-            return $parent_result;
-        }
-
-        $length = strlen($string);
-        $last_char = $string[$length - 1];
-
-        if ($last_char !== '*') {
-            return false;
-        }
-
-        $int = substr($string, 0, $length - 1);
-
-        if ($int == '') {
-            return '*';
-        }
-        if (!is_numeric($int)) {
-            return false;
-        }
-
-        $int = (int)$int;
-        if ($int < 0) {
-            return false;
-        }
-        if ($int == 0) {
-            return '0';
-        }
-        if ($int == 1) {
-            return '*';
-        }
-        return ((string)$int) . '*';
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php
deleted file mode 100644
index f79683b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/**
- * Validates contents based on NMTOKENS attribute type.
- */
-class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = trim($string);
-
-        // early abort: '' and '0' (strings that convert to false) are invalid
-        if (!$string) {
-            return false;
-        }
-
-        $tokens = $this->split($string, $config, $context);
-        $tokens = $this->filter($tokens, $config, $context);
-        if (empty($tokens)) {
-            return false;
-        }
-        return implode(' ', $tokens);
-    }
-
-    /**
-     * Splits a space separated list of tokens into its constituent parts.
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    protected function split($string, $config, $context)
-    {
-        // OPTIMIZABLE!
-        // do the preg_match, capture all subpatterns for reformulation
-
-        // we don't support U+00A1 and up codepoints or
-        // escaping because I don't know how to do that with regexps
-        // and plus it would complicate optimization efforts (you never
-        // see that anyway).
-        $pattern = '/(?:(?<=\s)|\A)' . // look behind for space or string start
-            '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)' .
-            '(?:(?=\s)|\z)/'; // look ahead for space or string end
-        preg_match_all($pattern, $string, $matches);
-        return $matches[1];
-    }
-
-    /**
-     * Template method for removing certain tokens based on arbitrary criteria.
-     * @note If we wanted to be really functional, we'd do an array_filter
-     *       with a callback. But... we're not.
-     * @param array $tokens
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    protected function filter($tokens, $config, $context)
-    {
-        return $tokens;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php
deleted file mode 100644
index a1d019e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/HTML/Pixels.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-/**
- * Validates an integer representation of pixels according to the HTML spec.
- */
-class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @type int
-     */
-    protected $max;
-
-    /**
-     * @param int $max
-     */
-    public function __construct($max = null)
-    {
-        $this->max = $max;
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = trim($string);
-        if ($string === '0') {
-            return $string;
-        }
-        if ($string === '') {
-            return false;
-        }
-        $length = strlen($string);
-        if (substr($string, $length - 2) == 'px') {
-            $string = substr($string, 0, $length - 2);
-        }
-        if (!is_numeric($string)) {
-            return false;
-        }
-        $int = (int)$string;
-
-        if ($int < 0) {
-            return '0';
-        }
-
-        // upper-bound value, extremely high values can
-        // crash operating systems, see <http://ha.ckers.org/imagecrash.html>
-        // WARNING, above link WILL crash you if you're using Windows
-
-        if ($this->max !== null && $int > $this->max) {
-            return (string)$this->max;
-        }
-        return (string)$int;
-    }
-
-    /**
-     * @param string $string
-     * @return HTMLPurifier_AttrDef
-     */
-    public function make($string)
-    {
-        if ($string === '') {
-            $max = null;
-        } else {
-            $max = (int)$string;
-        }
-        $class = get_class($this);
-        return new $class($max);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php
deleted file mode 100644
index 400e707..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Integer.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-/**
- * Validates an integer.
- * @note While this class was modeled off the CSS definition, no currently
- *       allowed CSS uses this type.  The properties that do are: widows,
- *       orphans, z-index, counter-increment, counter-reset.  Some of the
- *       HTML attributes, however, find use for a non-negative version of this.
- */
-class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Whether or not negative values are allowed.
-     * @type bool
-     */
-    protected $negative = true;
-
-    /**
-     * Whether or not zero is allowed.
-     * @type bool
-     */
-    protected $zero = true;
-
-    /**
-     * Whether or not positive values are allowed.
-     * @type bool
-     */
-    protected $positive = true;
-
-    /**
-     * @param $negative Bool indicating whether or not negative values are allowed
-     * @param $zero Bool indicating whether or not zero is allowed
-     * @param $positive Bool indicating whether or not positive values are allowed
-     */
-    public function __construct($negative = true, $zero = true, $positive = true)
-    {
-        $this->negative = $negative;
-        $this->zero = $zero;
-        $this->positive = $positive;
-    }
-
-    /**
-     * @param string $integer
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($integer, $config, $context)
-    {
-        $integer = $this->parseCDATA($integer);
-        if ($integer === '') {
-            return false;
-        }
-
-        // we could possibly simply typecast it to integer, but there are
-        // certain fringe cases that must not return an integer.
-
-        // clip leading sign
-        if ($this->negative && $integer[0] === '-') {
-            $digits = substr($integer, 1);
-            if ($digits === '0') {
-                $integer = '0';
-            } // rm minus sign for zero
-        } elseif ($this->positive && $integer[0] === '+') {
-            $digits = $integer = substr($integer, 1); // rm unnecessary plus
-        } else {
-            $digits = $integer;
-        }
-
-        // test if it's numeric
-        if (!ctype_digit($digits)) {
-            return false;
-        }
-
-        // perform scope tests
-        if (!$this->zero && $integer == 0) {
-            return false;
-        }
-        if (!$this->positive && $integer > 0) {
-            return false;
-        }
-        if (!$this->negative && $integer < 0) {
-            return false;
-        }
-
-        return $integer;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php
deleted file mode 100644
index 2a55cea..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Lang.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/**
- * Validates the HTML attribute lang, effectively a language code.
- * @note Built according to RFC 3066, which obsoleted RFC 1766
- */
-class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $string = trim($string);
-        if (!$string) {
-            return false;
-        }
-
-        $subtags = explode('-', $string);
-        $num_subtags = count($subtags);
-
-        if ($num_subtags == 0) { // sanity check
-            return false;
-        }
-
-        // process primary subtag : $subtags[0]
-        $length = strlen($subtags[0]);
-        switch ($length) {
-            case 0:
-                return false;
-            case 1:
-                if (!($subtags[0] == 'x' || $subtags[0] == 'i')) {
-                    return false;
-                }
-                break;
-            case 2:
-            case 3:
-                if (!ctype_alpha($subtags[0])) {
-                    return false;
-                } elseif (!ctype_lower($subtags[0])) {
-                    $subtags[0] = strtolower($subtags[0]);
-                }
-                break;
-            default:
-                return false;
-        }
-
-        $new_string = $subtags[0];
-        if ($num_subtags == 1) {
-            return $new_string;
-        }
-
-        // process second subtag : $subtags[1]
-        $length = strlen($subtags[1]);
-        if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
-            return $new_string;
-        }
-        if (!ctype_lower($subtags[1])) {
-            $subtags[1] = strtolower($subtags[1]);
-        }
-
-        $new_string .= '-' . $subtags[1];
-        if ($num_subtags == 2) {
-            return $new_string;
-        }
-
-        // process all other subtags, index 2 and up
-        for ($i = 2; $i < $num_subtags; $i++) {
-            $length = strlen($subtags[$i]);
-            if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) {
-                return $new_string;
-            }
-            if (!ctype_lower($subtags[$i])) {
-                $subtags[$i] = strtolower($subtags[$i]);
-            }
-            $new_string .= '-' . $subtags[$i];
-        }
-        return $new_string;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php
deleted file mode 100644
index c7eb319..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Switch.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-/**
- * Decorator that, depending on a token, switches between two definitions.
- */
-class HTMLPurifier_AttrDef_Switch
-{
-
-    /**
-     * @type string
-     */
-    protected $tag;
-
-    /**
-     * @type HTMLPurifier_AttrDef
-     */
-    protected $withTag;
-
-    /**
-     * @type HTMLPurifier_AttrDef
-     */
-    protected $withoutTag;
-
-    /**
-     * @param string $tag Tag name to switch upon
-     * @param HTMLPurifier_AttrDef $with_tag Call if token matches tag
-     * @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token
-     */
-    public function __construct($tag, $with_tag, $without_tag)
-    {
-        $this->tag = $tag;
-        $this->withTag = $with_tag;
-        $this->withoutTag = $without_tag;
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $token = $context->get('CurrentToken', true);
-        if (!$token || $token->name !== $this->tag) {
-            return $this->withoutTag->validate($string, $config, $context);
-        } else {
-            return $this->withTag->validate($string, $config, $context);
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php
deleted file mode 100644
index 4553a4e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/Text.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * Validates arbitrary text according to the HTML spec.
- */
-class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        return $this->parseCDATA($string);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php
deleted file mode 100644
index c1cd897..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-/**
- * Validates a URI as defined by RFC 3986.
- * @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
- */
-class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * @type HTMLPurifier_URIParser
-     */
-    protected $parser;
-
-    /**
-     * @type bool
-     */
-    protected $embedsResource;
-
-    /**
-     * @param bool $embeds_resource Does the URI here result in an extra HTTP request?
-     */
-    public function __construct($embeds_resource = false)
-    {
-        $this->parser = new HTMLPurifier_URIParser();
-        $this->embedsResource = (bool)$embeds_resource;
-    }
-
-    /**
-     * @param string $string
-     * @return HTMLPurifier_AttrDef_URI
-     */
-    public function make($string)
-    {
-        $embeds = ($string === 'embedded');
-        return new HTMLPurifier_AttrDef_URI($embeds);
-    }
-
-    /**
-     * @param string $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($uri, $config, $context)
-    {
-        if ($config->get('URI.Disable')) {
-            return false;
-        }
-
-        $uri = $this->parseCDATA($uri);
-
-        // parse the URI
-        $uri = $this->parser->parse($uri);
-        if ($uri === false) {
-            return false;
-        }
-
-        // add embedded flag to context for validators
-        $context->register('EmbeddedURI', $this->embedsResource);
-
-        $ok = false;
-        do {
-
-            // generic validation
-            $result = $uri->validate($config, $context);
-            if (!$result) {
-                break;
-            }
-
-            // chained filtering
-            $uri_def = $config->getDefinition('URI');
-            $result = $uri_def->filter($uri, $config, $context);
-            if (!$result) {
-                break;
-            }
-
-            // scheme-specific validation
-            $scheme_obj = $uri->getSchemeObj($config, $context);
-            if (!$scheme_obj) {
-                break;
-            }
-            if ($this->embedsResource && !$scheme_obj->browsable) {
-                break;
-            }
-            $result = $scheme_obj->validate($uri, $config, $context);
-            if (!$result) {
-                break;
-            }
-
-            // Post chained filtering
-            $result = $uri_def->postFilter($uri, $config, $context);
-            if (!$result) {
-                break;
-            }
-
-            // survived gauntlet
-            $ok = true;
-
-        } while (false);
-
-        $context->destroy('EmbeddedURI');
-        if (!$ok) {
-            return false;
-        }
-        // back to string
-        return $uri->toString();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php
deleted file mode 100644
index daf32b7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * Unpacks a mailbox into its display-name and address
-     * @param string $string
-     * @return mixed
-     */
-    public function unpack($string)
-    {
-        // needs to be implemented
-    }
-
-}
-
-// sub-implementations
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php
deleted file mode 100644
index 52c0d59..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-/**
- * Primitive email validation class based on the regexp found at
- * http://www.regular-expressions.info/email.html
- */
-class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email
-{
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        // no support for named mailboxes i.e. "Bob <bob@example.com>"
-        // that needs more percent encoding to be done
-        if ($string == '') {
-            return false;
-        }
-        $string = trim($string);
-        $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
-        return $result ? $string : false;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php
deleted file mode 100644
index 1beeaa5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/Host.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-/**
- * Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
- */
-class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * IPv4 sub-validator.
-     * @type HTMLPurifier_AttrDef_URI_IPv4
-     */
-    protected $ipv4;
-
-    /**
-     * IPv6 sub-validator.
-     * @type HTMLPurifier_AttrDef_URI_IPv6
-     */
-    protected $ipv6;
-
-    public function __construct()
-    {
-        $this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
-        $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
-    }
-
-    /**
-     * @param string $string
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($string, $config, $context)
-    {
-        $length = strlen($string);
-        // empty hostname is OK; it's usually semantically equivalent:
-        // the default host as defined by a URI scheme is used:
-        //
-        //      If the URI scheme defines a default for host, then that
-        //      default applies when the host subcomponent is undefined
-        //      or when the registered name is empty (zero length).
-        if ($string === '') {
-            return '';
-        }
-        if ($length > 1 && $string[0] === '[' && $string[$length - 1] === ']') {
-            //IPv6
-            $ip = substr($string, 1, $length - 2);
-            $valid = $this->ipv6->validate($ip, $config, $context);
-            if ($valid === false) {
-                return false;
-            }
-            return '[' . $valid . ']';
-        }
-
-        // need to do checks on unusual encodings too
-        $ipv4 = $this->ipv4->validate($string, $config, $context);
-        if ($ipv4 !== false) {
-            return $ipv4;
-        }
-
-        // A regular domain name.
-
-        // This doesn't match I18N domain names, but we don't have proper IRI support,
-        // so force users to insert Punycode.
-
-        // There is not a good sense in which underscores should be
-        // allowed, since it's technically not! (And if you go as
-        // far to allow everything as specified by the DNS spec...
-        // well, that's literally everything, modulo some space limits
-        // for the components and the overall name (which, by the way,
-        // we are NOT checking!).  So we (arbitrarily) decide this:
-        // let's allow underscores wherever we would have allowed
-        // hyphens, if they are enabled.  This is a pretty good match
-        // for browser behavior, for example, a large number of browsers
-        // cannot handle foo_.example.com, but foo_bar.example.com is
-        // fairly well supported.
-        $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
-
-        // Based off of RFC 1738, but amended so that
-        // as per RFC 3696, the top label need only not be all numeric.
-        // The productions describing this are:
-        $a   = '[a-z]';     // alpha
-        $an  = '[a-z0-9]';  // alphanum
-        $and = "[a-z0-9-$underscore]"; // alphanum | "-"
-        // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
-        $domainlabel = "$an(?:$and*$an)?";
-        // AMENDED as per RFC 3696
-        // toplabel    = alphanum | alphanum *( alphanum | "-" ) alphanum
-        //      side condition: not all numeric
-        $toplabel = "$an(?:$and*$an)?";
-        // hostname    = *( domainlabel "." ) toplabel [ "." ]
-        if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) {
-            if (!ctype_digit($matches[1])) {
-                return $string;
-            }
-        }
-
-        // PHP 5.3 and later support this functionality natively
-        if (function_exists('idn_to_ascii')) {
-            if (defined('IDNA_NONTRANSITIONAL_TO_ASCII') && defined('INTL_IDNA_VARIANT_UTS46')) {
-                $string = idn_to_ascii($string, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
-            } else {
-                $string = idn_to_ascii($string);
-            }
-
-        // If we have Net_IDNA2 support, we can support IRIs by
-        // punycoding them. (This is the most portable thing to do,
-        // since otherwise we have to assume browsers support
-        } elseif ($config->get('Core.EnableIDNA')) {
-            $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
-            // we need to encode each period separately
-            $parts = explode('.', $string);
-            try {
-                $new_parts = array();
-                foreach ($parts as $part) {
-                    $encodable = false;
-                    for ($i = 0, $c = strlen($part); $i < $c; $i++) {
-                        if (ord($part[$i]) > 0x7a) {
-                            $encodable = true;
-                            break;
-                        }
-                    }
-                    if (!$encodable) {
-                        $new_parts[] = $part;
-                    } else {
-                        $new_parts[] = $idna->encode($part);
-                    }
-                }
-                $string = implode('.', $new_parts);
-            } catch (Exception $e) {
-                // XXX error reporting
-            }
-        }
-        // Try again
-        if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
-            return $string;
-        }
-        return false;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php
deleted file mode 100644
index 30ac16c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv4.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * Validates an IPv4 address
- * @author Feyd @ forums.devnetwork.net (public domain)
- */
-class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef
-{
-
-    /**
-     * IPv4 regex, protected so that IPv6 can reuse it.
-     * @type string
-     */
-    protected $ip4;
-
-    /**
-     * @param string $aIP
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($aIP, $config, $context)
-    {
-        if (!$this->ip4) {
-            $this->_loadRegex();
-        }
-
-        if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) {
-            return $aIP;
-        }
-        return false;
-    }
-
-    /**
-     * Lazy load function to prevent regex from being stuffed in
-     * cache.
-     */
-    protected function _loadRegex()
-    {
-        $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
-        $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php
deleted file mode 100644
index f243793..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/URI/IPv6.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-/**
- * Validates an IPv6 address.
- * @author Feyd @ forums.devnetwork.net (public domain)
- * @note This function requires brackets to have been removed from address
- *       in URI.
- */
-class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
-{
-
-    /**
-     * @param string $aIP
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string
-     */
-    public function validate($aIP, $config, $context)
-    {
-        if (!$this->ip4) {
-            $this->_loadRegex();
-        }
-
-        $original = $aIP;
-
-        $hex = '[0-9a-fA-F]';
-        $blk = '(?:' . $hex . '{1,4})';
-        $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128
-
-        //      prefix check
-        if (strpos($aIP, '/') !== false) {
-            if (preg_match('#' . $pre . '$#s', $aIP, $find)) {
-                $aIP = substr($aIP, 0, 0 - strlen($find[0]));
-                unset($find);
-            } else {
-                return false;
-            }
-        }
-
-        //      IPv4-compatiblity check
-        if (preg_match('#(?<=:' . ')' . $this->ip4 . '$#s', $aIP, $find)) {
-            $aIP = substr($aIP, 0, 0 - strlen($find[0]));
-            $ip = explode('.', $find[0]);
-            $ip = array_map('dechex', $ip);
-            $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
-            unset($find, $ip);
-        }
-
-        //      compression check
-        $aIP = explode('::', $aIP);
-        $c = count($aIP);
-        if ($c > 2) {
-            return false;
-        } elseif ($c == 2) {
-            list($first, $second) = $aIP;
-            $first = explode(':', $first);
-            $second = explode(':', $second);
-
-            if (count($first) + count($second) > 8) {
-                return false;
-            }
-
-            while (count($first) < 8) {
-                array_push($first, '0');
-            }
-
-            array_splice($first, 8 - count($second), 8, $second);
-            $aIP = $first;
-            unset($first, $second);
-        } else {
-            $aIP = explode(':', $aIP[0]);
-        }
-        $c = count($aIP);
-
-        if ($c != 8) {
-            return false;
-        }
-
-        //      All the pieces should be 16-bit hex strings. Are they?
-        foreach ($aIP as $piece) {
-            if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) {
-                return false;
-            }
-        }
-        return $original;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php
deleted file mode 100644
index b428331..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-
-/**
- * Processes an entire attribute array for corrections needing multiple values.
- *
- * Occasionally, a certain attribute will need to be removed and popped onto
- * another value.  Instead of creating a complex return syntax for
- * HTMLPurifier_AttrDef, we just pass the whole attribute array to a
- * specialized object and have that do the special work.  That is the
- * family of HTMLPurifier_AttrTransform.
- *
- * An attribute transformation can be assigned to run before or after
- * HTMLPurifier_AttrDef validation.  See HTMLPurifier_HTMLDefinition for
- * more details.
- */
-
-abstract class HTMLPurifier_AttrTransform
-{
-
-    /**
-     * Abstract: makes changes to the attributes dependent on multiple values.
-     *
-     * @param array $attr Assoc array of attributes, usually from
-     *              HTMLPurifier_Token_Tag::$attr
-     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
-     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object
-     * @return array Processed attribute array.
-     */
-    abstract public function transform($attr, $config, $context);
-
-    /**
-     * Prepends CSS properties to the style attribute, creating the
-     * attribute if it doesn't exist.
-     * @param array &$attr Attribute array to process (passed by reference)
-     * @param string $css CSS to prepend
-     */
-    public function prependCSS(&$attr, $css)
-    {
-        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
-        $attr['style'] = $css . $attr['style'];
-    }
-
-    /**
-     * Retrieves and removes an attribute
-     * @param array &$attr Attribute array to process (passed by reference)
-     * @param mixed $key Key of attribute to confiscate
-     * @return mixed
-     */
-    public function confiscateAttr(&$attr, $key)
-    {
-        if (!isset($attr[$key])) {
-            return null;
-        }
-        $value = $attr[$key];
-        unset($attr[$key]);
-        return $value;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php
deleted file mode 100644
index 2f72869..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Background.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes proprietary background attribute to CSS.
- */
-class HTMLPurifier_AttrTransform_Background extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['background'])) {
-            return $attr;
-        }
-
-        $background = $this->confiscateAttr($attr, 'background');
-        // some validation should happen here
-
-        $this->prependCSS($attr, "background-image:url($background);");
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php
deleted file mode 100644
index d66c04a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BdoDir.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-// this MUST be placed in post, as it assumes that any value in dir is valid
-
-/**
- * Post-trasnform that ensures that bdo tags have the dir attribute set.
- */
-class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
-{
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (isset($attr['dir'])) {
-            return $attr;
-        }
-        $attr['dir'] = $config->get('Attr.DefaultTextDir');
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php
deleted file mode 100644
index 0f51fd2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BgColor.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated bgcolor attribute to CSS.
- */
-class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['bgcolor'])) {
-            return $attr;
-        }
-
-        $bgcolor = $this->confiscateAttr($attr, 'bgcolor');
-        // some validation should happen here
-
-        $this->prependCSS($attr, "background-color:$bgcolor;");
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php
deleted file mode 100644
index f25cd01..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/BoolToCSS.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes converts a boolean attribute to fixed CSS
- */
-class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform
-{
-    /**
-     * Name of boolean attribute that is trigger.
-     * @type string
-     */
-    protected $attr;
-
-    /**
-     * CSS declarations to add to style, needs trailing semicolon.
-     * @type string
-     */
-    protected $css;
-
-    /**
-     * @param string $attr attribute name to convert from
-     * @param string $css CSS declarations to add to style (needs semicolon)
-     */
-    public function __construct($attr, $css)
-    {
-        $this->attr = $attr;
-        $this->css = $css;
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr[$this->attr])) {
-            return $attr;
-        }
-        unset($attr[$this->attr]);
-        $this->prependCSS($attr, $this->css);
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php
deleted file mode 100644
index 057dc01..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Border.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated border attribute to CSS.
- */
-class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['border'])) {
-            return $attr;
-        }
-        $border_width = $this->confiscateAttr($attr, 'border');
-        // some validation should happen here
-        $this->prependCSS($attr, "border:{$border_width}px solid;");
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php
deleted file mode 100644
index 7ccd0e3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/EnumToCSS.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * Generic pre-transform that converts an attribute with a fixed number of
- * values (enumerated) to CSS.
- */
-class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform
-{
-    /**
-     * Name of attribute to transform from.
-     * @type string
-     */
-    protected $attr;
-
-    /**
-     * Lookup array of attribute values to CSS.
-     * @type array
-     */
-    protected $enumToCSS = array();
-
-    /**
-     * Case sensitivity of the matching.
-     * @type bool
-     * @warning Currently can only be guaranteed to work with ASCII
-     *          values.
-     */
-    protected $caseSensitive = false;
-
-    /**
-     * @param string $attr Attribute name to transform from
-     * @param array $enum_to_css Lookup array of attribute values to CSS
-     * @param bool $case_sensitive Case sensitivity indicator, default false
-     */
-    public function __construct($attr, $enum_to_css, $case_sensitive = false)
-    {
-        $this->attr = $attr;
-        $this->enumToCSS = $enum_to_css;
-        $this->caseSensitive = (bool)$case_sensitive;
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr[$this->attr])) {
-            return $attr;
-        }
-
-        $value = trim($attr[$this->attr]);
-        unset($attr[$this->attr]);
-
-        if (!$this->caseSensitive) {
-            $value = strtolower($value);
-        }
-
-        if (!isset($this->enumToCSS[$value])) {
-            return $attr;
-        }
-        $this->prependCSS($attr, $this->enumToCSS[$value]);
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php
deleted file mode 100644
index 235ebb3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgRequired.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-// must be called POST validation
-
-/**
- * Transform that supplies default values for the src and alt attributes
- * in img tags, as well as prevents the img tag from being removed
- * because of a missing alt tag. This needs to be registered as both
- * a pre and post attribute transform.
- */
-class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
-{
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        $src = true;
-        if (!isset($attr['src'])) {
-            if ($config->get('Core.RemoveInvalidImg')) {
-                return $attr;
-            }
-            $attr['src'] = $config->get('Attr.DefaultInvalidImage');
-            $src = false;
-        }
-
-        if (!isset($attr['alt'])) {
-            if ($src) {
-                $alt = $config->get('Attr.DefaultImageAlt');
-                if ($alt === null) {
-                    $attr['alt'] = basename($attr['src']);
-                } else {
-                    $attr['alt'] = $alt;
-                }
-            } else {
-                $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
-            }
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php
deleted file mode 100644
index 350b335..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ImgSpace.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated hspace and vspace attributes to CSS
- */
-class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @type string
-     */
-    protected $attr;
-
-    /**
-     * @type array
-     */
-    protected $css = array(
-        'hspace' => array('left', 'right'),
-        'vspace' => array('top', 'bottom')
-    );
-
-    /**
-     * @param string $attr
-     */
-    public function __construct($attr)
-    {
-        $this->attr = $attr;
-        if (!isset($this->css[$attr])) {
-            trigger_error(htmlspecialchars($attr) . ' is not valid space attribute');
-        }
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr[$this->attr])) {
-            return $attr;
-        }
-
-        $width = $this->confiscateAttr($attr, $this->attr);
-        // some validation could happen here
-
-        if (!isset($this->css[$this->attr])) {
-            return $attr;
-        }
-
-        $style = '';
-        foreach ($this->css[$this->attr] as $suffix) {
-            $property = "margin-$suffix";
-            $style .= "$property:{$width}px;";
-        }
-        $this->prependCSS($attr, $style);
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php
deleted file mode 100644
index 3ab47ed..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Input.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Performs miscellaneous cross attribute validation and filtering for
- * input elements. This is meant to be a post-transform.
- */
-class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @type HTMLPurifier_AttrDef_HTML_Pixels
-     */
-    protected $pixels;
-
-    public function __construct()
-    {
-        $this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['type'])) {
-            $t = 'text';
-        } else {
-            $t = strtolower($attr['type']);
-        }
-        if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
-            unset($attr['checked']);
-        }
-        if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
-            unset($attr['maxlength']);
-        }
-        if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
-            $result = $this->pixels->validate($attr['size'], $config, $context);
-            if ($result === false) {
-                unset($attr['size']);
-            } else {
-                $attr['size'] = $result;
-            }
-        }
-        if (isset($attr['src']) && $t !== 'image') {
-            unset($attr['src']);
-        }
-        if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
-            $attr['value'] = '';
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php
deleted file mode 100644
index 5b0aff0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Lang.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- * Post-transform that copies lang's value to xml:lang (and vice-versa)
- * @note Theoretically speaking, this could be a pre-transform, but putting
- *       post is more efficient.
- */
-class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
-{
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        $lang = isset($attr['lang']) ? $attr['lang'] : false;
-        $xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
-
-        if ($lang !== false && $xml_lang === false) {
-            $attr['xml:lang'] = $lang;
-        } elseif ($xml_lang !== false) {
-            $attr['lang'] = $xml_lang;
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php
deleted file mode 100644
index 853f335..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Length.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * Class for handling width/height length attribute transformations to CSS
- */
-class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform
-{
-
-    /**
-     * @type string
-     */
-    protected $name;
-
-    /**
-     * @type string
-     */
-    protected $cssName;
-
-    public function __construct($name, $css_name = null)
-    {
-        $this->name = $name;
-        $this->cssName = $css_name ? $css_name : $name;
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr[$this->name])) {
-            return $attr;
-        }
-        $length = $this->confiscateAttr($attr, $this->name);
-        if (ctype_digit($length)) {
-            $length .= 'px';
-        }
-        $this->prependCSS($attr, $this->cssName . ":$length;");
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php
deleted file mode 100644
index 63cce68..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Name.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * Pre-transform that changes deprecated name attribute to ID if necessary
- */
-class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
-{
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        // Abort early if we're using relaxed definition of name
-        if ($config->get('HTML.Attr.Name.UseCDATA')) {
-            return $attr;
-        }
-        if (!isset($attr['name'])) {
-            return $attr;
-        }
-        $id = $this->confiscateAttr($attr, 'name');
-        if (isset($attr['id'])) {
-            return $attr;
-        }
-        $attr['id'] = $id;
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php
deleted file mode 100644
index 5a1fdbb..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * Post-transform that performs validation to the name attribute; if
- * it is present with an equivalent id attribute, it is passed through;
- * otherwise validation is performed.
- */
-class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
-{
-
-    /**
-     * @type HTMLPurifier_AttrDef_HTML_ID
-     */
-    public $idDef;
-
-    public function __construct()
-    {
-        $this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['name'])) {
-            return $attr;
-        }
-        $name = $attr['name'];
-        if (isset($attr['id']) && $attr['id'] === $name) {
-            return $attr;
-        }
-        $result = $this->idDef->validate($name, $config, $context);
-        if ($result === false) {
-            unset($attr['name']);
-        } else {
-            $attr['name'] = $result;
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php
deleted file mode 100644
index 1057ebe..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Nofollow.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-// must be called POST validation
-
-/**
- * Adds rel="nofollow" to all outbound links.  This transform is
- * only attached if Attr.Nofollow is TRUE.
- */
-class HTMLPurifier_AttrTransform_Nofollow extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @type HTMLPurifier_URIParser
-     */
-    private $parser;
-
-    public function __construct()
-    {
-        $this->parser = new HTMLPurifier_URIParser();
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['href'])) {
-            return $attr;
-        }
-
-        // XXX Kind of inefficient
-        $url = $this->parser->parse($attr['href']);
-        $scheme = $url->getSchemeObj($config, $context);
-
-        if ($scheme->browsable && !$url->isLocal($config, $context)) {
-            if (isset($attr['rel'])) {
-                $rels = explode(' ', $attr['rel']);
-                if (!in_array('nofollow', $rels)) {
-                    $rels[] = 'nofollow';
-                }
-                $attr['rel'] = implode(' ', $rels);
-            } else {
-                $attr['rel'] = 'nofollow';
-            }
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php
deleted file mode 100644
index 231c81a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeEmbed.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-class HTMLPurifier_AttrTransform_SafeEmbed extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @type string
-     */
-    public $name = "SafeEmbed";
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        $attr['allowscriptaccess'] = 'never';
-        $attr['allownetworking'] = 'internal';
-        $attr['type'] = 'application/x-shockwave-flash';
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php
deleted file mode 100644
index d1f3a4d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeObject.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * Writes default type for all objects. Currently only supports flash.
- */
-class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @type string
-     */
-    public $name = "SafeObject";
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['type'])) {
-            $attr['type'] = 'application/x-shockwave-flash';
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php
deleted file mode 100644
index 1033106..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-
-/**
- * Validates name/value pairs in param tags to be used in safe objects. This
- * will only allow name values it recognizes, and pre-fill certain attributes
- * with required values.
- *
- * @note
- *      This class only supports Flash. In the future, Quicktime support
- *      may be added.
- *
- * @warning
- *      This class expects an injector to add the necessary parameters tags.
- */
-class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @type string
-     */
-    public $name = "SafeParam";
-
-    /**
-     * @type HTMLPurifier_AttrDef_URI
-     */
-    private $uri;
-
-    /**
-     * @type HTMLPurifier_AttrDef_Enum
-     */
-    public $wmode;
-
-    public function __construct()
-    {
-        $this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded
-        $this->wmode = new HTMLPurifier_AttrDef_Enum(array('window', 'opaque', 'transparent'));
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        // If we add support for other objects, we'll need to alter the
-        // transforms.
-        switch ($attr['name']) {
-            // application/x-shockwave-flash
-            // Keep this synchronized with Injector/SafeObject.php
-            case 'allowScriptAccess':
-                $attr['value'] = 'never';
-                break;
-            case 'allowNetworking':
-                $attr['value'] = 'internal';
-                break;
-            case 'allowFullScreen':
-                if ($config->get('HTML.FlashAllowFullScreen')) {
-                    $attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false';
-                } else {
-                    $attr['value'] = 'false';
-                }
-                break;
-            case 'wmode':
-                $attr['value'] = $this->wmode->validate($attr['value'], $config, $context);
-                break;
-            case 'movie':
-            case 'src':
-                $attr['name'] = "movie";
-                $attr['value'] = $this->uri->validate($attr['value'], $config, $context);
-                break;
-            case 'flashvars':
-                // we're going to allow arbitrary inputs to the SWF, on
-                // the reasoning that it could only hack the SWF, not us.
-                break;
-            // add other cases to support other param name/value pairs
-            default:
-                $attr['name'] = $attr['value'] = null;
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php
deleted file mode 100644
index b7057bb..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * Implements required attribute stipulation for <script>
- */
-class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['type'])) {
-            $attr['type'] = 'text/javascript';
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php
deleted file mode 100644
index dd63ea8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-// must be called POST validation
-
-/**
- * Adds target="blank" to all outbound links.  This transform is
- * only attached if Attr.TargetBlank is TRUE.  This works regardless
- * of whether or not Attr.AllowedFrameTargets
- */
-class HTMLPurifier_AttrTransform_TargetBlank extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @type HTMLPurifier_URIParser
-     */
-    private $parser;
-
-    public function __construct()
-    {
-        $this->parser = new HTMLPurifier_URIParser();
-    }
-
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (!isset($attr['href'])) {
-            return $attr;
-        }
-
-        // XXX Kind of inefficient
-        $url = $this->parser->parse($attr['href']);
-        $scheme = $url->getSchemeObj($config, $context);
-
-        if ($scheme->browsable && !$url->isBenign($config, $context)) {
-            $attr['target'] = '_blank';
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php
deleted file mode 100644
index 1db3c6c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-// must be called POST validation
-
-/**
- * Adds rel="noopener" to any links which target a different window
- * than the current one.  This is used to prevent malicious websites
- * from silently replacing the original window, which could be used
- * to do phishing.
- * This transform is controlled by %HTML.TargetNoopener.
- */
-class HTMLPurifier_AttrTransform_TargetNoopener extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (isset($attr['rel'])) {
-            $rels = explode(' ', $attr['rel']);
-        } else {
-            $rels = array();
-        }
-        if (isset($attr['target']) && !in_array('noopener', $rels)) {
-            $rels[] = 'noopener';
-        }
-        if (!empty($rels) || isset($attr['rel'])) {
-            $attr['rel'] = implode(' ', $rels);
-        }
-
-        return $attr;
-    }
-}
-
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoreferrer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoreferrer.php
deleted file mode 100644
index 587dc2e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoreferrer.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-// must be called POST validation
-
-/**
- * Adds rel="noreferrer" to any links which target a different window
- * than the current one.  This is used to prevent malicious websites
- * from silently replacing the original window, which could be used
- * to do phishing.
- * This transform is controlled by %HTML.TargetNoreferrer.
- */
-class HTMLPurifier_AttrTransform_TargetNoreferrer extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        if (isset($attr['rel'])) {
-            $rels = explode(' ', $attr['rel']);
-        } else {
-            $rels = array();
-        }
-        if (isset($attr['target']) && !in_array('noreferrer', $rels)) {
-            $rels[] = 'noreferrer';
-        }
-        if (!empty($rels) || isset($attr['rel'])) {
-            $attr['rel'] = implode(' ', $rels);
-        }
-
-        return $attr;
-    }
-}
-
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php
deleted file mode 100644
index 6a9f33a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * Sets height/width defaults for <textarea>
- */
-class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform
-{
-    /**
-     * @param array $attr
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function transform($attr, $config, $context)
-    {
-        // Calculated from Firefox
-        if (!isset($attr['cols'])) {
-            $attr['cols'] = '22';
-        }
-        if (!isset($attr['rows'])) {
-            $attr['rows'] = '3';
-        }
-        return $attr;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php
deleted file mode 100644
index e4429e8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-
-/**
- * Provides lookup array of attribute types to HTMLPurifier_AttrDef objects
- */
-class HTMLPurifier_AttrTypes
-{
-    /**
-     * Lookup array of attribute string identifiers to concrete implementations.
-     * @type HTMLPurifier_AttrDef[]
-     */
-    protected $info = array();
-
-    /**
-     * Constructs the info array, supplying default implementations for attribute
-     * types.
-     */
-    public function __construct()
-    {
-        // XXX This is kind of poor, since we don't actually /clone/
-        // instances; instead, we use the supplied make() attribute. So,
-        // the underlying class must know how to deal with arguments.
-        // With the old implementation of Enum, that ignored its
-        // arguments when handling a make dispatch, the IAlign
-        // definition wouldn't work.
-
-        // pseudo-types, must be instantiated via shorthand
-        $this->info['Enum']    = new HTMLPurifier_AttrDef_Enum();
-        $this->info['Bool']    = new HTMLPurifier_AttrDef_HTML_Bool();
-
-        $this->info['CDATA']    = new HTMLPurifier_AttrDef_Text();
-        $this->info['ID']       = new HTMLPurifier_AttrDef_HTML_ID();
-        $this->info['Length']   = new HTMLPurifier_AttrDef_HTML_Length();
-        $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
-        $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
-        $this->info['Pixels']   = new HTMLPurifier_AttrDef_HTML_Pixels();
-        $this->info['Text']     = new HTMLPurifier_AttrDef_Text();
-        $this->info['URI']      = new HTMLPurifier_AttrDef_URI();
-        $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
-        $this->info['Color']    = new HTMLPurifier_AttrDef_HTML_Color();
-        $this->info['IAlign']   = self::makeEnum('top,middle,bottom,left,right');
-        $this->info['LAlign']   = self::makeEnum('top,bottom,left,right');
-        $this->info['FrameTarget'] = new HTMLPurifier_AttrDef_HTML_FrameTarget();
-        $this->info['ContentEditable'] = new HTMLPurifier_AttrDef_HTML_ContentEditable();
-
-        // unimplemented aliases
-        $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
-        $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
-        $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
-        $this->info['Character'] = new HTMLPurifier_AttrDef_Text();
-
-        // "proprietary" types
-        $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class();
-
-        // number is really a positive integer (one or more digits)
-        // FIXME: ^^ not always, see start and value of list items
-        $this->info['Number']   = new HTMLPurifier_AttrDef_Integer(false, false, true);
-    }
-
-    private static function makeEnum($in)
-    {
-        return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in)));
-    }
-
-    /**
-     * Retrieves a type
-     * @param string $type String type name
-     * @return HTMLPurifier_AttrDef Object AttrDef for type
-     */
-    public function get($type)
-    {
-        // determine if there is any extra info tacked on
-        if (strpos($type, '#') !== false) {
-            list($type, $string) = explode('#', $type, 2);
-        } else {
-            $string = '';
-        }
-
-        if (!isset($this->info[$type])) {
-            trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR);
-            return;
-        }
-        return $this->info[$type]->make($string);
-    }
-
-    /**
-     * Sets a new implementation for a type
-     * @param string $type String type name
-     * @param HTMLPurifier_AttrDef $impl Object AttrDef for type
-     */
-    public function set($type, $impl)
-    {
-        $this->info[$type] = $impl;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php
deleted file mode 100644
index f97dc93..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php
+++ /dev/null
@@ -1,178 +0,0 @@
-<?php
-
-/**
- * Validates the attributes of a token. Doesn't manage required attributes
- * very well. The only reason we factored this out was because RemoveForeignElements
- * also needed it besides ValidateAttributes.
- */
-class HTMLPurifier_AttrValidator
-{
-
-    /**
-     * Validates the attributes of a token, mutating it as necessary.
-     * that has valid tokens
-     * @param HTMLPurifier_Token $token Token to validate.
-     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
-     * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
-     */
-    public function validateToken($token, $config, $context)
-    {
-        $definition = $config->getHTMLDefinition();
-        $e =& $context->get('ErrorCollector', true);
-
-        // initialize IDAccumulator if necessary
-        $ok =& $context->get('IDAccumulator', true);
-        if (!$ok) {
-            $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
-            $context->register('IDAccumulator', $id_accumulator);
-        }
-
-        // initialize CurrentToken if necessary
-        $current_token =& $context->get('CurrentToken', true);
-        if (!$current_token) {
-            $context->register('CurrentToken', $token);
-        }
-
-        if (!$token instanceof HTMLPurifier_Token_Start &&
-            !$token instanceof HTMLPurifier_Token_Empty
-        ) {
-            return;
-        }
-
-        // create alias to global definition array, see also $defs
-        // DEFINITION CALL
-        $d_defs = $definition->info_global_attr;
-
-        // don't update token until the very end, to ensure an atomic update
-        $attr = $token->attr;
-
-        // do global transformations (pre)
-        // nothing currently utilizes this
-        foreach ($definition->info_attr_transform_pre as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) {
-                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-                }
-            }
-        }
-
-        // do local transformations only applicable to this element (pre)
-        // ex. <p align="right"> to <p style="text-align:right;">
-        foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) {
-                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-                }
-            }
-        }
-
-        // create alias to this element's attribute definition array, see
-        // also $d_defs (global attribute definition array)
-        // DEFINITION CALL
-        $defs = $definition->info[$token->name]->attr;
-
-        $attr_key = false;
-        $context->register('CurrentAttr', $attr_key);
-
-        // iterate through all the attribute keypairs
-        // Watch out for name collisions: $key has previously been used
-        foreach ($attr as $attr_key => $value) {
-
-            // call the definition
-            if (isset($defs[$attr_key])) {
-                // there is a local definition defined
-                if ($defs[$attr_key] === false) {
-                    // We've explicitly been told not to allow this element.
-                    // This is usually when there's a global definition
-                    // that must be overridden.
-                    // Theoretically speaking, we could have a
-                    // AttrDef_DenyAll, but this is faster!
-                    $result = false;
-                } else {
-                    // validate according to the element's definition
-                    $result = $defs[$attr_key]->validate(
-                        $value,
-                        $config,
-                        $context
-                    );
-                }
-            } elseif (isset($d_defs[$attr_key])) {
-                // there is a global definition defined, validate according
-                // to the global definition
-                $result = $d_defs[$attr_key]->validate(
-                    $value,
-                    $config,
-                    $context
-                );
-            } else {
-                // system never heard of the attribute? DELETE!
-                $result = false;
-            }
-
-            // put the results into effect
-            if ($result === false || $result === null) {
-                // this is a generic error message that should replaced
-                // with more specific ones when possible
-                if ($e) {
-                    $e->send(E_ERROR, 'AttrValidator: Attribute removed');
-                }
-
-                // remove the attribute
-                unset($attr[$attr_key]);
-            } elseif (is_string($result)) {
-                // generally, if a substitution is happening, there
-                // was some sort of implicit correction going on. We'll
-                // delegate it to the attribute classes to say exactly what.
-
-                // simple substitution
-                $attr[$attr_key] = $result;
-            } else {
-                // nothing happens
-            }
-
-            // we'd also want slightly more complicated substitution
-            // involving an array as the return value,
-            // although we're not sure how colliding attributes would
-            // resolve (certain ones would be completely overriden,
-            // others would prepend themselves).
-        }
-
-        $context->destroy('CurrentAttr');
-
-        // post transforms
-
-        // global (error reporting untested)
-        foreach ($definition->info_attr_transform_post as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) {
-                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-                }
-            }
-        }
-
-        // local (error reporting untested)
-        foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
-            $attr = $transform->transform($o = $attr, $config, $context);
-            if ($e) {
-                if ($attr != $o) {
-                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
-                }
-            }
-        }
-
-        $token->attr = $attr;
-
-        // destroy CurrentToken if we made it ourselves
-        if (!$current_token) {
-            $context->destroy('CurrentToken');
-        }
-
-    }
-
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php
deleted file mode 100644
index 707122b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Bootstrap.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-// constants are slow, so we use as few as possible
-if (!defined('HTMLPURIFIER_PREFIX')) {
-    define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '/..'));
-}
-
-// accomodations for versions earlier than 5.0.2
-// borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
-if (!defined('PHP_EOL')) {
-    switch (strtoupper(substr(PHP_OS, 0, 3))) {
-        case 'WIN':
-            define('PHP_EOL', "\r\n");
-            break;
-        case 'DAR':
-            define('PHP_EOL', "\r");
-            break;
-        default:
-            define('PHP_EOL', "\n");
-    }
-}
-
-/**
- * Bootstrap class that contains meta-functionality for HTML Purifier such as
- * the autoload function.
- *
- * @note
- *      This class may be used without any other files from HTML Purifier.
- */
-class HTMLPurifier_Bootstrap
-{
-
-    /**
-     * Autoload function for HTML Purifier
-     * @param string $class Class to load
-     * @return bool
-     */
-    public static function autoload($class)
-    {
-        $file = HTMLPurifier_Bootstrap::getPath($class);
-        if (!$file) {
-            return false;
-        }
-        // Technically speaking, it should be ok and more efficient to
-        // just do 'require', but Antonio Parraga reports that with
-        // Zend extensions such as Zend debugger and APC, this invariant
-        // may be broken.  Since we have efficient alternatives, pay
-        // the cost here and avoid the bug.
-        require_once HTMLPURIFIER_PREFIX . '/' . $file;
-        return true;
-    }
-
-    /**
-     * Returns the path for a specific class.
-     * @param string $class Class path to get
-     * @return string
-     */
-    public static function getPath($class)
-    {
-        if (strncmp('HTMLPurifier', $class, 12) !== 0) {
-            return false;
-        }
-        // Custom implementations
-        if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
-            $code = str_replace('_', '-', substr($class, 22));
-            $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
-        } else {
-            $file = str_replace('_', '/', $class) . '.php';
-        }
-        if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) {
-            return false;
-        }
-        return $file;
-    }
-
-    /**
-     * "Pre-registers" our autoloader on the SPL stack.
-     */
-    public static function registerAutoload()
-    {
-        $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
-        if (($funcs = spl_autoload_functions()) === false) {
-            spl_autoload_register($autoload);
-        } elseif (function_exists('spl_autoload_unregister')) {
-            if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
-                // prepend flag exists, no need for shenanigans
-                spl_autoload_register($autoload, true, true);
-            } else {
-                $buggy  = version_compare(PHP_VERSION, '5.2.11', '<');
-                $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
-                          version_compare(PHP_VERSION, '5.1.0', '>=');
-                foreach ($funcs as $func) {
-                    if ($buggy && is_array($func)) {
-                        // :TRICKY: There are some compatibility issues and some
-                        // places where we need to error out
-                        $reflector = new ReflectionMethod($func[0], $func[1]);
-                        if (!$reflector->isStatic()) {
-                            throw new Exception(
-                                'HTML Purifier autoloader registrar is not compatible
-                                with non-static object methods due to PHP Bug #44144;
-                                Please do not use HTMLPurifier.autoload.php (or any
-                                file that includes this file); instead, place the code:
-                                spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
-                                after your own autoloaders.'
-                            );
-                        }
-                        // Suprisingly, spl_autoload_register supports the
-                        // Class::staticMethod callback format, although call_user_func doesn't
-                        if ($compat) {
-                            $func = implode('::', $func);
-                        }
-                    }
-                    spl_autoload_unregister($func);
-                }
-                spl_autoload_register($autoload);
-                foreach ($funcs as $func) {
-                    spl_autoload_register($func);
-                }
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php
deleted file mode 100644
index 3f08b81..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/CSSDefinition.php
+++ /dev/null
@@ -1,549 +0,0 @@
-<?php
-
-/**
- * Defines allowed CSS attributes and what their values are.
- * @see HTMLPurifier_HTMLDefinition
- */
-class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
-{
-
-    public $type = 'CSS';
-
-    /**
-     * Assoc array of attribute name to definition object.
-     * @type HTMLPurifier_AttrDef[]
-     */
-    public $info = array();
-
-    /**
-     * Constructs the info array.  The meat of this class.
-     * @param HTMLPurifier_Config $config
-     */
-    protected function doSetup($config)
-    {
-        $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
-            array('left', 'right', 'center', 'justify'),
-            false
-        );
-
-        $border_style =
-            $this->info['border-bottom-style'] =
-            $this->info['border-right-style'] =
-            $this->info['border-left-style'] =
-            $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
-                array(
-                    'none',
-                    'hidden',
-                    'dotted',
-                    'dashed',
-                    'solid',
-                    'double',
-                    'groove',
-                    'ridge',
-                    'inset',
-                    'outset'
-                ),
-                false
-            );
-
-        $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
-
-        $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
-            array('none', 'left', 'right', 'both'),
-            false
-        );
-        $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
-            array('none', 'left', 'right'),
-            false
-        );
-        $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
-            array('normal', 'italic', 'oblique'),
-            false
-        );
-        $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
-            array('normal', 'small-caps'),
-            false
-        );
-
-        $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(array('none')),
-                new HTMLPurifier_AttrDef_CSS_URI()
-            )
-        );
-
-        $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
-            array('inside', 'outside'),
-            false
-        );
-        $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
-            array(
-                'disc',
-                'circle',
-                'square',
-                'decimal',
-                'lower-roman',
-                'upper-roman',
-                'lower-alpha',
-                'upper-alpha',
-                'none'
-            ),
-            false
-        );
-        $this->info['list-style-image'] = $uri_or_none;
-
-        $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
-
-        $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
-            array('capitalize', 'uppercase', 'lowercase', 'none'),
-            false
-        );
-        $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
-
-        $this->info['background-image'] = $uri_or_none;
-        $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
-            array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
-        );
-        $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
-            array('scroll', 'fixed')
-        );
-        $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
-
-        $this->info['background-size'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(
-                    array(
-                        'auto',
-                        'cover',
-                        'contain',
-                        'initial',
-                        'inherit',
-                    )
-                ),
-                new HTMLPurifier_AttrDef_CSS_Percentage(),
-                new HTMLPurifier_AttrDef_CSS_Length()
-            )
-        );
-
-        $border_color =
-            $this->info['border-top-color'] =
-            $this->info['border-bottom-color'] =
-            $this->info['border-left-color'] =
-            $this->info['border-right-color'] =
-            $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(
-                array(
-                    new HTMLPurifier_AttrDef_Enum(array('transparent')),
-                    new HTMLPurifier_AttrDef_CSS_Color()
-                )
-            );
-
-        $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
-
-        $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
-
-        $border_width =
-            $this->info['border-top-width'] =
-            $this->info['border-bottom-width'] =
-            $this->info['border-left-width'] =
-            $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(
-                array(
-                    new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
-                    new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
-                )
-            );
-
-        $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
-
-        $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(array('normal')),
-                new HTMLPurifier_AttrDef_CSS_Length()
-            )
-        );
-
-        $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(array('normal')),
-                new HTMLPurifier_AttrDef_CSS_Length()
-            )
-        );
-
-        $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(
-                    array(
-                        'xx-small',
-                        'x-small',
-                        'small',
-                        'medium',
-                        'large',
-                        'x-large',
-                        'xx-large',
-                        'larger',
-                        'smaller'
-                    )
-                ),
-                new HTMLPurifier_AttrDef_CSS_Percentage(),
-                new HTMLPurifier_AttrDef_CSS_Length()
-            )
-        );
-
-        $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(array('normal')),
-                new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
-                new HTMLPurifier_AttrDef_CSS_Length('0'),
-                new HTMLPurifier_AttrDef_CSS_Percentage(true)
-            )
-        );
-
-        $margin =
-            $this->info['margin-top'] =
-            $this->info['margin-bottom'] =
-            $this->info['margin-left'] =
-            $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
-                array(
-                    new HTMLPurifier_AttrDef_CSS_Length(),
-                    new HTMLPurifier_AttrDef_CSS_Percentage(),
-                    new HTMLPurifier_AttrDef_Enum(array('auto'))
-                )
-            );
-
-        $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
-
-        // non-negative
-        $padding =
-            $this->info['padding-top'] =
-            $this->info['padding-bottom'] =
-            $this->info['padding-left'] =
-            $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
-                array(
-                    new HTMLPurifier_AttrDef_CSS_Length('0'),
-                    new HTMLPurifier_AttrDef_CSS_Percentage(true)
-                )
-            );
-
-        $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
-
-        $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_CSS_Length(),
-                new HTMLPurifier_AttrDef_CSS_Percentage()
-            )
-        );
-
-        $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_CSS_Length('0'),
-                new HTMLPurifier_AttrDef_CSS_Percentage(true),
-                new HTMLPurifier_AttrDef_Enum(array('auto', 'initial', 'inherit'))
-            )
-        );
-        $trusted_min_wh = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_CSS_Length('0'),
-                new HTMLPurifier_AttrDef_CSS_Percentage(true),
-                new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit'))
-            )
-        );
-        $trusted_max_wh = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_CSS_Length('0'),
-                new HTMLPurifier_AttrDef_CSS_Percentage(true),
-                new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit'))
-            )
-        );
-        $max = $config->get('CSS.MaxImgLength');
-
-        $this->info['width'] =
-        $this->info['height'] =
-            $max === null ?
-                $trusted_wh :
-                new HTMLPurifier_AttrDef_Switch(
-                    'img',
-                    // For img tags:
-                    new HTMLPurifier_AttrDef_CSS_Composite(
-                        array(
-                            new HTMLPurifier_AttrDef_CSS_Length('0', $max),
-                            new HTMLPurifier_AttrDef_Enum(array('auto'))
-                        )
-                    ),
-                    // For everyone else:
-                    $trusted_wh
-                );
-        $this->info['min-width'] =
-        $this->info['min-height'] =
-            $max === null ?
-                $trusted_min_wh :
-                new HTMLPurifier_AttrDef_Switch(
-                    'img',
-                    // For img tags:
-                    new HTMLPurifier_AttrDef_CSS_Composite(
-                        array(
-                            new HTMLPurifier_AttrDef_CSS_Length('0', $max),
-                            new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit'))
-                        )
-                    ),
-                    // For everyone else:
-                    $trusted_min_wh
-                );
-        $this->info['max-width'] =
-        $this->info['max-height'] =
-            $max === null ?
-                $trusted_max_wh :
-                new HTMLPurifier_AttrDef_Switch(
-                    'img',
-                    // For img tags:
-                    new HTMLPurifier_AttrDef_CSS_Composite(
-                        array(
-                            new HTMLPurifier_AttrDef_CSS_Length('0', $max),
-                            new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit'))
-                        )
-                    ),
-                    // For everyone else:
-                    $trusted_max_wh
-                );
-
-        $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
-
-        $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
-
-        // this could use specialized code
-        $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
-            array(
-                'normal',
-                'bold',
-                'bolder',
-                'lighter',
-                '100',
-                '200',
-                '300',
-                '400',
-                '500',
-                '600',
-                '700',
-                '800',
-                '900'
-            ),
-            false
-        );
-
-        // MUST be called after other font properties, as it references
-        // a CSSDefinition object
-        $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
-
-        // same here
-        $this->info['border'] =
-        $this->info['border-bottom'] =
-        $this->info['border-top'] =
-        $this->info['border-left'] =
-        $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
-
-        $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(
-            array('collapse', 'separate')
-        );
-
-        $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(
-            array('top', 'bottom')
-        );
-
-        $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(
-            array('auto', 'fixed')
-        );
-
-        $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Enum(
-                    array(
-                        'baseline',
-                        'sub',
-                        'super',
-                        'top',
-                        'text-top',
-                        'middle',
-                        'bottom',
-                        'text-bottom'
-                    )
-                ),
-                new HTMLPurifier_AttrDef_CSS_Length(),
-                new HTMLPurifier_AttrDef_CSS_Percentage()
-            )
-        );
-
-        $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
-
-        // These CSS properties don't work on many browsers, but we live
-        // in THE FUTURE!
-        $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(
-            array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')
-        );
-
-        if ($config->get('CSS.Proprietary')) {
-            $this->doSetupProprietary($config);
-        }
-
-        if ($config->get('CSS.AllowTricky')) {
-            $this->doSetupTricky($config);
-        }
-
-        if ($config->get('CSS.Trusted')) {
-            $this->doSetupTrusted($config);
-        }
-
-        $allow_important = $config->get('CSS.AllowImportant');
-        // wrap all attr-defs with decorator that handles !important
-        foreach ($this->info as $k => $v) {
-            $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
-        }
-
-        $this->setupConfigStuff($config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    protected function doSetupProprietary($config)
-    {
-        // Internet Explorer only scrollbar colors
-        $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
-        $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
-
-        // vendor specific prefixes of opacity
-        $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
-        $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
-
-        // only opacity, for now
-        $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
-
-        // more CSS3
-        $this->info['page-break-after'] =
-        $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(
-            array(
-                'auto',
-                'always',
-                'avoid',
-                'left',
-                'right'
-            )
-        );
-        $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
-
-        $border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
-                new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
-            ));
-
-        $this->info['border-top-left-radius'] =
-        $this->info['border-top-right-radius'] =
-        $this->info['border-bottom-right-radius'] =
-        $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
-        // TODO: support SLASH syntax
-        $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
-
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    protected function doSetupTricky($config)
-    {
-        $this->info['display'] = new HTMLPurifier_AttrDef_Enum(
-            array(
-                'inline',
-                'block',
-                'list-item',
-                'run-in',
-                'compact',
-                'marker',
-                'table',
-                'inline-block',
-                'inline-table',
-                'table-row-group',
-                'table-header-group',
-                'table-footer-group',
-                'table-row',
-                'table-column-group',
-                'table-column',
-                'table-cell',
-                'table-caption',
-                'none'
-            )
-        );
-        $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(
-            array('visible', 'hidden', 'collapse')
-        );
-        $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
-        $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    protected function doSetupTrusted($config)
-    {
-        $this->info['position'] = new HTMLPurifier_AttrDef_Enum(
-            array('static', 'relative', 'absolute', 'fixed')
-        );
-        $this->info['top'] =
-        $this->info['left'] =
-        $this->info['right'] =
-        $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_CSS_Length(),
-                new HTMLPurifier_AttrDef_CSS_Percentage(),
-                new HTMLPurifier_AttrDef_Enum(array('auto')),
-            )
-        );
-        $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(
-            array(
-                new HTMLPurifier_AttrDef_Integer(),
-                new HTMLPurifier_AttrDef_Enum(array('auto')),
-            )
-        );
-    }
-
-    /**
-     * Performs extra config-based processing. Based off of
-     * HTMLPurifier_HTMLDefinition.
-     * @param HTMLPurifier_Config $config
-     * @todo Refactor duplicate elements into common class (probably using
-     *       composition, not inheritance).
-     */
-    protected function setupConfigStuff($config)
-    {
-        // setup allowed elements
-        $support = "(for information on implementing this, see the " .
-            "support forums) ";
-        $allowed_properties = $config->get('CSS.AllowedProperties');
-        if ($allowed_properties !== null) {
-            foreach ($this->info as $name => $d) {
-                if (!isset($allowed_properties[$name])) {
-                    unset($this->info[$name]);
-                }
-                unset($allowed_properties[$name]);
-            }
-            // emit errors
-            foreach ($allowed_properties as $name => $d) {
-                // :TODO: Is this htmlspecialchars() call really necessary?
-                $name = htmlspecialchars($name);
-                trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
-            }
-        }
-
-        $forbidden_properties = $config->get('CSS.ForbiddenProperties');
-        if ($forbidden_properties !== null) {
-            foreach ($this->info as $name => $d) {
-                if (isset($forbidden_properties[$name])) {
-                    unset($this->info[$name]);
-                }
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php
deleted file mode 100644
index 8eb17b8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * Defines allowed child nodes and validates nodes against it.
- */
-abstract class HTMLPurifier_ChildDef
-{
-    /**
-     * Type of child definition, usually right-most part of class name lowercase.
-     * Used occasionally in terms of context.
-     * @type string
-     */
-    public $type;
-
-    /**
-     * Indicates whether or not an empty array of children is okay.
-     *
-     * This is necessary for redundant checking when changes affecting
-     * a child node may cause a parent node to now be disallowed.
-     * @type bool
-     */
-    public $allow_empty;
-
-    /**
-     * Lookup array of all elements that this definition could possibly allow.
-     * @type array
-     */
-    public $elements = array();
-
-    /**
-     * Get lookup of tag names that should not close this element automatically.
-     * All other elements will do so.
-     * @param HTMLPurifier_Config $config HTMLPurifier_Config object
-     * @return array
-     */
-    public function getAllowedElements($config)
-    {
-        return $this->elements;
-    }
-
-    /**
-     * Validates nodes according to definition and returns modification.
-     *
-     * @param HTMLPurifier_Node[] $children Array of HTMLPurifier_Node
-     * @param HTMLPurifier_Config $config HTMLPurifier_Config object
-     * @param HTMLPurifier_Context $context HTMLPurifier_Context object
-     * @return bool|array true to leave nodes as is, false to remove parent node, array of replacement children
-     */
-    abstract public function validateChildren($children, $config, $context);
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php
deleted file mode 100644
index 7439be2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Chameleon.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * Definition that uses different definitions depending on context.
- *
- * The del and ins tags are notable because they allow different types of
- * elements depending on whether or not they're in a block or inline context.
- * Chameleon allows this behavior to happen by using two different
- * definitions depending on context.  While this somewhat generalized,
- * it is specifically intended for those two tags.
- */
-class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
-{
-
-    /**
-     * Instance of the definition object to use when inline. Usually stricter.
-     * @type HTMLPurifier_ChildDef_Optional
-     */
-    public $inline;
-
-    /**
-     * Instance of the definition object to use when block.
-     * @type HTMLPurifier_ChildDef_Optional
-     */
-    public $block;
-
-    /**
-     * @type string
-     */
-    public $type = 'chameleon';
-
-    /**
-     * @param array $inline List of elements to allow when inline.
-     * @param array $block List of elements to allow when block.
-     */
-    public function __construct($inline, $block)
-    {
-        $this->inline = new HTMLPurifier_ChildDef_Optional($inline);
-        $this->block = new HTMLPurifier_ChildDef_Optional($block);
-        $this->elements = $this->block->elements;
-    }
-
-    /**
-     * @param HTMLPurifier_Node[] $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        if ($context->get('IsInline') === false) {
-            return $this->block->validateChildren(
-                $children,
-                $config,
-                $context
-            );
-        } else {
-            return $this->inline->validateChildren(
-                $children,
-                $config,
-                $context
-            );
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php
deleted file mode 100644
index f515888..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Custom.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-/**
- * Custom validation class, accepts DTD child definitions
- *
- * @warning Currently this class is an all or nothing proposition, that is,
- *          it will only give a bool return value.
- */
-class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
-{
-    /**
-     * @type string
-     */
-    public $type = 'custom';
-
-    /**
-     * @type bool
-     */
-    public $allow_empty = false;
-
-    /**
-     * Allowed child pattern as defined by the DTD.
-     * @type string
-     */
-    public $dtd_regex;
-
-    /**
-     * PCRE regex derived from $dtd_regex.
-     * @type string
-     */
-    private $_pcre_regex;
-
-    /**
-     * @param $dtd_regex Allowed child pattern from the DTD
-     */
-    public function __construct($dtd_regex)
-    {
-        $this->dtd_regex = $dtd_regex;
-        $this->_compileRegex();
-    }
-
-    /**
-     * Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex)
-     */
-    protected function _compileRegex()
-    {
-        $raw = str_replace(' ', '', $this->dtd_regex);
-        if ($raw[0] != '(') {
-            $raw = "($raw)";
-        }
-        $el = '[#a-zA-Z0-9_.-]+';
-        $reg = $raw;
-
-        // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M
-        // DOING! Seriously: if there's problems, please report them.
-
-        // collect all elements into the $elements array
-        preg_match_all("/$el/", $reg, $matches);
-        foreach ($matches[0] as $match) {
-            $this->elements[$match] = true;
-        }
-
-        // setup all elements as parentheticals with leading commas
-        $reg = preg_replace("/$el/", '(,\\0)', $reg);
-
-        // remove commas when they were not solicited
-        $reg = preg_replace("/([^,(|]\(+),/", '\\1', $reg);
-
-        // remove all non-paranthetical commas: they are handled by first regex
-        $reg = preg_replace("/,\(/", '(', $reg);
-
-        $this->_pcre_regex = $reg;
-    }
-
-    /**
-     * @param HTMLPurifier_Node[] $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        $list_of_children = '';
-        $nesting = 0; // depth into the nest
-        foreach ($children as $node) {
-            if (!empty($node->is_whitespace)) {
-                continue;
-            }
-            $list_of_children .= $node->name . ',';
-        }
-        // add leading comma to deal with stray comma declarations
-        $list_of_children = ',' . rtrim($list_of_children, ',');
-        $okay =
-            preg_match(
-                '/^,?' . $this->_pcre_regex . '$/',
-                $list_of_children
-            );
-        return (bool)$okay;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php
deleted file mode 100644
index a8a6cbd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Empty.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * Definition that disallows all elements.
- * @warning validateChildren() in this class is actually never called, because
- *          empty elements are corrected in HTMLPurifier_Strategy_MakeWellFormed
- *          before child definitions are parsed in earnest by
- *          HTMLPurifier_Strategy_FixNesting.
- */
-class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
-{
-    /**
-     * @type bool
-     */
-    public $allow_empty = true;
-
-    /**
-     * @type string
-     */
-    public $type = 'empty';
-
-    public function __construct()
-    {
-    }
-
-    /**
-     * @param HTMLPurifier_Node[] $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        return array();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php
deleted file mode 100644
index 3d584e7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/List.php
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-
-/**
- * Definition for list containers ul and ol.
- *
- * What does this do?  The big thing is to handle ol/ul at the top
- * level of list nodes, which should be handled specially by /folding/
- * them into the previous list node.  We generally shouldn't ever
- * see other disallowed elements, because the autoclose behavior
- * in MakeWellFormed handles it.
- */
-class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
-{
-    /**
-     * @type string
-     */
-    public $type = 'list';
-    /**
-     * @type array
-     */
-    // lying a little bit, so that we can handle ul and ol ourselves
-    // XXX: This whole business with 'wrap' is all a bit unsatisfactory
-    public $elements = array('li' => true, 'ul' => true, 'ol' => true);
-
-    public $whitespace;
-
-    /**
-     * @param array $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        // Flag for subclasses
-        $this->whitespace = false;
-
-        // if there are no tokens, delete parent node
-        if (empty($children)) {
-            return false;
-        }
-
-        // if li is not allowed, delete parent node
-        if (!isset($config->getHTMLDefinition()->info['li'])) {
-            trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING);
-            return false;
-        }
-
-        // the new set of children
-        $result = array();
-
-        // a little sanity check to make sure it's not ALL whitespace
-        $all_whitespace = true;
-
-        $current_li = null;
-
-        foreach ($children as $node) {
-            if (!empty($node->is_whitespace)) {
-                $result[] = $node;
-                continue;
-            }
-            $all_whitespace = false; // phew, we're not talking about whitespace
-
-            if ($node->name === 'li') {
-                // good
-                $current_li = $node;
-                $result[] = $node;
-            } else {
-                // we want to tuck this into the previous li
-                // Invariant: we expect the node to be ol/ul
-                // ToDo: Make this more robust in the case of not ol/ul
-                // by distinguishing between existing li and li created
-                // to handle non-list elements; non-list elements should
-                // not be appended to an existing li; only li created
-                // for non-list. This distinction is not currently made.
-                if ($current_li === null) {
-                    $current_li = new HTMLPurifier_Node_Element('li');
-                    $result[] = $current_li;
-                }
-                $current_li->children[] = $node;
-                $current_li->empty = false; // XXX fascinating! Check for this error elsewhere ToDo
-            }
-        }
-        if (empty($result)) {
-            return false;
-        }
-        if ($all_whitespace) {
-            return false;
-        }
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php
deleted file mode 100644
index b946806..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Optional.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * Definition that allows a set of elements, and allows no children.
- * @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required,
- *       really, one shouldn't inherit from the other.  Only altered behavior
- *       is to overload a returned false with an array.  Thus, it will never
- *       return false.
- */
-class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
-{
-    /**
-     * @type bool
-     */
-    public $allow_empty = true;
-
-    /**
-     * @type string
-     */
-    public $type = 'optional';
-
-    /**
-     * @param array $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        $result = parent::validateChildren($children, $config, $context);
-        // we assume that $children is not modified
-        if ($result === false) {
-            if (empty($children)) {
-                return true;
-            } elseif ($this->whitespace) {
-                return $children;
-            } else {
-                return array();
-            }
-        }
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php
deleted file mode 100644
index 0d1c8f5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Required.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-
-/**
- * Definition that allows a set of elements, but disallows empty children.
- */
-class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
-{
-    /**
-     * Lookup table of allowed elements.
-     * @type array
-     */
-    public $elements = array();
-
-    /**
-     * Whether or not the last passed node was all whitespace.
-     * @type bool
-     */
-    protected $whitespace = false;
-
-    /**
-     * @param array|string $elements List of allowed element names (lowercase).
-     */
-    public function __construct($elements)
-    {
-        if (is_string($elements)) {
-            $elements = str_replace(' ', '', $elements);
-            $elements = explode('|', $elements);
-        }
-        $keys = array_keys($elements);
-        if ($keys == array_keys($keys)) {
-            $elements = array_flip($elements);
-            foreach ($elements as $i => $x) {
-                $elements[$i] = true;
-                if (empty($i)) {
-                    unset($elements[$i]);
-                } // remove blank
-            }
-        }
-        $this->elements = $elements;
-    }
-
-    /**
-     * @type bool
-     */
-    public $allow_empty = false;
-
-    /**
-     * @type string
-     */
-    public $type = 'required';
-
-    /**
-     * @param array $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        // Flag for subclasses
-        $this->whitespace = false;
-
-        // if there are no tokens, delete parent node
-        if (empty($children)) {
-            return false;
-        }
-
-        // the new set of children
-        $result = array();
-
-        // whether or not parsed character data is allowed
-        // this controls whether or not we silently drop a tag
-        // or generate escaped HTML from it
-        $pcdata_allowed = isset($this->elements['#PCDATA']);
-
-        // a little sanity check to make sure it's not ALL whitespace
-        $all_whitespace = true;
-
-        $stack = array_reverse($children);
-        while (!empty($stack)) {
-            $node = array_pop($stack);
-            if (!empty($node->is_whitespace)) {
-                $result[] = $node;
-                continue;
-            }
-            $all_whitespace = false; // phew, we're not talking about whitespace
-
-            if (!isset($this->elements[$node->name])) {
-                // special case text
-                // XXX One of these ought to be redundant or something
-                if ($pcdata_allowed && $node instanceof HTMLPurifier_Node_Text) {
-                    $result[] = $node;
-                    continue;
-                }
-                // spill the child contents in
-                // ToDo: Make configurable
-                if ($node instanceof HTMLPurifier_Node_Element) {
-                    for ($i = count($node->children) - 1; $i >= 0; $i--) {
-                        $stack[] = $node->children[$i];
-                    }
-                    continue;
-                }
-                continue;
-            }
-            $result[] = $node;
-        }
-        if (empty($result)) {
-            return false;
-        }
-        if ($all_whitespace) {
-            $this->whitespace = true;
-            return false;
-        }
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php
deleted file mode 100644
index 3270a46..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/StrictBlockquote.php
+++ /dev/null
@@ -1,110 +0,0 @@
-<?php
-
-/**
- * Takes the contents of blockquote when in strict and reformats for validation.
- */
-class HTMLPurifier_ChildDef_StrictBlockquote extends HTMLPurifier_ChildDef_Required
-{
-    /**
-     * @type array
-     */
-    protected $real_elements;
-
-    /**
-     * @type array
-     */
-    protected $fake_elements;
-
-    /**
-     * @type bool
-     */
-    public $allow_empty = true;
-
-    /**
-     * @type string
-     */
-    public $type = 'strictblockquote';
-
-    /**
-     * @type bool
-     */
-    protected $init = false;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return array
-     * @note We don't want MakeWellFormed to auto-close inline elements since
-     *       they might be allowed.
-     */
-    public function getAllowedElements($config)
-    {
-        $this->init($config);
-        return $this->fake_elements;
-    }
-
-    /**
-     * @param array $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        $this->init($config);
-
-        // trick the parent class into thinking it allows more
-        $this->elements = $this->fake_elements;
-        $result = parent::validateChildren($children, $config, $context);
-        $this->elements = $this->real_elements;
-
-        if ($result === false) {
-            return array();
-        }
-        if ($result === true) {
-            $result = $children;
-        }
-
-        $def = $config->getHTMLDefinition();
-        $block_wrap_name = $def->info_block_wrapper;
-        $block_wrap = false;
-        $ret = array();
-
-        foreach ($result as $node) {
-            if ($block_wrap === false) {
-                if (($node instanceof HTMLPurifier_Node_Text && !$node->is_whitespace) ||
-                    ($node instanceof HTMLPurifier_Node_Element && !isset($this->elements[$node->name]))) {
-                        $block_wrap = new HTMLPurifier_Node_Element($def->info_block_wrapper);
-                        $ret[] = $block_wrap;
-                }
-            } else {
-                if ($node instanceof HTMLPurifier_Node_Element && isset($this->elements[$node->name])) {
-                    $block_wrap = false;
-
-                }
-            }
-            if ($block_wrap) {
-                $block_wrap->children[] = $node;
-            } else {
-                $ret[] = $node;
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    private function init($config)
-    {
-        if (!$this->init) {
-            $def = $config->getHTMLDefinition();
-            // allow all inline elements
-            $this->real_elements = $this->elements;
-            $this->fake_elements = $def->info_content_sets['Flow'];
-            $this->fake_elements['#PCDATA'] = true;
-            $this->init = true;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php
deleted file mode 100644
index 67c7e95..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php
+++ /dev/null
@@ -1,224 +0,0 @@
-<?php
-
-/**
- * Definition for tables.  The general idea is to extract out all of the
- * essential bits, and then reconstruct it later.
- *
- * This is a bit confusing, because the DTDs and the W3C
- * validators seem to disagree on the appropriate definition. The
- * DTD claims:
- *
- *      (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)
- *
- * But actually, the HTML4 spec then has this to say:
- *
- *      The TBODY start tag is always required except when the table
- *      contains only one table body and no table head or foot sections.
- *      The TBODY end tag may always be safely omitted.
- *
- * So the DTD is kind of wrong.  The validator is, unfortunately, kind
- * of on crack.
- *
- * The definition changed again in XHTML1.1; and in my opinion, this
- * formulation makes the most sense.
- *
- *      caption?, ( col* | colgroup* ), (( thead?, tfoot?, tbody+ ) | ( tr+ ))
- *
- * Essentially, we have two modes: thead/tfoot/tbody mode, and tr mode.
- * If we encounter a thead, tfoot or tbody, we are placed in the former
- * mode, and we *must* wrap any stray tr segments with a tbody. But if
- * we don't run into any of them, just have tr tags is OK.
- */
-class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
-{
-    /**
-     * @type bool
-     */
-    public $allow_empty = false;
-
-    /**
-     * @type string
-     */
-    public $type = 'table';
-
-    /**
-     * @type array
-     */
-    public $elements = array(
-        'tr' => true,
-        'tbody' => true,
-        'thead' => true,
-        'tfoot' => true,
-        'caption' => true,
-        'colgroup' => true,
-        'col' => true
-    );
-
-    public function __construct()
-    {
-    }
-
-    /**
-     * @param array $children
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array
-     */
-    public function validateChildren($children, $config, $context)
-    {
-        if (empty($children)) {
-            return false;
-        }
-
-        // only one of these elements is allowed in a table
-        $caption = false;
-        $thead = false;
-        $tfoot = false;
-
-        // whitespace
-        $initial_ws = array();
-        $after_caption_ws = array();
-        $after_thead_ws = array();
-        $after_tfoot_ws = array();
-
-        // as many of these as you want
-        $cols = array();
-        $content = array();
-
-        $tbody_mode = false; // if true, then we need to wrap any stray
-                             // <tr>s with a <tbody>.
-
-        $ws_accum =& $initial_ws;
-
-        foreach ($children as $node) {
-            if ($node instanceof HTMLPurifier_Node_Comment) {
-                $ws_accum[] = $node;
-                continue;
-            }
-            switch ($node->name) {
-            case 'tbody':
-                $tbody_mode = true;
-                // fall through
-            case 'tr':
-                $content[] = $node;
-                $ws_accum =& $content;
-                break;
-            case 'caption':
-                // there can only be one caption!
-                if ($caption !== false)  break;
-                $caption = $node;
-                $ws_accum =& $after_caption_ws;
-                break;
-            case 'thead':
-                $tbody_mode = true;
-                // XXX This breaks rendering properties with
-                // Firefox, which never floats a <thead> to
-                // the top. Ever. (Our scheme will float the
-                // first <thead> to the top.)  So maybe
-                // <thead>s that are not first should be
-                // turned into <tbody>? Very tricky, indeed.
-                if ($thead === false) {
-                    $thead = $node;
-                    $ws_accum =& $after_thead_ws;
-                } else {
-                    // Oops, there's a second one! What
-                    // should we do?  Current behavior is to
-                    // transmutate the first and last entries into
-                    // tbody tags, and then put into content.
-                    // Maybe a better idea is to *attach
-                    // it* to the existing thead or tfoot?
-                    // We don't do this, because Firefox
-                    // doesn't float an extra tfoot to the
-                    // bottom like it does for the first one.
-                    $node->name = 'tbody';
-                    $content[] = $node;
-                    $ws_accum =& $content;
-                }
-                break;
-            case 'tfoot':
-                // see above for some aveats
-                $tbody_mode = true;
-                if ($tfoot === false) {
-                    $tfoot = $node;
-                    $ws_accum =& $after_tfoot_ws;
-                } else {
-                    $node->name = 'tbody';
-                    $content[] = $node;
-                    $ws_accum =& $content;
-                }
-                break;
-            case 'colgroup':
-            case 'col':
-                $cols[] = $node;
-                $ws_accum =& $cols;
-                break;
-            case '#PCDATA':
-                // How is whitespace handled? We treat is as sticky to
-                // the *end* of the previous element. So all of the
-                // nonsense we have worked on is to keep things
-                // together.
-                if (!empty($node->is_whitespace)) {
-                    $ws_accum[] = $node;
-                }
-                break;
-            }
-        }
-
-        if (empty($content) && $thead === false && $tfoot === false) {
-            return false;
-        }
-
-        $ret = $initial_ws;
-        if ($caption !== false) {
-            $ret[] = $caption;
-            $ret = array_merge($ret, $after_caption_ws);
-        }
-        if ($cols !== false) {
-            $ret = array_merge($ret, $cols);
-        }
-        if ($thead !== false) {
-            $ret[] = $thead;
-            $ret = array_merge($ret, $after_thead_ws);
-        }
-        if ($tfoot !== false) {
-            $ret[] = $tfoot;
-            $ret = array_merge($ret, $after_tfoot_ws);
-        }
-
-        if ($tbody_mode) {
-            // we have to shuffle tr into tbody
-            $current_tr_tbody = null;
-
-            foreach($content as $node) {
-                switch ($node->name) {
-                case 'tbody':
-                    $current_tr_tbody = null;
-                    $ret[] = $node;
-                    break;
-                case 'tr':
-                    if ($current_tr_tbody === null) {
-                        $current_tr_tbody = new HTMLPurifier_Node_Element('tbody');
-                        $ret[] = $current_tr_tbody;
-                    }
-                    $current_tr_tbody->children[] = $node;
-                    break;
-                case '#PCDATA':
-                    //assert($node->is_whitespace);
-                    if ($current_tr_tbody === null) {
-                        $ret[] = $node;
-                    } else {
-                        $current_tr_tbody->children[] = $node;
-                    }
-                    break;
-                }
-            }
-        } else {
-            $ret = array_merge($ret, $content);
-        }
-
-        return $ret;
-
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Config.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Config.php
deleted file mode 100644
index 797d268..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Config.php
+++ /dev/null
@@ -1,920 +0,0 @@
-<?php
-
-/**
- * Configuration object that triggers customizable behavior.
- *
- * @warning This class is strongly defined: that means that the class
- *          will fail if an undefined directive is retrieved or set.
- *
- * @note Many classes that could (although many times don't) use the
- *       configuration object make it a mandatory parameter.  This is
- *       because a configuration object should always be forwarded,
- *       otherwise, you run the risk of missing a parameter and then
- *       being stumped when a configuration directive doesn't work.
- *
- * @todo Reconsider some of the public member variables
- */
-class HTMLPurifier_Config
-{
-
-    /**
-     * HTML Purifier's version
-     * @type string
-     */
-    public $version = '4.15.0';
-
-    /**
-     * Whether or not to automatically finalize
-     * the object if a read operation is done.
-     * @type bool
-     */
-    public $autoFinalize = true;
-
-    // protected member variables
-
-    /**
-     * Namespace indexed array of serials for specific namespaces.
-     * @see getSerial() for more info.
-     * @type string[]
-     */
-    protected $serials = array();
-
-    /**
-     * Serial for entire configuration object.
-     * @type string
-     */
-    protected $serial;
-
-    /**
-     * Parser for variables.
-     * @type HTMLPurifier_VarParser_Flexible
-     */
-    protected $parser = null;
-
-    /**
-     * Reference HTMLPurifier_ConfigSchema for value checking.
-     * @type HTMLPurifier_ConfigSchema
-     * @note This is public for introspective purposes. Please don't
-     *       abuse!
-     */
-    public $def;
-
-    /**
-     * Indexed array of definitions.
-     * @type HTMLPurifier_Definition[]
-     */
-    protected $definitions;
-
-    /**
-     * Whether or not config is finalized.
-     * @type bool
-     */
-    protected $finalized = false;
-
-    /**
-     * Property list containing configuration directives.
-     * @type array
-     */
-    protected $plist;
-
-    /**
-     * Whether or not a set is taking place due to an alias lookup.
-     * @type bool
-     */
-    private $aliasMode;
-
-    /**
-     * Set to false if you do not want line and file numbers in errors.
-     * (useful when unit testing).  This will also compress some errors
-     * and exceptions.
-     * @type bool
-     */
-    public $chatty = true;
-
-    /**
-     * Current lock; only gets to this namespace are allowed.
-     * @type string
-     */
-    private $lock;
-
-    /**
-     * Constructor
-     * @param HTMLPurifier_ConfigSchema $definition ConfigSchema that defines
-     * what directives are allowed.
-     * @param HTMLPurifier_PropertyList $parent
-     */
-    public function __construct($definition, $parent = null)
-    {
-        $parent = $parent ? $parent : $definition->defaultPlist;
-        $this->plist = new HTMLPurifier_PropertyList($parent);
-        $this->def = $definition; // keep a copy around for checking
-        $this->parser = new HTMLPurifier_VarParser_Flexible();
-    }
-
-    /**
-     * Convenience constructor that creates a config object based on a mixed var
-     * @param mixed $config Variable that defines the state of the config
-     *                      object. Can be: a HTMLPurifier_Config() object,
-     *                      an array of directives based on loadArray(),
-     *                      or a string filename of an ini file.
-     * @param HTMLPurifier_ConfigSchema $schema Schema object
-     * @return HTMLPurifier_Config Configured object
-     */
-    public static function create($config, $schema = null)
-    {
-        if ($config instanceof HTMLPurifier_Config) {
-            // pass-through
-            return $config;
-        }
-        if (!$schema) {
-            $ret = HTMLPurifier_Config::createDefault();
-        } else {
-            $ret = new HTMLPurifier_Config($schema);
-        }
-        if (is_string($config)) {
-            $ret->loadIni($config);
-        } elseif (is_array($config)) $ret->loadArray($config);
-        return $ret;
-    }
-
-    /**
-     * Creates a new config object that inherits from a previous one.
-     * @param HTMLPurifier_Config $config Configuration object to inherit from.
-     * @return HTMLPurifier_Config object with $config as its parent.
-     */
-    public static function inherit(HTMLPurifier_Config $config)
-    {
-        return new HTMLPurifier_Config($config->def, $config->plist);
-    }
-
-    /**
-     * Convenience constructor that creates a default configuration object.
-     * @return HTMLPurifier_Config default object.
-     */
-    public static function createDefault()
-    {
-        $definition = HTMLPurifier_ConfigSchema::instance();
-        $config = new HTMLPurifier_Config($definition);
-        return $config;
-    }
-
-    /**
-     * Retrieves a value from the configuration.
-     *
-     * @param string $key String key
-     * @param mixed $a
-     *
-     * @return mixed
-     */
-    public function get($key, $a = null)
-    {
-        if ($a !== null) {
-            $this->triggerError(
-                "Using deprecated API: use \$config->get('$key.$a') instead",
-                E_USER_WARNING
-            );
-            $key = "$key.$a";
-        }
-        if (!$this->finalized) {
-            $this->autoFinalize();
-        }
-        if (!isset($this->def->info[$key])) {
-            // can't add % due to SimpleTest bug
-            $this->triggerError(
-                'Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
-                E_USER_WARNING
-            );
-            return;
-        }
-        if (isset($this->def->info[$key]->isAlias)) {
-            $d = $this->def->info[$key];
-            $this->triggerError(
-                'Cannot get value from aliased directive, use real name ' . $d->key,
-                E_USER_ERROR
-            );
-            return;
-        }
-        if ($this->lock) {
-            list($ns) = explode('.', $key);
-            if ($ns !== $this->lock) {
-                $this->triggerError(
-                    'Cannot get value of namespace ' . $ns . ' when lock for ' .
-                    $this->lock .
-                    ' is active, this probably indicates a Definition setup method ' .
-                    'is accessing directives that are not within its namespace',
-                    E_USER_ERROR
-                );
-                return;
-            }
-        }
-        return $this->plist->get($key);
-    }
-
-    /**
-     * Retrieves an array of directives to values from a given namespace
-     *
-     * @param string $namespace String namespace
-     *
-     * @return array
-     */
-    public function getBatch($namespace)
-    {
-        if (!$this->finalized) {
-            $this->autoFinalize();
-        }
-        $full = $this->getAll();
-        if (!isset($full[$namespace])) {
-            $this->triggerError(
-                'Cannot retrieve undefined namespace ' .
-                htmlspecialchars($namespace),
-                E_USER_WARNING
-            );
-            return;
-        }
-        return $full[$namespace];
-    }
-
-    /**
-     * Returns a SHA-1 signature of a segment of the configuration object
-     * that uniquely identifies that particular configuration
-     *
-     * @param string $namespace Namespace to get serial for
-     *
-     * @return string
-     * @note Revision is handled specially and is removed from the batch
-     *       before processing!
-     */
-    public function getBatchSerial($namespace)
-    {
-        if (empty($this->serials[$namespace])) {
-            $batch = $this->getBatch($namespace);
-            unset($batch['DefinitionRev']);
-            $this->serials[$namespace] = sha1(serialize($batch));
-        }
-        return $this->serials[$namespace];
-    }
-
-    /**
-     * Returns a SHA-1 signature for the entire configuration object
-     * that uniquely identifies that particular configuration
-     *
-     * @return string
-     */
-    public function getSerial()
-    {
-        if (empty($this->serial)) {
-            $this->serial = sha1(serialize($this->getAll()));
-        }
-        return $this->serial;
-    }
-
-    /**
-     * Retrieves all directives, organized by namespace
-     *
-     * @warning This is a pretty inefficient function, avoid if you can
-     */
-    public function getAll()
-    {
-        if (!$this->finalized) {
-            $this->autoFinalize();
-        }
-        $ret = array();
-        foreach ($this->plist->squash() as $name => $value) {
-            list($ns, $key) = explode('.', $name, 2);
-            $ret[$ns][$key] = $value;
-        }
-        return $ret;
-    }
-
-    /**
-     * Sets a value to configuration.
-     *
-     * @param string $key key
-     * @param mixed $value value
-     * @param mixed $a
-     */
-    public function set($key, $value, $a = null)
-    {
-        if (strpos($key, '.') === false) {
-            $namespace = $key;
-            $directive = $value;
-            $value = $a;
-            $key = "$key.$directive";
-            $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
-        } else {
-            list($namespace) = explode('.', $key);
-        }
-        if ($this->isFinalized('Cannot set directive after finalization')) {
-            return;
-        }
-        if (!isset($this->def->info[$key])) {
-            $this->triggerError(
-                'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
-                E_USER_WARNING
-            );
-            return;
-        }
-        $def = $this->def->info[$key];
-
-        if (isset($def->isAlias)) {
-            if ($this->aliasMode) {
-                $this->triggerError(
-                    'Double-aliases not allowed, please fix '.
-                    'ConfigSchema bug with' . $key,
-                    E_USER_ERROR
-                );
-                return;
-            }
-            $this->aliasMode = true;
-            $this->set($def->key, $value);
-            $this->aliasMode = false;
-            $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
-            return;
-        }
-
-        // Raw type might be negative when using the fully optimized form
-        // of stdClass, which indicates allow_null == true
-        $rtype = is_int($def) ? $def : $def->type;
-        if ($rtype < 0) {
-            $type = -$rtype;
-            $allow_null = true;
-        } else {
-            $type = $rtype;
-            $allow_null = isset($def->allow_null);
-        }
-
-        try {
-            $value = $this->parser->parse($value, $type, $allow_null);
-        } catch (HTMLPurifier_VarParserException $e) {
-            $this->triggerError(
-                'Value for ' . $key . ' is of invalid type, should be ' .
-                HTMLPurifier_VarParser::getTypeName($type),
-                E_USER_WARNING
-            );
-            return;
-        }
-        if (is_string($value) && is_object($def)) {
-            // resolve value alias if defined
-            if (isset($def->aliases[$value])) {
-                $value = $def->aliases[$value];
-            }
-            // check to see if the value is allowed
-            if (isset($def->allowed) && !isset($def->allowed[$value])) {
-                $this->triggerError(
-                    'Value not supported, valid values are: ' .
-                    $this->_listify($def->allowed),
-                    E_USER_WARNING
-                );
-                return;
-            }
-        }
-        $this->plist->set($key, $value);
-
-        // reset definitions if the directives they depend on changed
-        // this is a very costly process, so it's discouraged
-        // with finalization
-        if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
-            $this->definitions[$namespace] = null;
-        }
-
-        $this->serials[$namespace] = false;
-    }
-
-    /**
-     * Convenience function for error reporting
-     *
-     * @param array $lookup
-     *
-     * @return string
-     */
-    private function _listify($lookup)
-    {
-        $list = array();
-        foreach ($lookup as $name => $b) {
-            $list[] = $name;
-        }
-        return implode(', ', $list);
-    }
-
-    /**
-     * Retrieves object reference to the HTML definition.
-     *
-     * @param bool $raw Return a copy that has not been setup yet. Must be
-     *             called before it's been setup, otherwise won't work.
-     * @param bool $optimized If true, this method may return null, to
-     *             indicate that a cached version of the modified
-     *             definition object is available and no further edits
-     *             are necessary.  Consider using
-     *             maybeGetRawHTMLDefinition, which is more explicitly
-     *             named, instead.
-     *
-     * @return HTMLPurifier_HTMLDefinition|null
-     */
-    public function getHTMLDefinition($raw = false, $optimized = false)
-    {
-        return $this->getDefinition('HTML', $raw, $optimized);
-    }
-
-    /**
-     * Retrieves object reference to the CSS definition
-     *
-     * @param bool $raw Return a copy that has not been setup yet. Must be
-     *             called before it's been setup, otherwise won't work.
-     * @param bool $optimized If true, this method may return null, to
-     *             indicate that a cached version of the modified
-     *             definition object is available and no further edits
-     *             are necessary.  Consider using
-     *             maybeGetRawCSSDefinition, which is more explicitly
-     *             named, instead.
-     *
-     * @return HTMLPurifier_CSSDefinition|null
-     */
-    public function getCSSDefinition($raw = false, $optimized = false)
-    {
-        return $this->getDefinition('CSS', $raw, $optimized);
-    }
-
-    /**
-     * Retrieves object reference to the URI definition
-     *
-     * @param bool $raw Return a copy that has not been setup yet. Must be
-     *             called before it's been setup, otherwise won't work.
-     * @param bool $optimized If true, this method may return null, to
-     *             indicate that a cached version of the modified
-     *             definition object is available and no further edits
-     *             are necessary.  Consider using
-     *             maybeGetRawURIDefinition, which is more explicitly
-     *             named, instead.
-     *
-     * @return HTMLPurifier_URIDefinition|null
-     */
-    public function getURIDefinition($raw = false, $optimized = false)
-    {
-        return $this->getDefinition('URI', $raw, $optimized);
-    }
-
-    /**
-     * Retrieves a definition
-     *
-     * @param string $type Type of definition: HTML, CSS, etc
-     * @param bool $raw Whether or not definition should be returned raw
-     * @param bool $optimized Only has an effect when $raw is true.  Whether
-     *        or not to return null if the result is already present in
-     *        the cache.  This is off by default for backwards
-     *        compatibility reasons, but you need to do things this
-     *        way in order to ensure that caching is done properly.
-     *        Check out enduser-customize.html for more details.
-     *        We probably won't ever change this default, as much as the
-     *        maybe semantics is the "right thing to do."
-     *
-     * @throws HTMLPurifier_Exception
-     * @return HTMLPurifier_Definition|null
-     */
-    public function getDefinition($type, $raw = false, $optimized = false)
-    {
-        if ($optimized && !$raw) {
-            throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");
-        }
-        if (!$this->finalized) {
-            $this->autoFinalize();
-        }
-        // temporarily suspend locks, so we can handle recursive definition calls
-        $lock = $this->lock;
-        $this->lock = null;
-        $factory = HTMLPurifier_DefinitionCacheFactory::instance();
-        $cache = $factory->create($type, $this);
-        $this->lock = $lock;
-        if (!$raw) {
-            // full definition
-            // ---------------
-            // check if definition is in memory
-            if (!empty($this->definitions[$type])) {
-                $def = $this->definitions[$type];
-                // check if the definition is setup
-                if ($def->setup) {
-                    return $def;
-                } else {
-                    $def->setup($this);
-                    if ($def->optimized) {
-                        $cache->add($def, $this);
-                    }
-                    return $def;
-                }
-            }
-            // check if definition is in cache
-            $def = $cache->get($this);
-            if ($def) {
-                // definition in cache, save to memory and return it
-                $this->definitions[$type] = $def;
-                return $def;
-            }
-            // initialize it
-            $def = $this->initDefinition($type);
-            // set it up
-            $this->lock = $type;
-            $def->setup($this);
-            $this->lock = null;
-            // save in cache
-            $cache->add($def, $this);
-            // return it
-            return $def;
-        } else {
-            // raw definition
-            // --------------
-            // check preconditions
-            $def = null;
-            if ($optimized) {
-                if (is_null($this->get($type . '.DefinitionID'))) {
-                    // fatally error out if definition ID not set
-                    throw new HTMLPurifier_Exception(
-                        "Cannot retrieve raw version without specifying %$type.DefinitionID"
-                    );
-                }
-            }
-            if (!empty($this->definitions[$type])) {
-                $def = $this->definitions[$type];
-                if ($def->setup && !$optimized) {
-                    $extra = $this->chatty ?
-                        " (try moving this code block earlier in your initialization)" :
-                        "";
-                    throw new HTMLPurifier_Exception(
-                        "Cannot retrieve raw definition after it has already been setup" .
-                        $extra
-                    );
-                }
-                if ($def->optimized === null) {
-                    $extra = $this->chatty ? " (try flushing your cache)" : "";
-                    throw new HTMLPurifier_Exception(
-                        "Optimization status of definition is unknown" . $extra
-                    );
-                }
-                if ($def->optimized !== $optimized) {
-                    $msg = $optimized ? "optimized" : "unoptimized";
-                    $extra = $this->chatty ?
-                        " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)"
-                        : "";
-                    throw new HTMLPurifier_Exception(
-                        "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra
-                    );
-                }
-            }
-            // check if definition was in memory
-            if ($def) {
-                if ($def->setup) {
-                    // invariant: $optimized === true (checked above)
-                    return null;
-                } else {
-                    return $def;
-                }
-            }
-            // if optimized, check if definition was in cache
-            // (because we do the memory check first, this formulation
-            // is prone to cache slamming, but I think
-            // guaranteeing that either /all/ of the raw
-            // setup code or /none/ of it is run is more important.)
-            if ($optimized) {
-                // This code path only gets run once; once we put
-                // something in $definitions (which is guaranteed by the
-                // trailing code), we always short-circuit above.
-                $def = $cache->get($this);
-                if ($def) {
-                    // save the full definition for later, but don't
-                    // return it yet
-                    $this->definitions[$type] = $def;
-                    return null;
-                }
-            }
-            // check invariants for creation
-            if (!$optimized) {
-                if (!is_null($this->get($type . '.DefinitionID'))) {
-                    if ($this->chatty) {
-                        $this->triggerError(
-                            'Due to a documentation error in previous version of HTML Purifier, your ' .
-                            'definitions are not being cached.  If this is OK, you can remove the ' .
-                            '%$type.DefinitionRev and %$type.DefinitionID declaration.  Otherwise, ' .
-                            'modify your code to use maybeGetRawDefinition, and test if the returned ' .
-                            'value is null before making any edits (if it is null, that means that a ' .
-                            'cached version is available, and no raw operations are necessary).  See ' .
-                            '<a href="http://htmlpurifier.org/docs/enduser-customize.html#optimized">' .
-                            'Customize</a> for more details',
-                            E_USER_WARNING
-                        );
-                    } else {
-                        $this->triggerError(
-                            "Useless DefinitionID declaration",
-                            E_USER_WARNING
-                        );
-                    }
-                }
-            }
-            // initialize it
-            $def = $this->initDefinition($type);
-            $def->optimized = $optimized;
-            return $def;
-        }
-        throw new HTMLPurifier_Exception("The impossible happened!");
-    }
-
-    /**
-     * Initialise definition
-     *
-     * @param string $type What type of definition to create
-     *
-     * @return HTMLPurifier_CSSDefinition|HTMLPurifier_HTMLDefinition|HTMLPurifier_URIDefinition
-     * @throws HTMLPurifier_Exception
-     */
-    private function initDefinition($type)
-    {
-        // quick checks failed, let's create the object
-        if ($type == 'HTML') {
-            $def = new HTMLPurifier_HTMLDefinition();
-        } elseif ($type == 'CSS') {
-            $def = new HTMLPurifier_CSSDefinition();
-        } elseif ($type == 'URI') {
-            $def = new HTMLPurifier_URIDefinition();
-        } else {
-            throw new HTMLPurifier_Exception(
-                "Definition of $type type not supported"
-            );
-        }
-        $this->definitions[$type] = $def;
-        return $def;
-    }
-
-    public function maybeGetRawDefinition($name)
-    {
-        return $this->getDefinition($name, true, true);
-    }
-
-    /**
-     * @return HTMLPurifier_HTMLDefinition|null
-     */
-    public function maybeGetRawHTMLDefinition()
-    {
-        return $this->getDefinition('HTML', true, true);
-    }
-    
-    /**
-     * @return HTMLPurifier_CSSDefinition|null
-     */
-    public function maybeGetRawCSSDefinition()
-    {
-        return $this->getDefinition('CSS', true, true);
-    }
-    
-    /**
-     * @return HTMLPurifier_URIDefinition|null
-     */
-    public function maybeGetRawURIDefinition()
-    {
-        return $this->getDefinition('URI', true, true);
-    }
-
-    /**
-     * Loads configuration values from an array with the following structure:
-     * Namespace.Directive => Value
-     *
-     * @param array $config_array Configuration associative array
-     */
-    public function loadArray($config_array)
-    {
-        if ($this->isFinalized('Cannot load directives after finalization')) {
-            return;
-        }
-        foreach ($config_array as $key => $value) {
-            $key = str_replace('_', '.', $key);
-            if (strpos($key, '.') !== false) {
-                $this->set($key, $value);
-            } else {
-                $namespace = $key;
-                $namespace_values = $value;
-                foreach ($namespace_values as $directive => $value2) {
-                    $this->set($namespace .'.'. $directive, $value2);
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns a list of array(namespace, directive) for all directives
-     * that are allowed in a web-form context as per an allowed
-     * namespaces/directives list.
-     *
-     * @param array $allowed List of allowed namespaces/directives
-     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
-     *
-     * @return array
-     */
-    public static function getAllowedDirectivesForForm($allowed, $schema = null)
-    {
-        if (!$schema) {
-            $schema = HTMLPurifier_ConfigSchema::instance();
-        }
-        if ($allowed !== true) {
-            if (is_string($allowed)) {
-                $allowed = array($allowed);
-            }
-            $allowed_ns = array();
-            $allowed_directives = array();
-            $blacklisted_directives = array();
-            foreach ($allowed as $ns_or_directive) {
-                if (strpos($ns_or_directive, '.') !== false) {
-                    // directive
-                    if ($ns_or_directive[0] == '-') {
-                        $blacklisted_directives[substr($ns_or_directive, 1)] = true;
-                    } else {
-                        $allowed_directives[$ns_or_directive] = true;
-                    }
-                } else {
-                    // namespace
-                    $allowed_ns[$ns_or_directive] = true;
-                }
-            }
-        }
-        $ret = array();
-        foreach ($schema->info as $key => $def) {
-            list($ns, $directive) = explode('.', $key, 2);
-            if ($allowed !== true) {
-                if (isset($blacklisted_directives["$ns.$directive"])) {
-                    continue;
-                }
-                if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) {
-                    continue;
-                }
-            }
-            if (isset($def->isAlias)) {
-                continue;
-            }
-            if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') {
-                continue;
-            }
-            $ret[] = array($ns, $directive);
-        }
-        return $ret;
-    }
-
-    /**
-     * Loads configuration values from $_GET/$_POST that were posted
-     * via ConfigForm
-     *
-     * @param array $array $_GET or $_POST array to import
-     * @param string|bool $index Index/name that the config variables are in
-     * @param array|bool $allowed List of allowed namespaces/directives
-     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
-     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
-     *
-     * @return mixed
-     */
-    public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
-    {
-        $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
-        $config = HTMLPurifier_Config::create($ret, $schema);
-        return $config;
-    }
-
-    /**
-     * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
-     *
-     * @param array $array $_GET or $_POST array to import
-     * @param string|bool $index Index/name that the config variables are in
-     * @param array|bool $allowed List of allowed namespaces/directives
-     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
-     */
-    public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true)
-    {
-         $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
-         $this->loadArray($ret);
-    }
-
-    /**
-     * Prepares an array from a form into something usable for the more
-     * strict parts of HTMLPurifier_Config
-     *
-     * @param array $array $_GET or $_POST array to import
-     * @param string|bool $index Index/name that the config variables are in
-     * @param array|bool $allowed List of allowed namespaces/directives
-     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
-     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
-     *
-     * @return array
-     */
-    public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
-    {
-        if ($index !== false) {
-            $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
-        }
-        $mq = $mq_fix && version_compare(PHP_VERSION, '7.4.0', '<') && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
-
-        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
-        $ret = array();
-        foreach ($allowed as $key) {
-            list($ns, $directive) = $key;
-            $skey = "$ns.$directive";
-            if (!empty($array["Null_$skey"])) {
-                $ret[$ns][$directive] = null;
-                continue;
-            }
-            if (!isset($array[$skey])) {
-                continue;
-            }
-            $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
-            $ret[$ns][$directive] = $value;
-        }
-        return $ret;
-    }
-
-    /**
-     * Loads configuration values from an ini file
-     *
-     * @param string $filename Name of ini file
-     */
-    public function loadIni($filename)
-    {
-        if ($this->isFinalized('Cannot load directives after finalization')) {
-            return;
-        }
-        $array = parse_ini_file($filename, true);
-        $this->loadArray($array);
-    }
-
-    /**
-     * Checks whether or not the configuration object is finalized.
-     *
-     * @param string|bool $error String error message, or false for no error
-     *
-     * @return bool
-     */
-    public function isFinalized($error = false)
-    {
-        if ($this->finalized && $error) {
-            $this->triggerError($error, E_USER_ERROR);
-        }
-        return $this->finalized;
-    }
-
-    /**
-     * Finalizes configuration only if auto finalize is on and not
-     * already finalized
-     */
-    public function autoFinalize()
-    {
-        if ($this->autoFinalize) {
-            $this->finalize();
-        } else {
-            $this->plist->squash(true);
-        }
-    }
-
-    /**
-     * Finalizes a configuration object, prohibiting further change
-     */
-    public function finalize()
-    {
-        $this->finalized = true;
-        $this->parser = null;
-    }
-
-    /**
-     * Produces a nicely formatted error message by supplying the
-     * stack frame information OUTSIDE of HTMLPurifier_Config.
-     *
-     * @param string $msg An error message
-     * @param int $no An error number
-     */
-    protected function triggerError($msg, $no)
-    {
-        // determine previous stack frame
-        $extra = '';
-        if ($this->chatty) {
-            $trace = debug_backtrace();
-            // zip(tail(trace), trace) -- but PHP is not Haskell har har
-            for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
-                // XXX this is not correct on some versions of HTML Purifier
-                if (isset($trace[$i + 1]['class']) && $trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
-                    continue;
-                }
-                $frame = $trace[$i];
-                $extra = " invoked on line {$frame['line']} in file {$frame['file']}";
-                break;
-            }
-        }
-        trigger_error($msg . $extra, $no);
-    }
-
-    /**
-     * Returns a serialized form of the configuration object that can
-     * be reconstituted.
-     *
-     * @return string
-     */
-    public function serialize()
-    {
-        $this->getDefinition('HTML');
-        $this->getDefinition('CSS');
-        $this->getDefinition('URI');
-        return serialize($this);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php
deleted file mode 100644
index c3fe8cd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema.php
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-
-/**
- * Configuration definition, defines directives and their defaults.
- */
-class HTMLPurifier_ConfigSchema
-{
-    /**
-     * Defaults of the directives and namespaces.
-     * @type array
-     * @note This shares the exact same structure as HTMLPurifier_Config::$conf
-     */
-    public $defaults = array();
-
-    /**
-     * The default property list. Do not edit this property list.
-     * @type array
-     */
-    public $defaultPlist;
-
-    /**
-     * Definition of the directives.
-     * The structure of this is:
-     *
-     *  array(
-     *      'Namespace' => array(
-     *          'Directive' => new stdClass(),
-     *      )
-     *  )
-     *
-     * The stdClass may have the following properties:
-     *
-     *  - If isAlias isn't set:
-     *      - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
-     *      - allow_null: If set, this directive allows null values
-     *      - aliases: If set, an associative array of value aliases to real values
-     *      - allowed: If set, a lookup array of allowed (string) values
-     *  - If isAlias is set:
-     *      - namespace: Namespace this directive aliases to
-     *      - name: Directive name this directive aliases to
-     *
-     * In certain degenerate cases, stdClass will actually be an integer. In
-     * that case, the value is equivalent to an stdClass with the type
-     * property set to the integer. If the integer is negative, type is
-     * equal to the absolute value of integer, and allow_null is true.
-     *
-     * This class is friendly with HTMLPurifier_Config. If you need introspection
-     * about the schema, you're better of using the ConfigSchema_Interchange,
-     * which uses more memory but has much richer information.
-     * @type array
-     */
-    public $info = array();
-
-    /**
-     * Application-wide singleton
-     * @type HTMLPurifier_ConfigSchema
-     */
-    protected static $singleton;
-
-    public function __construct()
-    {
-        $this->defaultPlist = new HTMLPurifier_PropertyList();
-    }
-
-    /**
-     * Unserializes the default ConfigSchema.
-     * @return HTMLPurifier_ConfigSchema
-     */
-    public static function makeFromSerial()
-    {
-        $contents = file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser');
-        $r = unserialize($contents);
-        if (!$r) {
-            $hash = sha1($contents);
-            trigger_error("Unserialization of configuration schema failed, sha1 of file was $hash", E_USER_ERROR);
-        }
-        return $r;
-    }
-
-    /**
-     * Retrieves an instance of the application-wide configuration definition.
-     * @param HTMLPurifier_ConfigSchema $prototype
-     * @return HTMLPurifier_ConfigSchema
-     */
-    public static function instance($prototype = null)
-    {
-        if ($prototype !== null) {
-            HTMLPurifier_ConfigSchema::$singleton = $prototype;
-        } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) {
-            HTMLPurifier_ConfigSchema::$singleton = HTMLPurifier_ConfigSchema::makeFromSerial();
-        }
-        return HTMLPurifier_ConfigSchema::$singleton;
-    }
-
-    /**
-     * Defines a directive for configuration
-     * @warning Will fail of directive's namespace is defined.
-     * @warning This method's signature is slightly different from the legacy
-     *          define() static method! Beware!
-     * @param string $key Name of directive
-     * @param mixed $default Default value of directive
-     * @param string $type Allowed type of the directive. See
-     *      HTMLPurifier_VarParser::$types for allowed values
-     * @param bool $allow_null Whether or not to allow null values
-     */
-    public function add($key, $default, $type, $allow_null)
-    {
-        $obj = new stdClass();
-        $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
-        if ($allow_null) {
-            $obj->allow_null = true;
-        }
-        $this->info[$key] = $obj;
-        $this->defaults[$key] = $default;
-        $this->defaultPlist->set($key, $default);
-    }
-
-    /**
-     * Defines a directive value alias.
-     *
-     * Directive value aliases are convenient for developers because it lets
-     * them set a directive to several values and get the same result.
-     * @param string $key Name of Directive
-     * @param array $aliases Hash of aliased values to the real alias
-     */
-    public function addValueAliases($key, $aliases)
-    {
-        if (!isset($this->info[$key]->aliases)) {
-            $this->info[$key]->aliases = array();
-        }
-        foreach ($aliases as $alias => $real) {
-            $this->info[$key]->aliases[$alias] = $real;
-        }
-    }
-
-    /**
-     * Defines a set of allowed values for a directive.
-     * @warning This is slightly different from the corresponding static
-     *          method definition.
-     * @param string $key Name of directive
-     * @param array $allowed Lookup array of allowed values
-     */
-    public function addAllowedValues($key, $allowed)
-    {
-        $this->info[$key]->allowed = $allowed;
-    }
-
-    /**
-     * Defines a directive alias for backwards compatibility
-     * @param string $key Directive that will be aliased
-     * @param string $new_key Directive that the alias will be to
-     */
-    public function addAlias($key, $new_key)
-    {
-        $obj = new stdClass;
-        $obj->key = $new_key;
-        $obj->isAlias = true;
-        $this->info[$key] = $obj;
-    }
-
-    /**
-     * Replaces any stdClass that only has the type property with type integer.
-     */
-    public function postProcess()
-    {
-        foreach ($this->info as $key => $v) {
-            if (count((array) $v) == 1) {
-                $this->info[$key] = $v->type;
-            } elseif (count((array) $v) == 2 && isset($v->allow_null)) {
-                $this->info[$key] = -$v->type;
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php
deleted file mode 100644
index d5906cd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Converts HTMLPurifier_ConfigSchema_Interchange to our runtime
- * representation used to perform checks on user configuration.
- */
-class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
-{
-
-    /**
-     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
-     * @return HTMLPurifier_ConfigSchema
-     */
-    public function build($interchange)
-    {
-        $schema = new HTMLPurifier_ConfigSchema();
-        foreach ($interchange->directives as $d) {
-            $schema->add(
-                $d->id->key,
-                $d->default,
-                $d->type,
-                $d->typeAllowsNull
-            );
-            if ($d->allowed !== null) {
-                $schema->addAllowedValues(
-                    $d->id->key,
-                    $d->allowed
-                );
-            }
-            foreach ($d->aliases as $alias) {
-                $schema->addAlias(
-                    $alias->key,
-                    $d->id->key
-                );
-            }
-            if ($d->valueAliases !== null) {
-                $schema->addValueAliases(
-                    $d->id->key,
-                    $d->valueAliases
-                );
-            }
-        }
-        $schema->postProcess();
-        return $schema;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php
deleted file mode 100644
index 5fa56f7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Builder/Xml.php
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-
-/**
- * Converts HTMLPurifier_ConfigSchema_Interchange to an XML format,
- * which can be further processed to generate documentation.
- */
-class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
-{
-
-    /**
-     * @type HTMLPurifier_ConfigSchema_Interchange
-     */
-    protected $interchange;
-
-    /**
-     * @type string
-     */
-    private $namespace;
-
-    /**
-     * @param string $html
-     */
-    protected function writeHTMLDiv($html)
-    {
-        $this->startElement('div');
-
-        $purifier = HTMLPurifier::getInstance();
-        $html = $purifier->purify($html);
-        $this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
-        $this->writeRaw($html);
-
-        $this->endElement(); // div
-    }
-
-    /**
-     * @param mixed $var
-     * @return string
-     */
-    protected function export($var)
-    {
-        if ($var === array()) {
-            return 'array()';
-        }
-        return var_export($var, true);
-    }
-
-    /**
-     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
-     */
-    public function build($interchange)
-    {
-        // global access, only use as last resort
-        $this->interchange = $interchange;
-
-        $this->setIndent(true);
-        $this->startDocument('1.0', 'UTF-8');
-        $this->startElement('configdoc');
-        $this->writeElement('title', $interchange->name);
-
-        foreach ($interchange->directives as $directive) {
-            $this->buildDirective($directive);
-        }
-
-        if ($this->namespace) {
-            $this->endElement();
-        } // namespace
-
-        $this->endElement(); // configdoc
-        $this->flush();
-    }
-
-    /**
-     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive
-     */
-    public function buildDirective($directive)
-    {
-        // Kludge, although I suppose having a notion of a "root namespace"
-        // certainly makes things look nicer when documentation is built.
-        // Depends on things being sorted.
-        if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) {
-            if ($this->namespace) {
-                $this->endElement();
-            } // namespace
-            $this->namespace = $directive->id->getRootNamespace();
-            $this->startElement('namespace');
-            $this->writeAttribute('id', $this->namespace);
-            $this->writeElement('name', $this->namespace);
-        }
-
-        $this->startElement('directive');
-        $this->writeAttribute('id', $directive->id->toString());
-
-        $this->writeElement('name', $directive->id->getDirective());
-
-        $this->startElement('aliases');
-        foreach ($directive->aliases as $alias) {
-            $this->writeElement('alias', $alias->toString());
-        }
-        $this->endElement(); // aliases
-
-        $this->startElement('constraints');
-        if ($directive->version) {
-            $this->writeElement('version', $directive->version);
-        }
-        $this->startElement('type');
-        if ($directive->typeAllowsNull) {
-            $this->writeAttribute('allow-null', 'yes');
-        }
-        $this->text($directive->type);
-        $this->endElement(); // type
-        if ($directive->allowed) {
-            $this->startElement('allowed');
-            foreach ($directive->allowed as $value => $x) {
-                $this->writeElement('value', $value);
-            }
-            $this->endElement(); // allowed
-        }
-        $this->writeElement('default', $this->export($directive->default));
-        $this->writeAttribute('xml:space', 'preserve');
-        if ($directive->external) {
-            $this->startElement('external');
-            foreach ($directive->external as $project) {
-                $this->writeElement('project', $project);
-            }
-            $this->endElement();
-        }
-        $this->endElement(); // constraints
-
-        if ($directive->deprecatedVersion) {
-            $this->startElement('deprecated');
-            $this->writeElement('version', $directive->deprecatedVersion);
-            $this->writeElement('use', $directive->deprecatedUse->toString());
-            $this->endElement(); // deprecated
-        }
-
-        $this->startElement('description');
-        $this->writeHTMLDiv($directive->description);
-        $this->endElement(); // description
-
-        $this->endElement(); // directive
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php
deleted file mode 100644
index 2671516..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Exception.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * Exceptions related to configuration schema
- */
-class HTMLPurifier_ConfigSchema_Exception extends HTMLPurifier_Exception
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php
deleted file mode 100644
index 0e08ae8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * Generic schema interchange format that can be converted to a runtime
- * representation (HTMLPurifier_ConfigSchema) or HTML documentation. Members
- * are completely validated.
- */
-class HTMLPurifier_ConfigSchema_Interchange
-{
-
-    /**
-     * Name of the application this schema is describing.
-     * @type string
-     */
-    public $name;
-
-    /**
-     * Array of Directive ID => array(directive info)
-     * @type HTMLPurifier_ConfigSchema_Interchange_Directive[]
-     */
-    public $directives = array();
-
-    /**
-     * Adds a directive array to $directives
-     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive
-     * @throws HTMLPurifier_ConfigSchema_Exception
-     */
-    public function addDirective($directive)
-    {
-        if (isset($this->directives[$i = $directive->id->toString()])) {
-            throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'");
-        }
-        $this->directives[$i] = $directive;
-    }
-
-    /**
-     * Convenience function to perform standard validation. Throws exception
-     * on failed validation.
-     */
-    public function validate()
-    {
-        $validator = new HTMLPurifier_ConfigSchema_Validator();
-        return $validator->validate($this);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php
deleted file mode 100644
index 127a39a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-/**
- * Interchange component class describing configuration directives.
- */
-class HTMLPurifier_ConfigSchema_Interchange_Directive
-{
-
-    /**
-     * ID of directive.
-     * @type HTMLPurifier_ConfigSchema_Interchange_Id
-     */
-    public $id;
-
-    /**
-     * Type, e.g. 'integer' or 'istring'.
-     * @type string
-     */
-    public $type;
-
-    /**
-     * Default value, e.g. 3 or 'DefaultVal'.
-     * @type mixed
-     */
-    public $default;
-
-    /**
-     * HTML description.
-     * @type string
-     */
-    public $description;
-
-    /**
-     * Whether or not null is allowed as a value.
-     * @type bool
-     */
-    public $typeAllowsNull = false;
-
-    /**
-     * Lookup table of allowed scalar values.
-     * e.g. array('allowed' => true).
-     * Null if all values are allowed.
-     * @type array
-     */
-    public $allowed;
-
-    /**
-     * List of aliases for the directive.
-     * e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))).
-     * @type HTMLPurifier_ConfigSchema_Interchange_Id[]
-     */
-    public $aliases = array();
-
-    /**
-     * Hash of value aliases, e.g. array('alt' => 'real'). Null if value
-     * aliasing is disabled (necessary for non-scalar types).
-     * @type array
-     */
-    public $valueAliases;
-
-    /**
-     * Version of HTML Purifier the directive was introduced, e.g. '1.3.1'.
-     * Null if the directive has always existed.
-     * @type string
-     */
-    public $version;
-
-    /**
-     * ID of directive that supercedes this old directive.
-     * Null if not deprecated.
-     * @type HTMLPurifier_ConfigSchema_Interchange_Id
-     */
-    public $deprecatedUse;
-
-    /**
-     * Version of HTML Purifier this directive was deprecated. Null if not
-     * deprecated.
-     * @type string
-     */
-    public $deprecatedVersion;
-
-    /**
-     * List of external projects this directive depends on, e.g. array('CSSTidy').
-     * @type array
-     */
-    public $external = array();
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php
deleted file mode 100644
index 126f09d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Interchange/Id.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * Represents a directive ID in the interchange format.
- */
-class HTMLPurifier_ConfigSchema_Interchange_Id
-{
-
-    /**
-     * @type string
-     */
-    public $key;
-
-    /**
-     * @param string $key
-     */
-    public function __construct($key)
-    {
-        $this->key = $key;
-    }
-
-    /**
-     * @return string
-     * @warning This is NOT magic, to ensure that people don't abuse SPL and
-     *          cause problems for PHP 5.0 support.
-     */
-    public function toString()
-    {
-        return $this->key;
-    }
-
-    /**
-     * @return string
-     */
-    public function getRootNamespace()
-    {
-        return substr($this->key, 0, strpos($this->key, "."));
-    }
-
-    /**
-     * @return string
-     */
-    public function getDirective()
-    {
-        return substr($this->key, strpos($this->key, ".") + 1);
-    }
-
-    /**
-     * @param string $id
-     * @return HTMLPurifier_ConfigSchema_Interchange_Id
-     */
-    public static function make($id)
-    {
-        return new HTMLPurifier_ConfigSchema_Interchange_Id($id);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
deleted file mode 100644
index 655e6dd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php
+++ /dev/null
@@ -1,226 +0,0 @@
-<?php
-
-class HTMLPurifier_ConfigSchema_InterchangeBuilder
-{
-
-    /**
-     * Used for processing DEFAULT, nothing else.
-     * @type HTMLPurifier_VarParser
-     */
-    protected $varParser;
-
-    /**
-     * @param HTMLPurifier_VarParser $varParser
-     */
-    public function __construct($varParser = null)
-    {
-        $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
-    }
-
-    /**
-     * @param string $dir
-     * @return HTMLPurifier_ConfigSchema_Interchange
-     */
-    public static function buildFromDirectory($dir = null)
-    {
-        $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
-        $interchange = new HTMLPurifier_ConfigSchema_Interchange();
-        return $builder->buildDir($interchange, $dir);
-    }
-
-    /**
-     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
-     * @param string $dir
-     * @return HTMLPurifier_ConfigSchema_Interchange
-     */
-    public function buildDir($interchange, $dir = null)
-    {
-        if (!$dir) {
-            $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema';
-        }
-        if (file_exists($dir . '/info.ini')) {
-            $info = parse_ini_file($dir . '/info.ini');
-            $interchange->name = $info['name'];
-        }
-
-        $files = array();
-        $dh = opendir($dir);
-        while (false !== ($file = readdir($dh))) {
-            if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
-                continue;
-            }
-            $files[] = $file;
-        }
-        closedir($dh);
-
-        sort($files);
-        foreach ($files as $file) {
-            $this->buildFile($interchange, $dir . '/' . $file);
-        }
-        return $interchange;
-    }
-
-    /**
-     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
-     * @param string $file
-     */
-    public function buildFile($interchange, $file)
-    {
-        $parser = new HTMLPurifier_StringHashParser();
-        $this->build(
-            $interchange,
-            new HTMLPurifier_StringHash($parser->parseFile($file))
-        );
-    }
-
-    /**
-     * Builds an interchange object based on a hash.
-     * @param HTMLPurifier_ConfigSchema_Interchange $interchange HTMLPurifier_ConfigSchema_Interchange object to build
-     * @param HTMLPurifier_StringHash $hash source data
-     * @throws HTMLPurifier_ConfigSchema_Exception
-     */
-    public function build($interchange, $hash)
-    {
-        if (!$hash instanceof HTMLPurifier_StringHash) {
-            $hash = new HTMLPurifier_StringHash($hash);
-        }
-        if (!isset($hash['ID'])) {
-            throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
-        }
-        if (strpos($hash['ID'], '.') === false) {
-            if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
-                $hash->offsetGet('DESCRIPTION'); // prevent complaining
-            } else {
-                throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
-            }
-        } else {
-            $this->buildDirective($interchange, $hash);
-        }
-        $this->_findUnused($hash);
-    }
-
-    /**
-     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
-     * @param HTMLPurifier_StringHash $hash
-     * @throws HTMLPurifier_ConfigSchema_Exception
-     */
-    public function buildDirective($interchange, $hash)
-    {
-        $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
-
-        // These are required elements:
-        $directive->id = $this->id($hash->offsetGet('ID'));
-        $id = $directive->id->toString(); // convenience
-
-        if (isset($hash['TYPE'])) {
-            $type = explode('/', $hash->offsetGet('TYPE'));
-            if (isset($type[1])) {
-                $directive->typeAllowsNull = true;
-            }
-            $directive->type = $type[0];
-        } else {
-            throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined");
-        }
-
-        if (isset($hash['DEFAULT'])) {
-            try {
-                $directive->default = $this->varParser->parse(
-                    $hash->offsetGet('DEFAULT'),
-                    $directive->type,
-                    $directive->typeAllowsNull
-                );
-            } catch (HTMLPurifier_VarParserException $e) {
-                throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
-            }
-        }
-
-        if (isset($hash['DESCRIPTION'])) {
-            $directive->description = $hash->offsetGet('DESCRIPTION');
-        }
-
-        if (isset($hash['ALLOWED'])) {
-            $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
-        }
-
-        if (isset($hash['VALUE-ALIASES'])) {
-            $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
-        }
-
-        if (isset($hash['ALIASES'])) {
-            $raw_aliases = trim($hash->offsetGet('ALIASES'));
-            $aliases = preg_split('/\s*,\s*/', $raw_aliases);
-            foreach ($aliases as $alias) {
-                $directive->aliases[] = $this->id($alias);
-            }
-        }
-
-        if (isset($hash['VERSION'])) {
-            $directive->version = $hash->offsetGet('VERSION');
-        }
-
-        if (isset($hash['DEPRECATED-USE'])) {
-            $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
-        }
-
-        if (isset($hash['DEPRECATED-VERSION'])) {
-            $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
-        }
-
-        if (isset($hash['EXTERNAL'])) {
-            $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL')));
-        }
-
-        $interchange->addDirective($directive);
-    }
-
-    /**
-     * Evaluates an array PHP code string without array() wrapper
-     * @param string $contents
-     */
-    protected function evalArray($contents)
-    {
-        return eval('return array(' . $contents . ');');
-    }
-
-    /**
-     * Converts an array list into a lookup array.
-     * @param array $array
-     * @return array
-     */
-    protected function lookup($array)
-    {
-        $ret = array();
-        foreach ($array as $val) {
-            $ret[$val] = true;
-        }
-        return $ret;
-    }
-
-    /**
-     * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
-     * object based on a string Id.
-     * @param string $id
-     * @return HTMLPurifier_ConfigSchema_Interchange_Id
-     */
-    protected function id($id)
-    {
-        return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
-    }
-
-    /**
-     * Triggers errors for any unused keys passed in the hash; such keys
-     * may indicate typos, missing values, etc.
-     * @param HTMLPurifier_StringHash $hash Hash to check.
-     */
-    protected function _findUnused($hash)
-    {
-        $accessed = $hash->getAccessed();
-        foreach ($hash as $k => $v) {
-            if (!isset($accessed[$k])) {
-                trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php
deleted file mode 100644
index fb31277..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php
+++ /dev/null
@@ -1,248 +0,0 @@
-<?php
-
-/**
- * Performs validations on HTMLPurifier_ConfigSchema_Interchange
- *
- * @note If you see '// handled by InterchangeBuilder', that means a
- *       design decision in that class would prevent this validation from
- *       ever being necessary. We have them anyway, however, for
- *       redundancy.
- */
-class HTMLPurifier_ConfigSchema_Validator
-{
-
-    /**
-     * @type HTMLPurifier_ConfigSchema_Interchange
-     */
-    protected $interchange;
-
-    /**
-     * @type array
-     */
-    protected $aliases;
-
-    /**
-     * Context-stack to provide easy to read error messages.
-     * @type array
-     */
-    protected $context = array();
-
-    /**
-     * to test default's type.
-     * @type HTMLPurifier_VarParser
-     */
-    protected $parser;
-
-    public function __construct()
-    {
-        $this->parser = new HTMLPurifier_VarParser();
-    }
-
-    /**
-     * Validates a fully-formed interchange object.
-     * @param HTMLPurifier_ConfigSchema_Interchange $interchange
-     * @return bool
-     */
-    public function validate($interchange)
-    {
-        $this->interchange = $interchange;
-        $this->aliases = array();
-        // PHP is a bit lax with integer <=> string conversions in
-        // arrays, so we don't use the identical !== comparison
-        foreach ($interchange->directives as $i => $directive) {
-            $id = $directive->id->toString();
-            if ($i != $id) {
-                $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
-            }
-            $this->validateDirective($directive);
-        }
-        return true;
-    }
-
-    /**
-     * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
-     * @param HTMLPurifier_ConfigSchema_Interchange_Id $id
-     */
-    public function validateId($id)
-    {
-        $id_string = $id->toString();
-        $this->context[] = "id '$id_string'";
-        if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
-            // handled by InterchangeBuilder
-            $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
-        }
-        // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.)
-        // we probably should check that it has at least one namespace
-        $this->with($id, 'key')
-            ->assertNotEmpty()
-            ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder
-        array_pop($this->context);
-    }
-
-    /**
-     * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object.
-     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
-     */
-    public function validateDirective($d)
-    {
-        $id = $d->id->toString();
-        $this->context[] = "directive '$id'";
-        $this->validateId($d->id);
-
-        $this->with($d, 'description')
-            ->assertNotEmpty();
-
-        // BEGIN - handled by InterchangeBuilder
-        $this->with($d, 'type')
-            ->assertNotEmpty();
-        $this->with($d, 'typeAllowsNull')
-            ->assertIsBool();
-        try {
-            // This also tests validity of $d->type
-            $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
-        } catch (HTMLPurifier_VarParserException $e) {
-            $this->error('default', 'had error: ' . $e->getMessage());
-        }
-        // END - handled by InterchangeBuilder
-
-        if (!is_null($d->allowed) || !empty($d->valueAliases)) {
-            // allowed and valueAliases require that we be dealing with
-            // strings, so check for that early.
-            $d_int = HTMLPurifier_VarParser::$types[$d->type];
-            if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
-                $this->error('type', 'must be a string type when used with allowed or value aliases');
-            }
-        }
-
-        $this->validateDirectiveAllowed($d);
-        $this->validateDirectiveValueAliases($d);
-        $this->validateDirectiveAliases($d);
-
-        array_pop($this->context);
-    }
-
-    /**
-     * Extra validation if $allowed member variable of
-     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
-     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
-     */
-    public function validateDirectiveAllowed($d)
-    {
-        if (is_null($d->allowed)) {
-            return;
-        }
-        $this->with($d, 'allowed')
-            ->assertNotEmpty()
-            ->assertIsLookup(); // handled by InterchangeBuilder
-        if (is_string($d->default) && !isset($d->allowed[$d->default])) {
-            $this->error('default', 'must be an allowed value');
-        }
-        $this->context[] = 'allowed';
-        foreach ($d->allowed as $val => $x) {
-            if (!is_string($val)) {
-                $this->error("value $val", 'must be a string');
-            }
-        }
-        array_pop($this->context);
-    }
-
-    /**
-     * Extra validation if $valueAliases member variable of
-     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
-     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
-     */
-    public function validateDirectiveValueAliases($d)
-    {
-        if (is_null($d->valueAliases)) {
-            return;
-        }
-        $this->with($d, 'valueAliases')
-            ->assertIsArray(); // handled by InterchangeBuilder
-        $this->context[] = 'valueAliases';
-        foreach ($d->valueAliases as $alias => $real) {
-            if (!is_string($alias)) {
-                $this->error("alias $alias", 'must be a string');
-            }
-            if (!is_string($real)) {
-                $this->error("alias target $real from alias '$alias'", 'must be a string');
-            }
-            if ($alias === $real) {
-                $this->error("alias '$alias'", "must not be an alias to itself");
-            }
-        }
-        if (!is_null($d->allowed)) {
-            foreach ($d->valueAliases as $alias => $real) {
-                if (isset($d->allowed[$alias])) {
-                    $this->error("alias '$alias'", 'must not be an allowed value');
-                } elseif (!isset($d->allowed[$real])) {
-                    $this->error("alias '$alias'", 'must be an alias to an allowed value');
-                }
-            }
-        }
-        array_pop($this->context);
-    }
-
-    /**
-     * Extra validation if $aliases member variable of
-     * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
-     * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d
-     */
-    public function validateDirectiveAliases($d)
-    {
-        $this->with($d, 'aliases')
-            ->assertIsArray(); // handled by InterchangeBuilder
-        $this->context[] = 'aliases';
-        foreach ($d->aliases as $alias) {
-            $this->validateId($alias);
-            $s = $alias->toString();
-            if (isset($this->interchange->directives[$s])) {
-                $this->error("alias '$s'", 'collides with another directive');
-            }
-            if (isset($this->aliases[$s])) {
-                $other_directive = $this->aliases[$s];
-                $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
-            }
-            $this->aliases[$s] = $d->id->toString();
-        }
-        array_pop($this->context);
-    }
-
-    // protected helper functions
-
-    /**
-     * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom
-     * for validating simple member variables of objects.
-     * @param $obj
-     * @param $member
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    protected function with($obj, $member)
-    {
-        return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
-    }
-
-    /**
-     * Emits an error, providing helpful context.
-     * @throws HTMLPurifier_ConfigSchema_Exception
-     */
-    protected function error($target, $msg)
-    {
-        if ($target !== false) {
-            $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();
-        } else {
-            $prefix = ucfirst($this->getFormattedContext());
-        }
-        throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
-    }
-
-    /**
-     * Returns a formatted context string.
-     * @return string
-     */
-    protected function getFormattedContext()
-    {
-        return implode(' in ', array_reverse($this->context));
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php
deleted file mode 100644
index c9aa364..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-
-/**
- * Fluent interface for validating the contents of member variables.
- * This should be immutable. See HTMLPurifier_ConfigSchema_Validator for
- * use-cases. We name this an 'atom' because it's ONLY for validations that
- * are independent and usually scalar.
- */
-class HTMLPurifier_ConfigSchema_ValidatorAtom
-{
-    /**
-     * @type string
-     */
-    protected $context;
-
-    /**
-     * @type object
-     */
-    protected $obj;
-
-    /**
-     * @type string
-     */
-    protected $member;
-
-    /**
-     * @type mixed
-     */
-    protected $contents;
-
-    public function __construct($context, $obj, $member)
-    {
-        $this->context = $context;
-        $this->obj = $obj;
-        $this->member = $member;
-        $this->contents =& $obj->$member;
-    }
-
-    /**
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    public function assertIsString()
-    {
-        if (!is_string($this->contents)) {
-            $this->error('must be a string');
-        }
-        return $this;
-    }
-
-    /**
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    public function assertIsBool()
-    {
-        if (!is_bool($this->contents)) {
-            $this->error('must be a boolean');
-        }
-        return $this;
-    }
-
-    /**
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    public function assertIsArray()
-    {
-        if (!is_array($this->contents)) {
-            $this->error('must be an array');
-        }
-        return $this;
-    }
-
-    /**
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    public function assertNotNull()
-    {
-        if ($this->contents === null) {
-            $this->error('must not be null');
-        }
-        return $this;
-    }
-
-    /**
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    public function assertAlnum()
-    {
-        $this->assertIsString();
-        if (!ctype_alnum($this->contents)) {
-            $this->error('must be alphanumeric');
-        }
-        return $this;
-    }
-
-    /**
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    public function assertNotEmpty()
-    {
-        if (empty($this->contents)) {
-            $this->error('must not be empty');
-        }
-        return $this;
-    }
-
-    /**
-     * @return HTMLPurifier_ConfigSchema_ValidatorAtom
-     */
-    public function assertIsLookup()
-    {
-        $this->assertIsArray();
-        foreach ($this->contents as $v) {
-            if ($v !== true) {
-                $this->error('must be a lookup array');
-            }
-        }
-        return $this;
-    }
-
-    /**
-     * @param string $msg
-     * @throws HTMLPurifier_ConfigSchema_Exception
-     */
-    protected function error($msg)
-    {
-        throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser
deleted file mode 100644
index a5426c7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema.ser
+++ /dev/null
@@ -1 +0,0 @@
-O:25:"HTMLPurifier_ConfigSchema":3:{s:8:"defaults";a:127:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:13:"Attr.ID.HTML5";N;s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:19:"CSS.AllowDuplicates";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:29:"Core.AggressivelyRemoveScript";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:23:"Core.AllowParseManyTags";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.ColorKeywords";a:148:{s:9:"aliceblue";s:7:"#F0F8FF";s:12:"antiquewhite";s:7:"#FAEBD7";s:4:"aqua";s:7:"#00FFFF";s:10:"aquamarine";s:7:"#7FFFD4";s:5:"azure";s:7:"#F0FFFF";s:5:"beige";s:7:"#F5F5DC";s:6:"bisque";s:7:"#FFE4C4";s:5:"black";s:7:"#000000";s:14:"blanchedalmond";s:7:"#FFEBCD";s:4:"blue";s:7:"#0000FF";s:10:"blueviolet";s:7:"#8A2BE2";s:5:"brown";s:7:"#A52A2A";s:9:"burlywood";s:7:"#DEB887";s:9:"cadetblue";s:7:"#5F9EA0";s:10:"chartreuse";s:7:"#7FFF00";s:9:"chocolate";s:7:"#D2691E";s:5:"coral";s:7:"#FF7F50";s:14:"cornflowerblue";s:7:"#6495ED";s:8:"cornsilk";s:7:"#FFF8DC";s:7:"crimson";s:7:"#DC143C";s:4:"cyan";s:7:"#00FFFF";s:8:"darkblue";s:7:"#00008B";s:8:"darkcyan";s:7:"#008B8B";s:13:"darkgoldenrod";s:7:"#B8860B";s:8:"darkgray";s:7:"#A9A9A9";s:8:"darkgrey";s:7:"#A9A9A9";s:9:"darkgreen";s:7:"#006400";s:9:"darkkhaki";s:7:"#BDB76B";s:11:"darkmagenta";s:7:"#8B008B";s:14:"darkolivegreen";s:7:"#556B2F";s:10:"darkorange";s:7:"#FF8C00";s:10:"darkorchid";s:7:"#9932CC";s:7:"darkred";s:7:"#8B0000";s:10:"darksalmon";s:7:"#E9967A";s:12:"darkseagreen";s:7:"#8FBC8F";s:13:"darkslateblue";s:7:"#483D8B";s:13:"darkslategray";s:7:"#2F4F4F";s:13:"darkslategrey";s:7:"#2F4F4F";s:13:"darkturquoise";s:7:"#00CED1";s:10:"darkviolet";s:7:"#9400D3";s:8:"deeppink";s:7:"#FF1493";s:11:"deepskyblue";s:7:"#00BFFF";s:7:"dimgray";s:7:"#696969";s:7:"dimgrey";s:7:"#696969";s:10:"dodgerblue";s:7:"#1E90FF";s:9:"firebrick";s:7:"#B22222";s:11:"floralwhite";s:7:"#FFFAF0";s:11:"forestgreen";s:7:"#228B22";s:7:"fuchsia";s:7:"#FF00FF";s:9:"gainsboro";s:7:"#DCDCDC";s:10:"ghostwhite";s:7:"#F8F8FF";s:4:"gold";s:7:"#FFD700";s:9:"goldenrod";s:7:"#DAA520";s:4:"gray";s:7:"#808080";s:4:"grey";s:7:"#808080";s:5:"green";s:7:"#008000";s:11:"greenyellow";s:7:"#ADFF2F";s:8:"honeydew";s:7:"#F0FFF0";s:7:"hotpink";s:7:"#FF69B4";s:9:"indianred";s:7:"#CD5C5C";s:6:"indigo";s:7:"#4B0082";s:5:"ivory";s:7:"#FFFFF0";s:5:"khaki";s:7:"#F0E68C";s:8:"lavender";s:7:"#E6E6FA";s:13:"lavenderblush";s:7:"#FFF0F5";s:9:"lawngreen";s:7:"#7CFC00";s:12:"lemonchiffon";s:7:"#FFFACD";s:9:"lightblue";s:7:"#ADD8E6";s:10:"lightcoral";s:7:"#F08080";s:9:"lightcyan";s:7:"#E0FFFF";s:20:"lightgoldenrodyellow";s:7:"#FAFAD2";s:9:"lightgray";s:7:"#D3D3D3";s:9:"lightgrey";s:7:"#D3D3D3";s:10:"lightgreen";s:7:"#90EE90";s:9:"lightpink";s:7:"#FFB6C1";s:11:"lightsalmon";s:7:"#FFA07A";s:13:"lightseagreen";s:7:"#20B2AA";s:12:"lightskyblue";s:7:"#87CEFA";s:14:"lightslategray";s:7:"#778899";s:14:"lightslategrey";s:7:"#778899";s:14:"lightsteelblue";s:7:"#B0C4DE";s:11:"lightyellow";s:7:"#FFFFE0";s:4:"lime";s:7:"#00FF00";s:9:"limegreen";s:7:"#32CD32";s:5:"linen";s:7:"#FAF0E6";s:7:"magenta";s:7:"#FF00FF";s:6:"maroon";s:7:"#800000";s:16:"mediumaquamarine";s:7:"#66CDAA";s:10:"mediumblue";s:7:"#0000CD";s:12:"mediumorchid";s:7:"#BA55D3";s:12:"mediumpurple";s:7:"#9370DB";s:14:"mediumseagreen";s:7:"#3CB371";s:15:"mediumslateblue";s:7:"#7B68EE";s:17:"mediumspringgreen";s:7:"#00FA9A";s:15:"mediumturquoise";s:7:"#48D1CC";s:15:"mediumvioletred";s:7:"#C71585";s:12:"midnightblue";s:7:"#191970";s:9:"mintcream";s:7:"#F5FFFA";s:9:"mistyrose";s:7:"#FFE4E1";s:8:"moccasin";s:7:"#FFE4B5";s:11:"navajowhite";s:7:"#FFDEAD";s:4:"navy";s:7:"#000080";s:7:"oldlace";s:7:"#FDF5E6";s:5:"olive";s:7:"#808000";s:9:"olivedrab";s:7:"#6B8E23";s:6:"orange";s:7:"#FFA500";s:9:"orangered";s:7:"#FF4500";s:6:"orchid";s:7:"#DA70D6";s:13:"palegoldenrod";s:7:"#EEE8AA";s:9:"palegreen";s:7:"#98FB98";s:13:"paleturquoise";s:7:"#AFEEEE";s:13:"palevioletred";s:7:"#DB7093";s:10:"papayawhip";s:7:"#FFEFD5";s:9:"peachpuff";s:7:"#FFDAB9";s:4:"peru";s:7:"#CD853F";s:4:"pink";s:7:"#FFC0CB";s:4:"plum";s:7:"#DDA0DD";s:10:"powderblue";s:7:"#B0E0E6";s:6:"purple";s:7:"#800080";s:13:"rebeccapurple";s:7:"#663399";s:3:"red";s:7:"#FF0000";s:9:"rosybrown";s:7:"#BC8F8F";s:9:"royalblue";s:7:"#4169E1";s:11:"saddlebrown";s:7:"#8B4513";s:6:"salmon";s:7:"#FA8072";s:10:"sandybrown";s:7:"#F4A460";s:8:"seagreen";s:7:"#2E8B57";s:8:"seashell";s:7:"#FFF5EE";s:6:"sienna";s:7:"#A0522D";s:6:"silver";s:7:"#C0C0C0";s:7:"skyblue";s:7:"#87CEEB";s:9:"slateblue";s:7:"#6A5ACD";s:9:"slategray";s:7:"#708090";s:9:"slategrey";s:7:"#708090";s:4:"snow";s:7:"#FFFAFA";s:11:"springgreen";s:7:"#00FF7F";s:9:"steelblue";s:7:"#4682B4";s:3:"tan";s:7:"#D2B48C";s:4:"teal";s:7:"#008080";s:7:"thistle";s:7:"#D8BFD8";s:6:"tomato";s:7:"#FF6347";s:9:"turquoise";s:7:"#40E0D0";s:6:"violet";s:7:"#EE82EE";s:5:"wheat";s:7:"#F5DEB3";s:5:"white";s:7:"#FFFFFF";s:10:"whitesmoke";s:7:"#F5F5F5";s:6:"yellow";s:7:"#FFFF00";s:11:"yellowgreen";s:7:"#9ACD32";}s:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:24:"Core.LegacyEntityDecoder";b:0;s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:10:"HTML.Forms";b:0;s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:19:"HTML.TargetNoopener";b:1;s:21:"HTML.TargetNoreferrer";b:1;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.AllowedSchemes";a:7:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;s:3:"tel";b:1;}s:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:12:"defaultPlist";O:25:"HTMLPurifier_PropertyList":3:{s:7:"�*�data";a:127:{s:19:"Attr.AllowedClasses";N;s:24:"Attr.AllowedFrameTargets";a:0:{}s:15:"Attr.AllowedRel";a:0:{}s:15:"Attr.AllowedRev";a:0:{}s:18:"Attr.ClassUseCDATA";N;s:20:"Attr.DefaultImageAlt";N;s:24:"Attr.DefaultInvalidImage";s:0:"";s:27:"Attr.DefaultInvalidImageAlt";s:13:"Invalid image";s:19:"Attr.DefaultTextDir";s:3:"ltr";s:13:"Attr.EnableID";b:0;s:21:"Attr.ForbiddenClasses";a:0:{}s:13:"Attr.ID.HTML5";N;s:16:"Attr.IDBlacklist";a:0:{}s:22:"Attr.IDBlacklistRegexp";N;s:13:"Attr.IDPrefix";s:0:"";s:18:"Attr.IDPrefixLocal";s:0:"";s:24:"AutoFormat.AutoParagraph";b:0;s:17:"AutoFormat.Custom";a:0:{}s:25:"AutoFormat.DisplayLinkURI";b:0;s:18:"AutoFormat.Linkify";b:0;s:33:"AutoFormat.PurifierLinkify.DocURL";s:3:"#%s";s:26:"AutoFormat.PurifierLinkify";b:0;s:32:"AutoFormat.RemoveEmpty.Predicate";a:4:{s:8:"colgroup";a:0:{}s:2:"th";a:0:{}s:2:"td";a:0:{}s:6:"iframe";a:1:{i:0;s:3:"src";}}s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";a:2:{s:2:"td";b:1;s:2:"th";b:1;}s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";b:0;s:22:"AutoFormat.RemoveEmpty";b:0;s:39:"AutoFormat.RemoveSpansWithoutAttributes";b:0;s:19:"CSS.AllowDuplicates";b:0;s:18:"CSS.AllowImportant";b:0;s:15:"CSS.AllowTricky";b:0;s:16:"CSS.AllowedFonts";N;s:21:"CSS.AllowedProperties";N;s:17:"CSS.DefinitionRev";i:1;s:23:"CSS.ForbiddenProperties";a:0:{}s:16:"CSS.MaxImgLength";s:6:"1200px";s:15:"CSS.Proprietary";b:0;s:11:"CSS.Trusted";b:0;s:20:"Cache.DefinitionImpl";s:10:"Serializer";s:20:"Cache.SerializerPath";N;s:27:"Cache.SerializerPermissions";i:493;s:22:"Core.AggressivelyFixLt";b:1;s:29:"Core.AggressivelyRemoveScript";b:1;s:28:"Core.AllowHostnameUnderscore";b:0;s:23:"Core.AllowParseManyTags";b:0;s:18:"Core.CollectErrors";b:0;s:18:"Core.ColorKeywords";a:148:{s:9:"aliceblue";s:7:"#F0F8FF";s:12:"antiquewhite";s:7:"#FAEBD7";s:4:"aqua";s:7:"#00FFFF";s:10:"aquamarine";s:7:"#7FFFD4";s:5:"azure";s:7:"#F0FFFF";s:5:"beige";s:7:"#F5F5DC";s:6:"bisque";s:7:"#FFE4C4";s:5:"black";s:7:"#000000";s:14:"blanchedalmond";s:7:"#FFEBCD";s:4:"blue";s:7:"#0000FF";s:10:"blueviolet";s:7:"#8A2BE2";s:5:"brown";s:7:"#A52A2A";s:9:"burlywood";s:7:"#DEB887";s:9:"cadetblue";s:7:"#5F9EA0";s:10:"chartreuse";s:7:"#7FFF00";s:9:"chocolate";s:7:"#D2691E";s:5:"coral";s:7:"#FF7F50";s:14:"cornflowerblue";s:7:"#6495ED";s:8:"cornsilk";s:7:"#FFF8DC";s:7:"crimson";s:7:"#DC143C";s:4:"cyan";s:7:"#00FFFF";s:8:"darkblue";s:7:"#00008B";s:8:"darkcyan";s:7:"#008B8B";s:13:"darkgoldenrod";s:7:"#B8860B";s:8:"darkgray";s:7:"#A9A9A9";s:8:"darkgrey";s:7:"#A9A9A9";s:9:"darkgreen";s:7:"#006400";s:9:"darkkhaki";s:7:"#BDB76B";s:11:"darkmagenta";s:7:"#8B008B";s:14:"darkolivegreen";s:7:"#556B2F";s:10:"darkorange";s:7:"#FF8C00";s:10:"darkorchid";s:7:"#9932CC";s:7:"darkred";s:7:"#8B0000";s:10:"darksalmon";s:7:"#E9967A";s:12:"darkseagreen";s:7:"#8FBC8F";s:13:"darkslateblue";s:7:"#483D8B";s:13:"darkslategray";s:7:"#2F4F4F";s:13:"darkslategrey";s:7:"#2F4F4F";s:13:"darkturquoise";s:7:"#00CED1";s:10:"darkviolet";s:7:"#9400D3";s:8:"deeppink";s:7:"#FF1493";s:11:"deepskyblue";s:7:"#00BFFF";s:7:"dimgray";s:7:"#696969";s:7:"dimgrey";s:7:"#696969";s:10:"dodgerblue";s:7:"#1E90FF";s:9:"firebrick";s:7:"#B22222";s:11:"floralwhite";s:7:"#FFFAF0";s:11:"forestgreen";s:7:"#228B22";s:7:"fuchsia";s:7:"#FF00FF";s:9:"gainsboro";s:7:"#DCDCDC";s:10:"ghostwhite";s:7:"#F8F8FF";s:4:"gold";s:7:"#FFD700";s:9:"goldenrod";s:7:"#DAA520";s:4:"gray";s:7:"#808080";s:4:"grey";s:7:"#808080";s:5:"green";s:7:"#008000";s:11:"greenyellow";s:7:"#ADFF2F";s:8:"honeydew";s:7:"#F0FFF0";s:7:"hotpink";s:7:"#FF69B4";s:9:"indianred";s:7:"#CD5C5C";s:6:"indigo";s:7:"#4B0082";s:5:"ivory";s:7:"#FFFFF0";s:5:"khaki";s:7:"#F0E68C";s:8:"lavender";s:7:"#E6E6FA";s:13:"lavenderblush";s:7:"#FFF0F5";s:9:"lawngreen";s:7:"#7CFC00";s:12:"lemonchiffon";s:7:"#FFFACD";s:9:"lightblue";s:7:"#ADD8E6";s:10:"lightcoral";s:7:"#F08080";s:9:"lightcyan";s:7:"#E0FFFF";s:20:"lightgoldenrodyellow";s:7:"#FAFAD2";s:9:"lightgray";s:7:"#D3D3D3";s:9:"lightgrey";s:7:"#D3D3D3";s:10:"lightgreen";s:7:"#90EE90";s:9:"lightpink";s:7:"#FFB6C1";s:11:"lightsalmon";s:7:"#FFA07A";s:13:"lightseagreen";s:7:"#20B2AA";s:12:"lightskyblue";s:7:"#87CEFA";s:14:"lightslategray";s:7:"#778899";s:14:"lightslategrey";s:7:"#778899";s:14:"lightsteelblue";s:7:"#B0C4DE";s:11:"lightyellow";s:7:"#FFFFE0";s:4:"lime";s:7:"#00FF00";s:9:"limegreen";s:7:"#32CD32";s:5:"linen";s:7:"#FAF0E6";s:7:"magenta";s:7:"#FF00FF";s:6:"maroon";s:7:"#800000";s:16:"mediumaquamarine";s:7:"#66CDAA";s:10:"mediumblue";s:7:"#0000CD";s:12:"mediumorchid";s:7:"#BA55D3";s:12:"mediumpurple";s:7:"#9370DB";s:14:"mediumseagreen";s:7:"#3CB371";s:15:"mediumslateblue";s:7:"#7B68EE";s:17:"mediumspringgreen";s:7:"#00FA9A";s:15:"mediumturquoise";s:7:"#48D1CC";s:15:"mediumvioletred";s:7:"#C71585";s:12:"midnightblue";s:7:"#191970";s:9:"mintcream";s:7:"#F5FFFA";s:9:"mistyrose";s:7:"#FFE4E1";s:8:"moccasin";s:7:"#FFE4B5";s:11:"navajowhite";s:7:"#FFDEAD";s:4:"navy";s:7:"#000080";s:7:"oldlace";s:7:"#FDF5E6";s:5:"olive";s:7:"#808000";s:9:"olivedrab";s:7:"#6B8E23";s:6:"orange";s:7:"#FFA500";s:9:"orangered";s:7:"#FF4500";s:6:"orchid";s:7:"#DA70D6";s:13:"palegoldenrod";s:7:"#EEE8AA";s:9:"palegreen";s:7:"#98FB98";s:13:"paleturquoise";s:7:"#AFEEEE";s:13:"palevioletred";s:7:"#DB7093";s:10:"papayawhip";s:7:"#FFEFD5";s:9:"peachpuff";s:7:"#FFDAB9";s:4:"peru";s:7:"#CD853F";s:4:"pink";s:7:"#FFC0CB";s:4:"plum";s:7:"#DDA0DD";s:10:"powderblue";s:7:"#B0E0E6";s:6:"purple";s:7:"#800080";s:13:"rebeccapurple";s:7:"#663399";s:3:"red";s:7:"#FF0000";s:9:"rosybrown";s:7:"#BC8F8F";s:9:"royalblue";s:7:"#4169E1";s:11:"saddlebrown";s:7:"#8B4513";s:6:"salmon";s:7:"#FA8072";s:10:"sandybrown";s:7:"#F4A460";s:8:"seagreen";s:7:"#2E8B57";s:8:"seashell";s:7:"#FFF5EE";s:6:"sienna";s:7:"#A0522D";s:6:"silver";s:7:"#C0C0C0";s:7:"skyblue";s:7:"#87CEEB";s:9:"slateblue";s:7:"#6A5ACD";s:9:"slategray";s:7:"#708090";s:9:"slategrey";s:7:"#708090";s:4:"snow";s:7:"#FFFAFA";s:11:"springgreen";s:7:"#00FF7F";s:9:"steelblue";s:7:"#4682B4";s:3:"tan";s:7:"#D2B48C";s:4:"teal";s:7:"#008080";s:7:"thistle";s:7:"#D8BFD8";s:6:"tomato";s:7:"#FF6347";s:9:"turquoise";s:7:"#40E0D0";s:6:"violet";s:7:"#EE82EE";s:5:"wheat";s:7:"#F5DEB3";s:5:"white";s:7:"#FFFFFF";s:10:"whitesmoke";s:7:"#F5F5F5";s:6:"yellow";s:7:"#FFFF00";s:11:"yellowgreen";s:7:"#9ACD32";}s:30:"Core.ConvertDocumentToFragment";b:1;s:36:"Core.DirectLexLineNumberSyncInterval";i:0;s:20:"Core.DisableExcludes";b:0;s:15:"Core.EnableIDNA";b:0;s:13:"Core.Encoding";s:5:"utf-8";s:26:"Core.EscapeInvalidChildren";b:0;s:22:"Core.EscapeInvalidTags";b:0;s:29:"Core.EscapeNonASCIICharacters";b:0;s:19:"Core.HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:13:"Core.Language";s:2:"en";s:24:"Core.LegacyEntityDecoder";b:0;s:14:"Core.LexerImpl";N;s:24:"Core.MaintainLineNumbers";N;s:22:"Core.NormalizeNewlines";b:1;s:21:"Core.RemoveInvalidImg";b:1;s:33:"Core.RemoveProcessingInstructions";b:0;s:25:"Core.RemoveScriptContents";N;s:13:"Filter.Custom";a:0:{}s:34:"Filter.ExtractStyleBlocks.Escaping";b:1;s:31:"Filter.ExtractStyleBlocks.Scope";N;s:34:"Filter.ExtractStyleBlocks.TidyImpl";N;s:25:"Filter.ExtractStyleBlocks";b:0;s:14:"Filter.YouTube";b:0;s:12:"HTML.Allowed";N;s:22:"HTML.AllowedAttributes";N;s:20:"HTML.AllowedComments";a:0:{}s:26:"HTML.AllowedCommentsRegexp";N;s:20:"HTML.AllowedElements";N;s:19:"HTML.AllowedModules";N;s:23:"HTML.Attr.Name.UseCDATA";b:0;s:17:"HTML.BlockWrapper";s:1:"p";s:16:"HTML.CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:18:"HTML.CustomDoctype";N;s:17:"HTML.DefinitionID";N;s:18:"HTML.DefinitionRev";i:1;s:12:"HTML.Doctype";N;s:25:"HTML.FlashAllowFullScreen";b:0;s:24:"HTML.ForbiddenAttributes";a:0:{}s:22:"HTML.ForbiddenElements";a:0:{}s:10:"HTML.Forms";b:0;s:17:"HTML.MaxImgLength";i:1200;s:13:"HTML.Nofollow";b:0;s:11:"HTML.Parent";s:3:"div";s:16:"HTML.Proprietary";b:0;s:14:"HTML.SafeEmbed";b:0;s:15:"HTML.SafeIframe";b:0;s:15:"HTML.SafeObject";b:0;s:18:"HTML.SafeScripting";a:0:{}s:11:"HTML.Strict";b:0;s:16:"HTML.TargetBlank";b:0;s:19:"HTML.TargetNoopener";b:1;s:21:"HTML.TargetNoreferrer";b:1;s:12:"HTML.TidyAdd";a:0:{}s:14:"HTML.TidyLevel";s:6:"medium";s:15:"HTML.TidyRemove";a:0:{}s:12:"HTML.Trusted";b:0;s:10:"HTML.XHTML";b:1;s:28:"Output.CommentScriptContents";b:1;s:19:"Output.FixInnerHTML";b:1;s:18:"Output.FlashCompat";b:0;s:14:"Output.Newline";N;s:15:"Output.SortAttr";b:0;s:17:"Output.TidyFormat";b:0;s:17:"Test.ForceNoIconv";b:0;s:18:"URI.AllowedSchemes";a:7:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;s:3:"tel";b:1;}s:8:"URI.Base";N;s:17:"URI.DefaultScheme";s:4:"http";s:16:"URI.DefinitionID";N;s:17:"URI.DefinitionRev";i:1;s:11:"URI.Disable";b:0;s:19:"URI.DisableExternal";b:0;s:28:"URI.DisableExternalResources";b:0;s:20:"URI.DisableResources";b:0;s:8:"URI.Host";N;s:17:"URI.HostBlacklist";a:0:{}s:16:"URI.MakeAbsolute";b:0;s:9:"URI.Munge";N;s:18:"URI.MungeResources";b:0;s:18:"URI.MungeSecretKey";N;s:26:"URI.OverrideAllowedSchemes";b:1;s:20:"URI.SafeIframeRegexp";N;}s:9:"�*�parent";N;s:8:"�*�cache";N;}s:4:"info";a:140:{s:19:"Attr.AllowedClasses";i:-8;s:24:"Attr.AllowedFrameTargets";i:8;s:15:"Attr.AllowedRel";i:8;s:15:"Attr.AllowedRev";i:8;s:18:"Attr.ClassUseCDATA";i:-7;s:20:"Attr.DefaultImageAlt";i:-1;s:24:"Attr.DefaultInvalidImage";i:1;s:27:"Attr.DefaultInvalidImageAlt";i:1;s:19:"Attr.DefaultTextDir";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:2:{s:3:"ltr";b:1;s:3:"rtl";b:1;}}s:13:"Attr.EnableID";i:7;s:17:"HTML.EnableAttrID";O:8:"stdClass":2:{s:3:"key";s:13:"Attr.EnableID";s:7:"isAlias";b:1;}s:21:"Attr.ForbiddenClasses";i:8;s:13:"Attr.ID.HTML5";i:-7;s:16:"Attr.IDBlacklist";i:9;s:22:"Attr.IDBlacklistRegexp";i:-1;s:13:"Attr.IDPrefix";i:1;s:18:"Attr.IDPrefixLocal";i:1;s:24:"AutoFormat.AutoParagraph";i:7;s:17:"AutoFormat.Custom";i:9;s:25:"AutoFormat.DisplayLinkURI";i:7;s:18:"AutoFormat.Linkify";i:7;s:33:"AutoFormat.PurifierLinkify.DocURL";i:1;s:37:"AutoFormatParam.PurifierLinkifyDocURL";O:8:"stdClass":2:{s:3:"key";s:33:"AutoFormat.PurifierLinkify.DocURL";s:7:"isAlias";b:1;}s:26:"AutoFormat.PurifierLinkify";i:7;s:32:"AutoFormat.RemoveEmpty.Predicate";i:10;s:44:"AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions";i:8;s:33:"AutoFormat.RemoveEmpty.RemoveNbsp";i:7;s:22:"AutoFormat.RemoveEmpty";i:7;s:39:"AutoFormat.RemoveSpansWithoutAttributes";i:7;s:19:"CSS.AllowDuplicates";i:7;s:18:"CSS.AllowImportant";i:7;s:15:"CSS.AllowTricky";i:7;s:16:"CSS.AllowedFonts";i:-8;s:21:"CSS.AllowedProperties";i:-8;s:17:"CSS.DefinitionRev";i:5;s:23:"CSS.ForbiddenProperties";i:8;s:16:"CSS.MaxImgLength";i:-1;s:15:"CSS.Proprietary";i:7;s:11:"CSS.Trusted";i:7;s:20:"Cache.DefinitionImpl";i:-1;s:20:"Core.DefinitionCache";O:8:"stdClass":2:{s:3:"key";s:20:"Cache.DefinitionImpl";s:7:"isAlias";b:1;}s:20:"Cache.SerializerPath";i:-1;s:27:"Cache.SerializerPermissions";i:-5;s:22:"Core.AggressivelyFixLt";i:7;s:29:"Core.AggressivelyRemoveScript";i:7;s:28:"Core.AllowHostnameUnderscore";i:7;s:23:"Core.AllowParseManyTags";i:7;s:18:"Core.CollectErrors";i:7;s:18:"Core.ColorKeywords";i:10;s:30:"Core.ConvertDocumentToFragment";i:7;s:24:"Core.AcceptFullDocuments";O:8:"stdClass":2:{s:3:"key";s:30:"Core.ConvertDocumentToFragment";s:7:"isAlias";b:1;}s:36:"Core.DirectLexLineNumberSyncInterval";i:5;s:20:"Core.DisableExcludes";i:7;s:15:"Core.EnableIDNA";i:7;s:13:"Core.Encoding";i:2;s:26:"Core.EscapeInvalidChildren";i:7;s:22:"Core.EscapeInvalidTags";i:7;s:29:"Core.EscapeNonASCIICharacters";i:7;s:19:"Core.HiddenElements";i:8;s:13:"Core.Language";i:1;s:24:"Core.LegacyEntityDecoder";i:7;s:14:"Core.LexerImpl";i:-11;s:24:"Core.MaintainLineNumbers";i:-7;s:22:"Core.NormalizeNewlines";i:7;s:21:"Core.RemoveInvalidImg";i:7;s:33:"Core.RemoveProcessingInstructions";i:7;s:25:"Core.RemoveScriptContents";i:-7;s:13:"Filter.Custom";i:9;s:34:"Filter.ExtractStyleBlocks.Escaping";i:7;s:33:"Filter.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:38:"FilterParam.ExtractStyleBlocksEscaping";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.Escaping";s:7:"isAlias";b:1;}s:31:"Filter.ExtractStyleBlocks.Scope";i:-1;s:30:"Filter.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:35:"FilterParam.ExtractStyleBlocksScope";O:8:"stdClass":2:{s:3:"key";s:31:"Filter.ExtractStyleBlocks.Scope";s:7:"isAlias";b:1;}s:34:"Filter.ExtractStyleBlocks.TidyImpl";i:-11;s:38:"FilterParam.ExtractStyleBlocksTidyImpl";O:8:"stdClass":2:{s:3:"key";s:34:"Filter.ExtractStyleBlocks.TidyImpl";s:7:"isAlias";b:1;}s:25:"Filter.ExtractStyleBlocks";i:7;s:14:"Filter.YouTube";i:7;s:12:"HTML.Allowed";i:-4;s:22:"HTML.AllowedAttributes";i:-8;s:20:"HTML.AllowedComments";i:8;s:26:"HTML.AllowedCommentsRegexp";i:-1;s:20:"HTML.AllowedElements";i:-8;s:19:"HTML.AllowedModules";i:-8;s:23:"HTML.Attr.Name.UseCDATA";i:7;s:17:"HTML.BlockWrapper";i:1;s:16:"HTML.CoreModules";i:8;s:18:"HTML.CustomDoctype";i:-1;s:17:"HTML.DefinitionID";i:-1;s:18:"HTML.DefinitionRev";i:5;s:12:"HTML.Doctype";O:8:"stdClass":3:{s:4:"type";i:1;s:10:"allow_null";b:1;s:7:"allowed";a:5:{s:22:"HTML 4.01 Transitional";b:1;s:16:"HTML 4.01 Strict";b:1;s:22:"XHTML 1.0 Transitional";b:1;s:16:"XHTML 1.0 Strict";b:1;s:9:"XHTML 1.1";b:1;}}s:25:"HTML.FlashAllowFullScreen";i:7;s:24:"HTML.ForbiddenAttributes";i:8;s:22:"HTML.ForbiddenElements";i:8;s:10:"HTML.Forms";i:7;s:17:"HTML.MaxImgLength";i:-5;s:13:"HTML.Nofollow";i:7;s:11:"HTML.Parent";i:1;s:16:"HTML.Proprietary";i:7;s:14:"HTML.SafeEmbed";i:7;s:15:"HTML.SafeIframe";i:7;s:15:"HTML.SafeObject";i:7;s:18:"HTML.SafeScripting";i:8;s:11:"HTML.Strict";i:7;s:16:"HTML.TargetBlank";i:7;s:19:"HTML.TargetNoopener";i:7;s:21:"HTML.TargetNoreferrer";i:7;s:12:"HTML.TidyAdd";i:8;s:14:"HTML.TidyLevel";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:4:{s:4:"none";b:1;s:5:"light";b:1;s:6:"medium";b:1;s:5:"heavy";b:1;}}s:15:"HTML.TidyRemove";i:8;s:12:"HTML.Trusted";i:7;s:10:"HTML.XHTML";i:7;s:10:"Core.XHTML";O:8:"stdClass":2:{s:3:"key";s:10:"HTML.XHTML";s:7:"isAlias";b:1;}s:28:"Output.CommentScriptContents";i:7;s:26:"Core.CommentScriptContents";O:8:"stdClass":2:{s:3:"key";s:28:"Output.CommentScriptContents";s:7:"isAlias";b:1;}s:19:"Output.FixInnerHTML";i:7;s:18:"Output.FlashCompat";i:7;s:14:"Output.Newline";i:-1;s:15:"Output.SortAttr";i:7;s:17:"Output.TidyFormat";i:7;s:15:"Core.TidyFormat";O:8:"stdClass":2:{s:3:"key";s:17:"Output.TidyFormat";s:7:"isAlias";b:1;}s:17:"Test.ForceNoIconv";i:7;s:18:"URI.AllowedSchemes";i:8;s:8:"URI.Base";i:-1;s:17:"URI.DefaultScheme";i:-1;s:16:"URI.DefinitionID";i:-1;s:17:"URI.DefinitionRev";i:5;s:11:"URI.Disable";i:7;s:15:"Attr.DisableURI";O:8:"stdClass":2:{s:3:"key";s:11:"URI.Disable";s:7:"isAlias";b:1;}s:19:"URI.DisableExternal";i:7;s:28:"URI.DisableExternalResources";i:7;s:20:"URI.DisableResources";i:7;s:8:"URI.Host";i:-1;s:17:"URI.HostBlacklist";i:9;s:16:"URI.MakeAbsolute";i:7;s:9:"URI.Munge";i:-1;s:18:"URI.MungeResources";i:7;s:18:"URI.MungeSecretKey";i:-1;s:26:"URI.OverrideAllowedSchemes";i:7;s:20:"URI.SafeIframeRegexp";i:-1;}}
\ No newline at end of file
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt
deleted file mode 100644
index 0517fed..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Attr.AllowedClasses
-TYPE: lookup/null
-VERSION: 4.0.0
-DEFAULT: null
---DESCRIPTION--
-List of allowed class values in the class attribute. By default, this is null,
-which means all classes are allowed.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt
deleted file mode 100644
index 249edd6..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Attr.AllowedFrameTargets
-TYPE: lookup
-DEFAULT: array()
---DESCRIPTION--
-Lookup table of all allowed link frame targets.  Some commonly used link
-targets include _blank, _self, _parent and _top. Values should be
-lowercase, as validation will be done in a case-sensitive manner despite
-W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute
-so this directive will have no effect in that doctype. XHTML 1.1 does not
-enable the Target module by default, you will have to manually enable it
-(see the module documentation for more details.)
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt
deleted file mode 100644
index 9a8fa6a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.AllowedRel
-TYPE: lookup
-VERSION: 1.6.0
-DEFAULT: array()
---DESCRIPTION--
-List of allowed forward document relationships in the rel attribute. Common
-values may be nofollow or print. By default, this is empty, meaning that no
-document relationships are allowed.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt
deleted file mode 100644
index b017883..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.AllowedRev
-TYPE: lookup
-VERSION: 1.6.0
-DEFAULT: array()
---DESCRIPTION--
-List of allowed reverse document relationships in the rev attribute. This
-attribute is a bit of an edge-case; if you don't know what it is for, stay
-away.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt
deleted file mode 100644
index e774b82..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Attr.ClassUseCDATA
-TYPE: bool/null
-DEFAULT: null
-VERSION: 4.0.0
---DESCRIPTION--
-If null, class will auto-detect the doctype and, if matching XHTML 1.1 or
-XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise,
-it will use a relaxed CDATA definition.  If true, the relaxed CDATA definition
-is forced; if false, the NMTOKENS definition is forced.  To get behavior
-of HTML Purifier prior to 4.0.0, set this directive to false.
-
-Some rational behind the auto-detection:
-in previous versions of HTML Purifier, it was assumed that the form of
-class was NMTOKENS, as specified by the XHTML Modularization (representing
-XHTML 1.1 and XHTML 2.0).  The DTDs for HTML 4.01 and XHTML 1.0, however
-specify class as CDATA.  HTML 5 effectively defines it as CDATA, but
-with the additional constraint that each name should be unique (this is not
-explicitly outlined in previous specifications).
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt
deleted file mode 100644
index 533165e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Attr.DefaultImageAlt
-TYPE: string/null
-DEFAULT: null
-VERSION: 3.2.0
---DESCRIPTION--
-This is the content of the alt tag of an image if the user had not
-previously specified an alt attribute.  This applies to all images without
-a valid alt attribute, as opposed to %Attr.DefaultInvalidImageAlt, which
-only applies to invalid images, and overrides in the case of an invalid image.
-Default behavior with null is to use the basename of the src tag for the alt.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt
deleted file mode 100644
index 9eb7e38..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.DefaultInvalidImage
-TYPE: string
-DEFAULT: ''
---DESCRIPTION--
-This is the default image an img tag will be pointed to if it does not have
-a valid src attribute.  In future versions, we may allow the image tag to
-be removed completely, but due to design issues, this is not possible right
-now.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt
deleted file mode 100644
index 2f17bf4..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Attr.DefaultInvalidImageAlt
-TYPE: string
-DEFAULT: 'Invalid image'
---DESCRIPTION--
-This is the content of the alt tag of an invalid image if the user had not
-previously specified an alt attribute.  It has no effect when the image is
-valid but there was no alt attribute present.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt
deleted file mode 100644
index 52654b5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Attr.DefaultTextDir
-TYPE: string
-DEFAULT: 'ltr'
---DESCRIPTION--
-Defines the default text direction (ltr or rtl) of the document being
-parsed.  This generally is the same as the value of the dir attribute in
-HTML, or ltr if that is not specified.
---ALLOWED--
-'ltr', 'rtl'
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt
deleted file mode 100644
index 6440d21..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Attr.EnableID
-TYPE: bool
-DEFAULT: false
-VERSION: 1.2.0
---DESCRIPTION--
-Allows the ID attribute in HTML.  This is disabled by default due to the
-fact that without proper configuration user input can easily break the
-validation of a webpage by specifying an ID that is already on the
-surrounding HTML.  If you don't mind throwing caution to the wind, enable
-this directive, but I strongly recommend you also consider blacklisting IDs
-you use (%Attr.IDBlacklist) or prefixing all user supplied IDs
-(%Attr.IDPrefix).  When set to true HTML Purifier reverts to the behavior of
-pre-1.2.0 versions.
---ALIASES--
-HTML.EnableAttrID
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt
deleted file mode 100644
index f31d226..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Attr.ForbiddenClasses
-TYPE: lookup
-VERSION: 4.0.0
-DEFAULT: array()
---DESCRIPTION--
-List of forbidden class values in the class attribute. By default, this is
-empty, which means that no classes are forbidden. See also %Attr.AllowedClasses.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
deleted file mode 100644
index 735d4b7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Attr.ID.HTML5
-TYPE: bool/null
-DEFAULT: null
-VERSION: 4.8.0
---DESCRIPTION--
-In HTML5, restrictions on the format of the id attribute have been significantly
-relaxed, such that any string is valid so long as it contains no spaces and
-is at least one character.  In lieu of a general HTML5 compatibility flag,
-set this configuration directive to true to use the relaxed rules.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt
deleted file mode 100644
index 5f2b5e3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Attr.IDBlacklist
-TYPE: list
-DEFAULT: array()
-DESCRIPTION: Array of IDs not allowed in the document.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt
deleted file mode 100644
index 6f58245..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Attr.IDBlacklistRegexp
-TYPE: string/null
-VERSION: 1.6.0
-DEFAULT: NULL
---DESCRIPTION--
-PCRE regular expression to be matched against all IDs. If the expression is
-matches, the ID is rejected. Use this with care: may cause significant
-degradation. ID matching is done after all other validation.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt
deleted file mode 100644
index cc49d43..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Attr.IDPrefix
-TYPE: string
-VERSION: 1.2.0
-DEFAULT: ''
---DESCRIPTION--
-String to prefix to IDs.  If you have no idea what IDs your pages may use,
-you may opt to simply add a prefix to all user-submitted ID attributes so
-that they are still usable, but will not conflict with core page IDs.
-Example: setting the directive to 'user_' will result in a user submitted
-'foo' to become 'user_foo'  Be sure to set %HTML.EnableAttrID to true
-before using this.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt
deleted file mode 100644
index 2c5924a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Attr.IDPrefixLocal
-TYPE: string
-VERSION: 1.2.0
-DEFAULT: ''
---DESCRIPTION--
-Temporary prefix for IDs used in conjunction with %Attr.IDPrefix.  If you
-need to allow multiple sets of user content on web page, you may need to
-have a seperate prefix that changes with each iteration.  This way,
-seperately submitted user content displayed on the same page doesn't
-clobber each other. Ideal values are unique identifiers for the content it
-represents (i.e. the id of the row in the database). Be sure to add a
-seperator (like an underscore) at the end.  Warning: this directive will
-not work unless %Attr.IDPrefix is set to a non-empty value!
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt
deleted file mode 100644
index d5caa1b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-AutoFormat.AutoParagraph
-TYPE: bool
-VERSION: 2.0.1
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-  This directive turns on auto-paragraphing, where double newlines are
-  converted in to paragraphs whenever possible. Auto-paragraphing:
-</p>
-<ul>
-  <li>Always applies to inline elements or text in the root node,</li>
-  <li>Applies to inline elements or text with double newlines in nodes
-      that allow paragraph tags,</li>
-  <li>Applies to double newlines in paragraph tags</li>
-</ul>
-<p>
-  <code>p</code> tags must be allowed for this directive to take effect.
-  We do not use <code>br</code> tags for paragraphing, as that is
-  semantically incorrect.
-</p>
-<p>
-  To prevent auto-paragraphing as a content-producer, refrain from using
-  double-newlines except to specify a new paragraph or in contexts where
-  it has special meaning (whitespace usually has no meaning except in
-  tags like <code>pre</code>, so this should not be difficult.) To prevent
-  the paragraphing of inline text adjacent to block elements, wrap them
-  in <code>div</code> tags (the behavior is slightly different outside of
-  the root node.)
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt
deleted file mode 100644
index 2a47648..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.Custom
-TYPE: list
-VERSION: 2.0.1
-DEFAULT: array()
---DESCRIPTION--
-
-<p>
-  This directive can be used to add custom auto-format injectors.
-  Specify an array of injector names (class name minus the prefix)
-  or concrete implementations. Injector class must exist.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt
deleted file mode 100644
index 663064a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-AutoFormat.DisplayLinkURI
-TYPE: bool
-VERSION: 3.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  This directive turns on the in-text display of URIs in &lt;a&gt; tags, and disables
-  those links. For example, <a href="http://example.com">example</a> becomes
-  example (<a>http://example.com</a>).
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt
deleted file mode 100644
index 3a48ba9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.Linkify
-TYPE: bool
-VERSION: 2.0.1
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-  This directive turns on linkification, auto-linking http, ftp and
-  https URLs. <code>a</code> tags with the <code>href</code> attribute
-  must be allowed.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt
deleted file mode 100644
index db58b13..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.PurifierLinkify.DocURL
-TYPE: string
-VERSION: 2.0.1
-DEFAULT: '#%s'
-ALIASES: AutoFormatParam.PurifierLinkifyDocURL
---DESCRIPTION--
-<p>
-  Location of configuration documentation to link to, let %s substitute
-  into the configuration's namespace and directive names sans the percent
-  sign.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt
deleted file mode 100644
index 7996488..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-AutoFormat.PurifierLinkify
-TYPE: bool
-VERSION: 2.0.1
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-  Internal auto-formatter that converts configuration directives in
-  syntax <a>%Namespace.Directive</a> to links. <code>a</code> tags
-  with the <code>href</code> attribute must be allowed.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt
deleted file mode 100644
index 6367fe2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.Predicate.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-AutoFormat.RemoveEmpty.Predicate
-TYPE: hash
-VERSION: 4.7.0
-DEFAULT: array('colgroup' => array(), 'th' => array(), 'td' => array(), 'iframe' => array('src'))
---DESCRIPTION--
-<p>
-  Given that an element has no contents, it will be removed by default, unless
-  this predicate dictates otherwise.  The predicate can either be an associative
-  map from tag name to list of attributes that must be present for the element
-  to be considered preserved: thus, the default always preserves <code>colgroup</code>,
-  <code>th</code> and <code>td</code>, and also <code>iframe</code> if it
-  has a <code>src</code>.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt
deleted file mode 100644
index 35c393b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions
-TYPE: lookup
-VERSION: 4.0.0
-DEFAULT: array('td' => true, 'th' => true)
---DESCRIPTION--
-<p>
-  When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp
-  are enabled, this directive defines what HTML elements should not be
-  removede if they have only a non-breaking space in them.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt
deleted file mode 100644
index 9228dee..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-AutoFormat.RemoveEmpty.RemoveNbsp
-TYPE: bool
-VERSION: 4.0.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  When enabled, HTML Purifier will treat any elements that contain only
-  non-breaking spaces as well as regular whitespace as empty, and remove
-  them when %AutoFormat.RemoveEmpty is enabled.
-</p>
-<p>
-  See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements
-  that don't have this behavior applied to them.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt
deleted file mode 100644
index 34657ba..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-AutoFormat.RemoveEmpty
-TYPE: bool
-VERSION: 3.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  When enabled, HTML Purifier will attempt to remove empty elements that
-  contribute no semantic information to the document. The following types
-  of nodes will be removed:
-</p>
-<ul><li>
-    Tags with no attributes and no content, and that are not empty
-    elements (remove <code>&lt;a&gt;&lt;/a&gt;</code> but not
-    <code>&lt;br /&gt;</code>), and
-  </li>
-  <li>
-    Tags with no content, except for:<ul>
-      <li>The <code>colgroup</code> element, or</li>
-      <li>
-        Elements with the <code>id</code> or <code>name</code> attribute,
-        when those attributes are permitted on those elements.
-      </li>
-    </ul></li>
-</ul>
-<p>
-  Please be very careful when using this functionality; while it may not
-  seem that empty elements contain useful information, they can alter the
-  layout of a document given appropriate styling. This directive is most
-  useful when you are processing machine-generated HTML, please avoid using
-  it on regular user HTML.
-</p>
-<p>
-  Elements that contain only whitespace will be treated as empty. Non-breaking
-  spaces, however, do not count as whitespace. See
-  %AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior.
-</p>
-<p>
-  This algorithm is not perfect; you may still notice some empty tags,
-  particularly if a node had elements, but those elements were later removed
-  because they were not permitted in that context, or tags that, after
-  being auto-closed by another tag, where empty. This is for safety reasons
-  to prevent clever code from breaking validation. The general rule of thumb:
-  if a tag looked empty on the way in, it will get removed; if HTML Purifier
-  made it empty, it will stay.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt
deleted file mode 100644
index dde990a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-AutoFormat.RemoveSpansWithoutAttributes
-TYPE: bool
-VERSION: 4.0.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-  This directive causes <code>span</code> tags without any attributes
-  to be removed. It will also remove spans that had all attributes
-  removed during processing.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
deleted file mode 100644
index 4d054b1..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-CSS.AllowDuplicates
-TYPE: bool
-DEFAULT: false
-VERSION: 4.8.0
---DESCRIPTION--
-<p>
-  By default, HTML Purifier removes duplicate CSS properties,
-  like <code>color:red; color:blue</code>.  If this is set to
-  true, duplicate properties are allowed.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt
deleted file mode 100644
index b324608..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CSS.AllowImportant
-TYPE: bool
-DEFAULT: false
-VERSION: 3.1.0
---DESCRIPTION--
-This parameter determines whether or not !important cascade modifiers should
-be allowed in user CSS. If false, !important will stripped.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt
deleted file mode 100644
index 748be0e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-CSS.AllowTricky
-TYPE: bool
-DEFAULT: false
-VERSION: 3.1.0
---DESCRIPTION--
-This parameter determines whether or not to allow "tricky" CSS properties and
-values. Tricky CSS properties/values can drastically modify page layout or
-be used for deceptive practices but do not directly constitute a security risk.
-For example, <code>display:none;</code> is considered a tricky property that
-will only be allowed if this directive is set to true.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt
deleted file mode 100644
index 3fd4654..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedFonts.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-CSS.AllowedFonts
-TYPE: lookup/null
-VERSION: 4.3.0
-DEFAULT: NULL
---DESCRIPTION--
-<p>
-    Allows you to manually specify a set of allowed fonts.  If
-    <code>NULL</code>, all fonts are allowed.  This directive
-    affects generic names (serif, sans-serif, monospace, cursive,
-    fantasy) as well as specific font families.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt
deleted file mode 100644
index 460112e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-CSS.AllowedProperties
-TYPE: lookup/null
-VERSION: 3.1.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    If HTML Purifier's style attributes set is unsatisfactory for your needs,
-    you can overload it with your own list of tags to allow.  Note that this
-    method is subtractive: it does its job by taking away from HTML Purifier
-    usual feature set, so you cannot add an attribute that HTML Purifier never
-    supported in the first place.
-</p>
-<p>
-    <strong>Warning:</strong> If another directive conflicts with the
-    elements here, <em>that</em> directive will win and override.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt
deleted file mode 100644
index 5cb7dda..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-CSS.DefinitionRev
-TYPE: int
-VERSION: 2.0.0
-DEFAULT: 1
---DESCRIPTION--
-
-<p>
-    Revision identifier for your custom definition. See
-    %HTML.DefinitionRev for details.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt
deleted file mode 100644
index f1f5c5f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-CSS.ForbiddenProperties
-TYPE: lookup
-VERSION: 4.2.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-    This is the logical inverse of %CSS.AllowedProperties, and it will
-    override that directive or any other directive.  If possible,
-    %CSS.AllowedProperties is recommended over this directive,
-    because it can sometimes be difficult to tell whether or not you've
-    forbidden all of the CSS properties you truly would like to disallow.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt
deleted file mode 100644
index 7a32914..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-CSS.MaxImgLength
-TYPE: string/null
-DEFAULT: '1200px'
-VERSION: 3.1.1
---DESCRIPTION--
-<p>
- This parameter sets the maximum allowed length on <code>img</code> tags,
- effectively the <code>width</code> and <code>height</code> properties.
- Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is
- in place to prevent imagecrash attacks, disable with null at your own risk.
- This directive is similar to %HTML.MaxImgLength, and both should be
- concurrently edited, although there are
- subtle differences in the input format (the CSS max is a number with
- a unit).
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt
deleted file mode 100644
index 148eedb..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-CSS.Proprietary
-TYPE: bool
-VERSION: 3.0.0
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-    Whether or not to allow safe, proprietary CSS values.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt
deleted file mode 100644
index e733a61..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/CSS.Trusted.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CSS.Trusted
-TYPE: bool
-VERSION: 4.2.1
-DEFAULT: false
---DESCRIPTION--
-Indicates whether or not the user's CSS input is trusted or not. If the
-input is trusted, a more expansive set of allowed properties.  See
-also %HTML.Trusted.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt
deleted file mode 100644
index c486724..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Cache.DefinitionImpl
-TYPE: string/null
-VERSION: 2.0.0
-DEFAULT: 'Serializer'
---DESCRIPTION--
-
-This directive defines which method to use when caching definitions,
-the complex data-type that makes HTML Purifier tick. Set to null
-to disable caching (not recommended, as you will see a definite
-performance degradation).
-
---ALIASES--
-Core.DefinitionCache
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt
deleted file mode 100644
index 5403650..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Cache.SerializerPath
-TYPE: string/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Absolute path with no trailing slash to store serialized definitions in.
-    Default is within the
-    HTML Purifier library inside DefinitionCache/Serializer. This
-    path must be writable by the webserver.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
deleted file mode 100644
index 2e0cc81..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Cache.SerializerPermissions
-TYPE: int/null
-VERSION: 4.3.0
-DEFAULT: 0755
---DESCRIPTION--
-
-<p>
-    Directory permissions of the files and directories created inside
-    the DefinitionCache/Serializer or other custom serializer path.
-</p>
-<p>
-    In HTML Purifier 4.8.0, this also supports <code>NULL</code>,
-    which means that no chmod'ing or directory creation shall
-    occur.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt
deleted file mode 100644
index 568cbf3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Core.AggressivelyFixLt
-TYPE: bool
-VERSION: 2.1.0
-DEFAULT: true
---DESCRIPTION--
-<p>
-    This directive enables aggressive pre-filter fixes HTML Purifier can
-    perform in order to ensure that open angled-brackets do not get killed
-    during parsing stage. Enabling this will result in two preg_replace_callback
-    calls and at least two preg_replace calls for every HTML document parsed;
-    if your users make very well-formed HTML, you can set this directive false.
-    This has no effect when DirectLex is used.
-</p>
-<p>
-    <strong>Notice:</strong> This directive's default turned from false to true
-    in HTML Purifier 3.2.0.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt
deleted file mode 100644
index b2b6ab1..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyRemoveScript.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Core.AggressivelyRemoveScript
-TYPE: bool
-VERSION: 4.9.0
-DEFAULT: true
---DESCRIPTION--
-<p>
-    This directive enables aggressive pre-filter removal of
-    script tags.  This is not necessary for security,
-    but it can help work around a bug in libxml where embedded
-    HTML elements inside script sections cause the parser to
-    choke.  To revert to pre-4.9.0 behavior, set this to false.
-    This directive has no effect if %Core.Trusted is true,
-    %Core.RemoveScriptContents is false, or %Core.HiddenElements
-    does not contain script.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt
deleted file mode 100644
index 2c910cc..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowHostnameUnderscore.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Core.AllowHostnameUnderscore
-TYPE: bool
-VERSION: 4.6.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    By RFC 1123, underscores are not permitted in host names.
-    (This is in contrast to the specification for DNS, RFC
-    2181, which allows underscores.)
-    However, most browsers do the right thing when faced with
-    an underscore in the host name, and so some poorly written
-    websites are written with the expectation this should work.
-    Setting this parameter to true relaxes our allowed character
-    check so that underscores are permitted.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowParseManyTags.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowParseManyTags.txt
deleted file mode 100644
index 06278f8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.AllowParseManyTags.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.AllowParseManyTags
-TYPE: bool
-DEFAULT: false
-VERSION: 4.10.1
---DESCRIPTION--
-<p>
-    This directive allows parsing of many nested tags.
-    If you set true, relaxes any hardcoded limit from the parser.
-    However, in that case it may cause a Dos attack.
-    Be careful when enabling it.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt
deleted file mode 100644
index d731791..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.CollectErrors
-TYPE: bool
-VERSION: 2.0.0
-DEFAULT: false
---DESCRIPTION--
-
-Whether or not to collect errors found while filtering the document. This
-is a useful way to give feedback to your users. <strong>Warning:</strong>
-Currently this feature is very patchy and experimental, with lots of
-possible error messages not yet implemented. It will not cause any
-problems, but it may not help your users either.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt
deleted file mode 100644
index a75844c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt
+++ /dev/null
@@ -1,160 +0,0 @@
-Core.ColorKeywords
-TYPE: hash
-VERSION: 2.0.0
---DEFAULT--
-array (
-  'aliceblue' => '#F0F8FF',
-  'antiquewhite' => '#FAEBD7',
-  'aqua' => '#00FFFF',
-  'aquamarine' => '#7FFFD4',
-  'azure' => '#F0FFFF',
-  'beige' => '#F5F5DC',
-  'bisque' => '#FFE4C4',
-  'black' => '#000000',
-  'blanchedalmond' => '#FFEBCD',
-  'blue' => '#0000FF',
-  'blueviolet' => '#8A2BE2',
-  'brown' => '#A52A2A',
-  'burlywood' => '#DEB887',
-  'cadetblue' => '#5F9EA0',
-  'chartreuse' => '#7FFF00',
-  'chocolate' => '#D2691E',
-  'coral' => '#FF7F50',
-  'cornflowerblue' => '#6495ED',
-  'cornsilk' => '#FFF8DC',
-  'crimson' => '#DC143C',
-  'cyan' => '#00FFFF',
-  'darkblue' => '#00008B',
-  'darkcyan' => '#008B8B',
-  'darkgoldenrod' => '#B8860B',
-  'darkgray' => '#A9A9A9',
-  'darkgrey' => '#A9A9A9',
-  'darkgreen' => '#006400',
-  'darkkhaki' => '#BDB76B',
-  'darkmagenta' => '#8B008B',
-  'darkolivegreen' => '#556B2F',
-  'darkorange' => '#FF8C00',
-  'darkorchid' => '#9932CC',
-  'darkred' => '#8B0000',
-  'darksalmon' => '#E9967A',
-  'darkseagreen' => '#8FBC8F',
-  'darkslateblue' => '#483D8B',
-  'darkslategray' => '#2F4F4F',
-  'darkslategrey' => '#2F4F4F',
-  'darkturquoise' => '#00CED1',
-  'darkviolet' => '#9400D3',
-  'deeppink' => '#FF1493',
-  'deepskyblue' => '#00BFFF',
-  'dimgray' => '#696969',
-  'dimgrey' => '#696969',
-  'dodgerblue' => '#1E90FF',
-  'firebrick' => '#B22222',
-  'floralwhite' => '#FFFAF0',
-  'forestgreen' => '#228B22',
-  'fuchsia' => '#FF00FF',
-  'gainsboro' => '#DCDCDC',
-  'ghostwhite' => '#F8F8FF',
-  'gold' => '#FFD700',
-  'goldenrod' => '#DAA520',
-  'gray' => '#808080',
-  'grey' => '#808080',
-  'green' => '#008000',
-  'greenyellow' => '#ADFF2F',
-  'honeydew' => '#F0FFF0',
-  'hotpink' => '#FF69B4',
-  'indianred' => '#CD5C5C',
-  'indigo' => '#4B0082',
-  'ivory' => '#FFFFF0',
-  'khaki' => '#F0E68C',
-  'lavender' => '#E6E6FA',
-  'lavenderblush' => '#FFF0F5',
-  'lawngreen' => '#7CFC00',
-  'lemonchiffon' => '#FFFACD',
-  'lightblue' => '#ADD8E6',
-  'lightcoral' => '#F08080',
-  'lightcyan' => '#E0FFFF',
-  'lightgoldenrodyellow' => '#FAFAD2',
-  'lightgray' => '#D3D3D3',
-  'lightgrey' => '#D3D3D3',
-  'lightgreen' => '#90EE90',
-  'lightpink' => '#FFB6C1',
-  'lightsalmon' => '#FFA07A',
-  'lightseagreen' => '#20B2AA',
-  'lightskyblue' => '#87CEFA',
-  'lightslategray' => '#778899',
-  'lightslategrey' => '#778899',
-  'lightsteelblue' => '#B0C4DE',
-  'lightyellow' => '#FFFFE0',
-  'lime' => '#00FF00',
-  'limegreen' => '#32CD32',
-  'linen' => '#FAF0E6',
-  'magenta' => '#FF00FF',
-  'maroon' => '#800000',
-  'mediumaquamarine' => '#66CDAA',
-  'mediumblue' => '#0000CD',
-  'mediumorchid' => '#BA55D3',
-  'mediumpurple' => '#9370DB',
-  'mediumseagreen' => '#3CB371',
-  'mediumslateblue' => '#7B68EE',
-  'mediumspringgreen' => '#00FA9A',
-  'mediumturquoise' => '#48D1CC',
-  'mediumvioletred' => '#C71585',
-  'midnightblue' => '#191970',
-  'mintcream' => '#F5FFFA',
-  'mistyrose' => '#FFE4E1',
-  'moccasin' => '#FFE4B5',
-  'navajowhite' => '#FFDEAD',
-  'navy' => '#000080',
-  'oldlace' => '#FDF5E6',
-  'olive' => '#808000',
-  'olivedrab' => '#6B8E23',
-  'orange' => '#FFA500',
-  'orangered' => '#FF4500',
-  'orchid' => '#DA70D6',
-  'palegoldenrod' => '#EEE8AA',
-  'palegreen' => '#98FB98',
-  'paleturquoise' => '#AFEEEE',
-  'palevioletred' => '#DB7093',
-  'papayawhip' => '#FFEFD5',
-  'peachpuff' => '#FFDAB9',
-  'peru' => '#CD853F',
-  'pink' => '#FFC0CB',
-  'plum' => '#DDA0DD',
-  'powderblue' => '#B0E0E6',
-  'purple' => '#800080',
-  'rebeccapurple' => '#663399',
-  'red' => '#FF0000',
-  'rosybrown' => '#BC8F8F',
-  'royalblue' => '#4169E1',
-  'saddlebrown' => '#8B4513',
-  'salmon' => '#FA8072',
-  'sandybrown' => '#F4A460',
-  'seagreen' => '#2E8B57',
-  'seashell' => '#FFF5EE',
-  'sienna' => '#A0522D',
-  'silver' => '#C0C0C0',
-  'skyblue' => '#87CEEB',
-  'slateblue' => '#6A5ACD',
-  'slategray' => '#708090',
-  'slategrey' => '#708090',
-  'snow' => '#FFFAFA',
-  'springgreen' => '#00FF7F',
-  'steelblue' => '#4682B4',
-  'tan' => '#D2B48C',
-  'teal' => '#008080',
-  'thistle' => '#D8BFD8',
-  'tomato' => '#FF6347',
-  'turquoise' => '#40E0D0',
-  'violet' => '#EE82EE',
-  'wheat' => '#F5DEB3',
-  'white' => '#FFFFFF',
-  'whitesmoke' => '#F5F5F5',
-  'yellow' => '#FFFF00',
-  'yellowgreen' => '#9ACD32'
-)
---DESCRIPTION--
-
-Lookup array of color names to six digit hexadecimal number corresponding
-to color, with preceding hash mark. Used when parsing colors.  The lookup
-is done in a case-insensitive manner.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt
deleted file mode 100644
index 64b114f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Core.ConvertDocumentToFragment
-TYPE: bool
-DEFAULT: true
---DESCRIPTION--
-
-This parameter determines whether or not the filter should convert
-input that is a full document with html and body tags to a fragment
-of just the contents of a body tag. This parameter is simply something
-HTML Purifier can do during an edge-case: for most inputs, this
-processing is not necessary.
-
---ALIASES--
-Core.AcceptFullDocuments
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt
deleted file mode 100644
index 36f16e0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Core.DirectLexLineNumberSyncInterval
-TYPE: int
-VERSION: 2.0.0
-DEFAULT: 0
---DESCRIPTION--
-
-<p>
-  Specifies the number of tokens the DirectLex line number tracking
-  implementations should process before attempting to resyncronize the
-  current line count by manually counting all previous new-lines. When
-  at 0, this functionality is disabled. Lower values will decrease
-  performance, and this is only strictly necessary if the counting
-  algorithm is buggy (in which case you should report it as a bug).
-  This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is
-  not being used.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt
deleted file mode 100644
index 1cd4c2c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.DisableExcludes.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Core.DisableExcludes
-TYPE: bool
-DEFAULT: false
-VERSION: 4.5.0
---DESCRIPTION--
-<p>
-  This directive disables SGML-style exclusions, e.g. the exclusion of
-  <code>&lt;object&gt;</code> in any descendant of a
-  <code>&lt;pre&gt;</code> tag.  Disabling excludes will allow some
-  invalid documents to pass through HTML Purifier, but HTML Purifier
-  will also be less likely to accidentally remove large documents during
-  processing.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt
deleted file mode 100644
index ce243c3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EnableIDNA.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Core.EnableIDNA
-TYPE: bool
-DEFAULT: false
-VERSION: 4.4.0
---DESCRIPTION--
-Allows international domain names in URLs.  This configuration option
-requires the PEAR Net_IDNA2 module to be installed.  It operates by
-punycoding any internationalized host names for maximum portability.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt
deleted file mode 100644
index 8bfb47c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Core.Encoding
-TYPE: istring
-DEFAULT: 'utf-8'
---DESCRIPTION--
-If for some reason you are unable to convert all webpages to UTF-8, you can
-use this directive as a stop-gap compatibility change to let HTML Purifier
-deal with non UTF-8 input.  This technique has notable deficiencies:
-absolutely no characters outside of the selected character encoding will be
-preserved, not even the ones that have been ampersand escaped (this is due
-to a UTF-8 specific <em>feature</em> that automatically resolves all
-entities), making it pretty useless for anything except the most I18N-blind
-applications, although %Core.EscapeNonASCIICharacters offers fixes this
-trouble with another tradeoff. This directive only accepts ISO-8859-1 if
-iconv is not enabled.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt
deleted file mode 100644
index a3881be..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.EscapeInvalidChildren
-TYPE: bool
-DEFAULT: false
---DESCRIPTION--
-<p><strong>Warning:</strong> this configuration option is no longer does anything as of 4.6.0.</p>
-
-<p>When true, a child is found that is not allowed in the context of the
-parent element will be transformed into text as if it were ASCII. When
-false, that element and all internal tags will be dropped, though text will
-be preserved.  There is no option for dropping the element but preserving
-child nodes.</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt
deleted file mode 100644
index a7a5b24..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Core.EscapeInvalidTags
-TYPE: bool
-DEFAULT: false
---DESCRIPTION--
-When true, invalid tags will be written back to the document as plain text.
-Otherwise, they are silently dropped.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt
deleted file mode 100644
index abb4999..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Core.EscapeNonASCIICharacters
-TYPE: bool
-VERSION: 1.4.0
-DEFAULT: false
---DESCRIPTION--
-This directive overcomes a deficiency in %Core.Encoding by blindly
-converting all non-ASCII characters into decimal numeric entities before
-converting it to its native encoding. This means that even characters that
-can be expressed in the non-UTF-8 encoding will be entity-ized, which can
-be a real downer for encodings like Big5. It also assumes that the ASCII
-repetoire is available, although this is the case for almost all encodings.
-Anyway, use UTF-8!
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt
deleted file mode 100644
index 915391e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Core.HiddenElements
-TYPE: lookup
---DEFAULT--
-array (
-  'script' => true,
-  'style' => true,
-)
---DESCRIPTION--
-
-<p>
-  This directive is a lookup array of elements which should have their
-  contents removed when they are not allowed by the HTML definition.
-  For example, the contents of a <code>script</code> tag are not
-  normally shown in a document, so if script tags are to be removed,
-  their contents should be removed to. This is opposed to a <code>b</code>
-  tag, which defines some presentational changes but does not hide its
-  contents.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt
deleted file mode 100644
index 233fca1..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.Language.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Core.Language
-TYPE: string
-VERSION: 2.0.0
-DEFAULT: 'en'
---DESCRIPTION--
-
-ISO 639 language code for localizable things in HTML Purifier to use,
-which is mainly error reporting. There is currently only an English (en)
-translation, so this directive is currently useless.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt
deleted file mode 100644
index 392b436..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LegacyEntityDecoder.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Core.LegacyEntityDecoder
-TYPE: bool
-VERSION: 4.9.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Prior to HTML Purifier 4.9.0, entities were decoded by performing
-    a global search replace for all entities whose decoded versions
-    did not have special meanings under HTML, and replaced them with
-    their decoded versions.  We would match all entities, even if they did
-    not have a trailing semicolon, but only if there weren't any trailing
-    alphanumeric characters.
-</p>
-<table>
-<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
-<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
-<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
-<tr><td>&amp;yena</td><td>&amp;yena</td><td>&amp;yena</td></tr>
-<tr><td>&amp;yen=</td><td>&yen;=</td><td>&yen;=</td></tr>
-</table>
-<p>
-    In HTML Purifier 4.9.0, we changed the behavior of entity parsing
-    to match entities that had missing trailing semicolons in less
-    cases, to more closely match HTML5 parsing behavior:
-</p>
-<table>
-<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
-<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
-<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
-<tr><td>&amp;yena</td><td>&yen;a</td><td>&amp;yena</td></tr>
-<tr><td>&amp;yen=</td><td>&yen;=</td><td>&amp;yen=</td></tr>
-</table>
-<p>
-    This flag reverts back to pre-HTML Purifier 4.9.0 behavior.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt
deleted file mode 100644
index 8983e2c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-Core.LexerImpl
-TYPE: mixed/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-  This parameter determines what lexer implementation can be used. The
-  valid values are:
-</p>
-<dl>
-  <dt><em>null</em></dt>
-  <dd>
-    Recommended, the lexer implementation will be auto-detected based on
-    your PHP-version and configuration.
-  </dd>
-  <dt><em>string</em> lexer identifier</dt>
-  <dd>
-    This is a slim way of manually overridding the implementation.
-    Currently recognized values are: DOMLex (the default PHP5
-implementation)
-    and DirectLex (the default PHP4 implementation). Only use this if
-    you know what you are doing: usually, the auto-detection will
-    manage things for cases you aren't even aware of.
-  </dd>
-  <dt><em>object</em> lexer instance</dt>
-  <dd>
-    Super-advanced: you can specify your own, custom, implementation that
-    implements the interface defined by <code>HTMLPurifier_Lexer</code>.
-    I may remove this option simply because I don't expect anyone
-    to use it.
-  </dd>
-</dl>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt
deleted file mode 100644
index eb841a7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Core.MaintainLineNumbers
-TYPE: bool/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-  If true, HTML Purifier will add line number information to all tokens.
-  This is useful when error reporting is turned on, but can result in
-  significant performance degradation and should not be used when
-  unnecessary. This directive must be used with the DirectLex lexer,
-  as the DOMLex lexer does not (yet) support this functionality.
-  If the value is null, an appropriate value will be selected based
-  on other configuration.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt
deleted file mode 100644
index d77f536..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Core.NormalizeNewlines
-TYPE: bool
-VERSION: 4.2.0
-DEFAULT: true
---DESCRIPTION--
-<p>
-    Whether or not to normalize newlines to the operating
-    system default.  When <code>false</code>, HTML Purifier
-    will attempt to preserve mixed newline files.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt
deleted file mode 100644
index 4070c2a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.RemoveInvalidImg
-TYPE: bool
-DEFAULT: true
-VERSION: 1.3.0
---DESCRIPTION--
-
-<p>
-  This directive enables pre-emptive URI checking in <code>img</code>
-  tags, as the attribute validation strategy is not authorized to
-  remove elements from the document. Revert to pre-1.3.0 behavior by setting to false.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt
deleted file mode 100644
index 3397d9f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Core.RemoveProcessingInstructions
-TYPE: bool
-VERSION: 4.2.0
-DEFAULT: false
---DESCRIPTION--
-Instead of escaping processing instructions in the form <code>&lt;? ...
-?&gt;</code>, remove it out-right.  This may be useful if the HTML
-you are validating contains XML processing instruction gunk, however,
-it can also be user-unfriendly for people attempting to post PHP
-snippets.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt
deleted file mode 100644
index a4cd966..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Core.RemoveScriptContents
-TYPE: bool/null
-DEFAULT: NULL
-VERSION: 2.0.0
-DEPRECATED-VERSION: 2.1.0
-DEPRECATED-USE: Core.HiddenElements
---DESCRIPTION--
-<p>
-  This directive enables HTML Purifier to remove not only script tags
-  but all of their contents.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt
deleted file mode 100644
index 3db50ef..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Filter.Custom
-TYPE: list
-VERSION: 3.1.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-  This directive can be used to add custom filters; it is nearly the
-  equivalent of the now deprecated <code>HTMLPurifier-&gt;addFilter()</code>
-  method. Specify an array of concrete implementations.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt
deleted file mode 100644
index 16829bc..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Filter.ExtractStyleBlocks.Escaping
-TYPE: bool
-VERSION: 3.0.0
-DEFAULT: true
-ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping
---DESCRIPTION--
-
-<p>
-  Whether or not to escape the dangerous characters &lt;, &gt; and &amp;
-  as \3C, \3E and \26, respectively. This is can be safely set to false
-  if the contents of StyleBlocks will be placed in an external stylesheet,
-  where there is no risk of it being interpreted as HTML.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt
deleted file mode 100644
index 7f95f54..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Filter.ExtractStyleBlocks.Scope
-TYPE: string/null
-VERSION: 3.0.0
-DEFAULT: NULL
-ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope
---DESCRIPTION--
-
-<p>
-  If you would like users to be able to define external stylesheets, but
-  only allow them to specify CSS declarations for a specific node and
-  prevent them from fiddling with other elements, use this directive.
-  It accepts any valid CSS selector, and will prepend this to any
-  CSS declaration extracted from the document. For example, if this
-  directive is set to <code>#user-content</code> and a user uses the
-  selector <code>a:hover</code>, the final selector will be
-  <code>#user-content a:hover</code>.
-</p>
-<p>
-  The comma shorthand may be used; consider the above example, with
-  <code>#user-content, #user-content2</code>, the final selector will
-  be <code>#user-content a:hover, #user-content2 a:hover</code>.
-</p>
-<p>
-  <strong>Warning:</strong> It is possible for users to bypass this measure
-  using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
-  Purifier, and I am working to get it fixed. Until then, HTML Purifier
-  performs a basic check to prevent this.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt
deleted file mode 100644
index 6c231b2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Filter.ExtractStyleBlocks.TidyImpl
-TYPE: mixed/null
-VERSION: 3.1.0
-DEFAULT: NULL
-ALIASES: FilterParam.ExtractStyleBlocksTidyImpl
---DESCRIPTION--
-<p>
-  If left NULL, HTML Purifier will attempt to instantiate a <code>csstidy</code>
-  class to use for internal cleaning. This will usually be good enough.
-</p>
-<p>
-  However, for trusted user input, you can set this to <code>false</code> to
-  disable cleaning. In addition, you can supply your own concrete implementation
-  of Tidy's interface to use, although I don't know why you'd want to do that.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt
deleted file mode 100644
index 078d087..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-Filter.ExtractStyleBlocks
-TYPE: bool
-VERSION: 3.1.0
-DEFAULT: false
-EXTERNAL: CSSTidy
---DESCRIPTION--
-<p>
-  This directive turns on the style block extraction filter, which removes
-  <code>style</code> blocks from input HTML, cleans them up with CSSTidy,
-  and places them in the <code>StyleBlocks</code> context variable, for further
-  use by you, usually to be placed in an external stylesheet, or a
-  <code>style</code> block in the <code>head</code> of your document.
-</p>
-<p>
-  Sample usage:
-</p>
-<pre><![CDATA[
-<?php
-    header('Content-type: text/html; charset=utf-8');
-    echo '<?xml version="1.0" encoding="UTF-8"?>';
-?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head>
-  <title>Filter.ExtractStyleBlocks</title>
-<?php
-    require_once '/path/to/library/HTMLPurifier.auto.php';
-    require_once '/path/to/csstidy.class.php';
-
-    $dirty = '<style>body {color:#F00;}</style> Some text';
-
-    $config = HTMLPurifier_Config::createDefault();
-    $config->set('Filter', 'ExtractStyleBlocks', true);
-    $purifier = new HTMLPurifier($config);
-
-    $html = $purifier->purify($dirty);
-
-    // This implementation writes the stylesheets to the styles/ directory.
-    // You can also echo the styles inside the document, but it's a bit
-    // more difficult to make sure they get interpreted properly by
-    // browsers; try the usual CSS armoring techniques.
-    $styles = $purifier->context->get('StyleBlocks');
-    $dir = 'styles/';
-    if (!is_dir($dir)) mkdir($dir);
-    $hash = sha1($_GET['html']);
-    foreach ($styles as $i => $style) {
-        file_put_contents($name = $dir . $hash . "_$i");
-        echo '<link rel="stylesheet" type="text/css" href="'.$name.'" />';
-    }
-?>
-</head>
-<body>
-  <div>
-    <?php echo $html; ?>
-  </div>
-</b]]><![CDATA[ody>
-</html>
-]]></pre>
-<p>
-  <strong>Warning:</strong> It is possible for a user to mount an
-  imagecrash attack using this CSS. Counter-measures are difficult;
-  it is not simply enough to limit the range of CSS lengths (using
-  relative lengths with many nesting levels allows for large values
-  to be attained without actually specifying them in the stylesheet),
-  and the flexible nature of selectors makes it difficult to selectively
-  disable lengths on image tags (HTML Purifier, however, does disable
-  CSS width and height in inline styling). There are probably two effective
-  counter measures: an explicit width and height set to auto in all
-  images in your document (unlikely) or the disabling of width and
-  height (somewhat reasonable). Whether or not these measures should be
-  used is left to the reader.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt
deleted file mode 100644
index 321eaa2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Filter.YouTube
-TYPE: bool
-VERSION: 3.1.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  <strong>Warning:</strong> Deprecated in favor of %HTML.SafeObject and
-  %Output.FlashCompat (turn both on to allow YouTube videos and other
-  Flash content).
-</p>
-<p>
-  This directive enables YouTube video embedding in HTML Purifier. Check
-  <a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document
-  on embedding videos</a> for more information on what this filter does.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt
deleted file mode 100644
index 0b2c106..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-HTML.Allowed
-TYPE: itext/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    This is a preferred convenience directive that combines
-    %HTML.AllowedElements and %HTML.AllowedAttributes.
-    Specify elements and attributes that are allowed using:
-    <code>element1[attr1|attr2],element2...</code>.  For example,
-    if you would like to only allow paragraphs and links, specify
-    <code>a[href],p</code>.  You can specify attributes that apply
-    to all elements using an asterisk, e.g. <code>*[lang]</code>.
-    You can also use newlines instead of commas to separate elements.
-</p>
-<p>
-    <strong>Warning</strong>:
-    All of the constraints on the component directives are still enforced.
-    The syntax is a <em>subset</em> of TinyMCE's <code>valid_elements</code>
-    whitelist: directly copy-pasting it here will probably result in
-    broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes
-    are set, this directive has no effect.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt
deleted file mode 100644
index fcf093f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-HTML.AllowedAttributes
-TYPE: lookup/null
-VERSION: 1.3.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    If HTML Purifier's attribute set is unsatisfactory, overload it!
-    The syntax is "tag.attr" or "*.attr" for the global attributes
-    (style, id, class, dir, lang, xml:lang).
-</p>
-<p>
-    <strong>Warning:</strong> If another directive conflicts with the
-    elements here, <em>that</em> directive will win and override. For
-    example, %HTML.EnableAttrID will take precedence over *.id in this
-    directive.  You must set that directive to true before you can use
-    IDs at all.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt
deleted file mode 100644
index 140e214..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedComments.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-HTML.AllowedComments
-TYPE: lookup
-VERSION: 4.4.0
-DEFAULT: array()
---DESCRIPTION--
-A whitelist which indicates what explicit comment bodies should be
-allowed, modulo leading and trailing whitespace.  See also %HTML.AllowedCommentsRegexp
-(these directives are union'ed together, so a comment is considered
-valid if any directive deems it valid.)
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt
deleted file mode 100644
index f22e977..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedCommentsRegexp.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-HTML.AllowedCommentsRegexp
-TYPE: string/null
-VERSION: 4.4.0
-DEFAULT: NULL
---DESCRIPTION--
-A regexp, which if it matches the body of a comment, indicates that
-it should be allowed. Trailing and leading spaces are removed prior
-to running this regular expression.
-<strong>Warning:</strong> Make sure you specify
-correct anchor metacharacters <code>^regex$</code>, otherwise you may accept
-comments that you did not mean to! In particular, the regex <code>/foo|bar/</code>
-is probably not sufficiently strict, since it also allows <code>foobar</code>.
-See also %HTML.AllowedComments (these directives are union'ed together,
-so a comment is considered valid if any directive deems it valid.)
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt
deleted file mode 100644
index 1d3fa79..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-HTML.AllowedElements
-TYPE: lookup/null
-VERSION: 1.3.0
-DEFAULT: NULL
---DESCRIPTION--
-<p>
-    If HTML Purifier's tag set is unsatisfactory for your needs, you can
-    overload it with your own list of tags to allow.  If you change
-    this, you probably also want to change %HTML.AllowedAttributes; see
-    also %HTML.Allowed which lets you set allowed elements and
-    attributes at the same time.
-</p>
-<p>
-    If you attempt to allow an element that HTML Purifier does not know
-    about, HTML Purifier will raise an error.  You will need to manually
-    tell HTML Purifier about this element by using the
-    <a href="http://htmlpurifier.org/docs/enduser-customize.html">advanced customization features.</a>
-</p>
-<p>
-    <strong>Warning:</strong> If another directive conflicts with the
-    elements here, <em>that</em> directive will win and override.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt
deleted file mode 100644
index 5a59a55..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-HTML.AllowedModules
-TYPE: lookup/null
-VERSION: 2.0.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    A doctype comes with a set of usual modules to use. Without having
-    to mucking about with the doctypes, you can quickly activate or
-    disable these modules by specifying which modules you wish to allow
-    with this directive. This is most useful for unit testing specific
-    modules, although end users may find it useful for their own ends.
-</p>
-<p>
-    If you specify a module that does not exist, the manager will silently
-    fail to use it, so be careful! User-defined modules are not affected
-    by this directive. Modules defined in %HTML.CoreModules are not
-    affected by this directive.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt
deleted file mode 100644
index 151fb7b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.Attr.Name.UseCDATA
-TYPE: bool
-DEFAULT: false
-VERSION: 4.0.0
---DESCRIPTION--
-The W3C specification DTD defines the name attribute to be CDATA, not ID, due
-to limitations of DTD.  In certain documents, this relaxed behavior is desired,
-whether it is to specify duplicate names, or to specify names that would be
-illegal IDs (for example, names that begin with a digit.) Set this configuration
-directive to true to use the relaxed parsing rules.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt
deleted file mode 100644
index 45ae469..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-HTML.BlockWrapper
-TYPE: string
-VERSION: 1.3.0
-DEFAULT: 'p'
---DESCRIPTION--
-
-<p>
-    String name of element to wrap inline elements that are inside a block
-    context.  This only occurs in the children of blockquote in strict mode.
-</p>
-<p>
-    Example: by default value,
-    <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> would become
-    <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>.
-    The <code>&lt;p&gt;</code> tags can be replaced with whatever you desire,
-    as long as it is a block level element.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt
deleted file mode 100644
index 5246188..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-HTML.CoreModules
-TYPE: lookup
-VERSION: 2.0.0
---DEFAULT--
-array (
-  'Structure' => true,
-  'Text' => true,
-  'Hypertext' => true,
-  'List' => true,
-  'NonXMLCommonAttributes' => true,
-  'XMLCommonAttributes' => true,
-  'CommonAttributes' => true,
-)
---DESCRIPTION--
-
-<p>
-    Certain modularized doctypes (XHTML, namely), have certain modules
-    that must be included for the doctype to be an conforming document
-    type: put those modules here. By default, XHTML's core modules
-    are used. You can set this to a blank array to disable core module
-    protection, but this is not recommended.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt
deleted file mode 100644
index 6ed70b5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-HTML.CustomDoctype
-TYPE: string/null
-VERSION: 2.0.1
-DEFAULT: NULL
---DESCRIPTION--
-
-A custom doctype for power-users who defined their own document
-type. This directive only applies when %HTML.Doctype is blank.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt
deleted file mode 100644
index 103db75..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-HTML.DefinitionID
-TYPE: string/null
-DEFAULT: NULL
-VERSION: 2.0.0
---DESCRIPTION--
-
-<p>
-    Unique identifier for a custom-built HTML definition. If you edit
-    the raw version of the HTMLDefinition, introducing changes that the
-    configuration object does not reflect, you must specify this variable.
-    If you change your custom edits, you should change this directive, or
-    clear your cache. Example:
-</p>
-<pre>
-$config = HTMLPurifier_Config::createDefault();
-$config->set('HTML', 'DefinitionID', '1');
-$def = $config->getHTMLDefinition();
-$def->addAttribute('a', 'tabindex', 'Number');
-</pre>
-<p>
-    In the above example, the configuration is still at the defaults, but
-    using the advanced API, an extra attribute has been added. The
-    configuration object normally has no way of knowing that this change
-    has taken place, so it needs an extra directive: %HTML.DefinitionID.
-    If someone else attempts to use the default configuration, these two
-    pieces of code will not clobber each other in the cache, since one has
-    an extra directive attached to it.
-</p>
-<p>
-    You <em>must</em> specify a value to this directive to use the
-    advanced API features.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt
deleted file mode 100644
index 229ae02..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-HTML.DefinitionRev
-TYPE: int
-VERSION: 2.0.0
-DEFAULT: 1
---DESCRIPTION--
-
-<p>
-    Revision identifier for your custom definition specified in
-    %HTML.DefinitionID.  This serves the same purpose: uniquely identifying
-    your custom definition, but this one does so in a chronological
-    context: revision 3 is more up-to-date then revision 2.  Thus, when
-    this gets incremented, the cache handling is smart enough to clean
-    up any older revisions of your definition as well as flush the
-    cache.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt
deleted file mode 100644
index 9dab497..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.Doctype
-TYPE: string/null
-DEFAULT: NULL
---DESCRIPTION--
-Doctype to use during filtering. Technically speaking this is not actually
-a doctype (as it does not identify a corresponding DTD), but we are using
-this name for sake of simplicity. When non-blank, this will override any
-older directives like %HTML.XHTML or %HTML.Strict.
---ALLOWED--
-'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1'
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt
deleted file mode 100644
index 7878dc0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.FlashAllowFullScreen
-TYPE: bool
-VERSION: 4.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to permit embedded Flash content from
-    %HTML.SafeObject to expand to the full screen.  Corresponds to
-    the <code>allowFullScreen</code> parameter.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt
deleted file mode 100644
index 57358f9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-HTML.ForbiddenAttributes
-TYPE: lookup
-VERSION: 3.1.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-    While this directive is similar to %HTML.AllowedAttributes, for
-    forwards-compatibility with XML, this attribute has a different syntax. Instead of
-    <code>tag.attr</code>, use <code>tag@attr</code>. To disallow <code>href</code>
-    attributes in <code>a</code> tags, set this directive to
-    <code>a@href</code>. You can also disallow an attribute globally with
-    <code>attr</code> or <code>*@attr</code> (either syntax is fine; the latter
-    is provided for consistency with %HTML.AllowedAttributes).
-</p>
-<p>
-    <strong>Warning:</strong> This directive complements %HTML.ForbiddenElements,
-    accordingly, check
-    out that directive for a discussion of why you
-    should think twice before using this directive.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt
deleted file mode 100644
index 93a53e1..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-HTML.ForbiddenElements
-TYPE: lookup
-VERSION: 3.1.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-    This was, perhaps, the most requested feature ever in HTML
-    Purifier. Please don't abuse it! This is the logical inverse of
-    %HTML.AllowedElements, and it will override that directive, or any
-    other directive.
-</p>
-<p>
-    If possible, %HTML.Allowed is recommended over this directive, because it
-    can sometimes be difficult to tell whether or not you've forbidden all of
-    the behavior you would like to disallow. If you forbid <code>img</code>
-    with the expectation of preventing images on your site, you'll be in for
-    a nasty surprise when people start using the <code>background-image</code>
-    CSS property.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Forms.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Forms.txt
deleted file mode 100644
index 4a432d8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Forms.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.Forms
-TYPE: bool
-VERSION: 4.13.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to permit form elements in the user input, regardless of
-    %HTML.Trusted value. Please be very careful when using this functionality, as
-    enabling forms in untrusted documents may allow for phishing attacks.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt
deleted file mode 100644
index e424c38..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-HTML.MaxImgLength
-TYPE: int/null
-DEFAULT: 1200
-VERSION: 3.1.1
---DESCRIPTION--
-<p>
- This directive controls the maximum number of pixels in the width and
- height attributes in <code>img</code> tags. This is
- in place to prevent imagecrash attacks, disable with null at your own risk.
- This directive is similar to %CSS.MaxImgLength, and both should be
- concurrently edited, although there are
- subtle differences in the input format (the HTML max is an integer).
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt
deleted file mode 100644
index 700b309..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Nofollow.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-HTML.Nofollow
-TYPE: bool
-VERSION: 4.3.0
-DEFAULT: FALSE
---DESCRIPTION--
-If enabled, nofollow rel attributes are added to all outgoing links.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt
deleted file mode 100644
index 62e8e16..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-HTML.Parent
-TYPE: string
-VERSION: 1.3.0
-DEFAULT: 'div'
---DESCRIPTION--
-
-<p>
-    String name of element that HTML fragment passed to library will be
-    inserted in.  An interesting variation would be using span as the
-    parent element, meaning that only inline tags would be allowed.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt
deleted file mode 100644
index dfb7204..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-HTML.Proprietary
-TYPE: bool
-VERSION: 3.1.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to allow proprietary elements and attributes in your
-    documents, as per <code>HTMLPurifier_HTMLModule_Proprietary</code>.
-    <strong>Warning:</strong> This can cause your documents to stop
-    validating!
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt
deleted file mode 100644
index cdda09a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-HTML.SafeEmbed
-TYPE: bool
-VERSION: 3.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to permit embed tags in documents, with a number of extra
-    security features added to prevent script execution. This is similar to
-    what websites like MySpace do to embed tags. Embed is a proprietary
-    element and will cause your website to stop validating; you should
-    see if you can use %Output.FlashCompat with %HTML.SafeObject instead
-    first.</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt
deleted file mode 100644
index 5eb6ec2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeIframe.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-HTML.SafeIframe
-TYPE: bool
-VERSION: 4.4.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to permit iframe tags in untrusted documents.  This
-    directive must be accompanied by a whitelist of permitted iframes,
-    such as %URI.SafeIframeRegexp, otherwise it will fatally error.
-    This directive has no effect on strict doctypes, as iframes are not
-    valid.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt
deleted file mode 100644
index ceb342e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-HTML.SafeObject
-TYPE: bool
-VERSION: 3.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Whether or not to permit object tags in documents, with a number of extra
-    security features added to prevent script execution. This is similar to
-    what websites like MySpace do to object tags.  You should also enable
-    %Output.FlashCompat in order to generate Internet Explorer
-    compatibility code for your object tags.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt
deleted file mode 100644
index 5ebc7a1..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.SafeScripting.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-HTML.SafeScripting
-TYPE: lookup
-VERSION: 4.5.0
-DEFAULT: array()
---DESCRIPTION--
-<p>
-    Whether or not to permit script tags to external scripts in documents.
-    Inline scripting is not allowed, and the script must match an explicit whitelist.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt
deleted file mode 100644
index a8b1de5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-HTML.Strict
-TYPE: bool
-VERSION: 1.3.0
-DEFAULT: false
-DEPRECATED-VERSION: 1.7.0
-DEPRECATED-USE: HTML.Doctype
---DESCRIPTION--
-Determines whether or not to use Transitional (loose) or Strict rulesets.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt
deleted file mode 100644
index 587a167..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetBlank.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-HTML.TargetBlank
-TYPE: bool
-VERSION: 4.4.0
-DEFAULT: FALSE
---DESCRIPTION--
-If enabled, <code>target=blank</code> attributes are added to all outgoing links.
-(This includes links from an HTTPS version of a page to an HTTP version.)
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
deleted file mode 100644
index dd514c0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
+++ /dev/null
@@ -1,10 +0,0 @@
---# vim: et sw=4 sts=4
-HTML.TargetNoopener
-TYPE: bool
-VERSION: 4.8.0
-DEFAULT: TRUE
---DESCRIPTION--
-If enabled, noopener rel attributes are added to links which have
-a target attribute associated with them.  This prevents malicious
-destinations from overwriting the original window.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
deleted file mode 100644
index cb5a0b0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-HTML.TargetNoreferrer
-TYPE: bool
-VERSION: 4.8.0
-DEFAULT: TRUE
---DESCRIPTION--
-If enabled, noreferrer rel attributes are added to links which have
-a target attribute associated with them.  This prevents malicious
-destinations from overwriting the original window.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt
deleted file mode 100644
index b4c271b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-HTML.TidyAdd
-TYPE: lookup
-VERSION: 2.0.0
-DEFAULT: array()
---DESCRIPTION--
-
-Fixes to add to the default set of Tidy fixes as per your level.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt
deleted file mode 100644
index 4186ccd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-HTML.TidyLevel
-TYPE: string
-VERSION: 2.0.0
-DEFAULT: 'medium'
---DESCRIPTION--
-
-<p>General level of cleanliness the Tidy module should enforce.
-There are four allowed values:</p>
-<dl>
-    <dt>none</dt>
-    <dd>No extra tidying should be done</dd>
-    <dt>light</dt>
-    <dd>Only fix elements that would be discarded otherwise due to
-    lack of support in doctype</dd>
-    <dt>medium</dt>
-    <dd>Enforce best practices</dd>
-    <dt>heavy</dt>
-    <dd>Transform all deprecated elements and attributes to standards
-    compliant equivalents</dd>
-</dl>
-
---ALLOWED--
-'none', 'light', 'medium', 'heavy'
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt
deleted file mode 100644
index 996762b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-HTML.TidyRemove
-TYPE: lookup
-VERSION: 2.0.0
-DEFAULT: array()
---DESCRIPTION--
-
-Fixes to remove from the default set of Tidy fixes as per your level.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt
deleted file mode 100644
index 1db9237..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-HTML.Trusted
-TYPE: bool
-VERSION: 2.0.0
-DEFAULT: false
---DESCRIPTION--
-Indicates whether or not the user input is trusted or not. If the input is
-trusted, a more expansive set of allowed tags and attributes will be used.
-See also %CSS.Trusted.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt
deleted file mode 100644
index 2a47e38..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-HTML.XHTML
-TYPE: bool
-DEFAULT: true
-VERSION: 1.1.0
-DEPRECATED-VERSION: 1.7.0
-DEPRECATED-USE: HTML.Doctype
---DESCRIPTION--
-Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor.
---ALIASES--
-Core.XHTML
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt
deleted file mode 100644
index 08921fd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Output.CommentScriptContents
-TYPE: bool
-VERSION: 2.0.0
-DEFAULT: true
---DESCRIPTION--
-Determines whether or not HTML Purifier should attempt to fix up the
-contents of script tags for legacy browsers with comments.
---ALIASES--
-Core.CommentScriptContents
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt
deleted file mode 100644
index d6f0d9f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FixInnerHTML.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Output.FixInnerHTML
-TYPE: bool
-VERSION: 4.3.0
-DEFAULT: true
---DESCRIPTION--
-<p>
-  If true, HTML Purifier will protect against Internet Explorer's
-  mishandling of the <code>innerHTML</code> attribute by appending
-  a space to any attribute that does not contain angled brackets, spaces
-  or quotes, but contains a backtick.  This slightly changes the
-  semantics of any given attribute, so if this is unacceptable and
-  you do not use <code>innerHTML</code> on any of your pages, you can
-  turn this directive off.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt
deleted file mode 100644
index 93398e8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Output.FlashCompat
-TYPE: bool
-VERSION: 4.1.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  If true, HTML Purifier will generate Internet Explorer compatibility
-  code for all object code.  This is highly recommended if you enable
-  %HTML.SafeObject.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt
deleted file mode 100644
index 79f8ad8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Output.Newline
-TYPE: string/null
-VERSION: 2.0.1
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Newline string to format final output with. If left null, HTML Purifier
-    will auto-detect the default newline type of the system and use that;
-    you can manually override it here. Remember, \r\n is Windows, \r
-    is Mac, and \n is Unix.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt
deleted file mode 100644
index 232b023..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Output.SortAttr
-TYPE: bool
-VERSION: 3.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-  If true, HTML Purifier will sort attributes by name before writing them back
-  to the document, converting a tag like: <code>&lt;el b="" a="" c="" /&gt;</code>
-  to <code>&lt;el a="" b="" c="" /&gt;</code>. This is a workaround for
-  a bug in FCKeditor which causes it to swap attributes order, adding noise
-  to text diffs. If you're not seeing this bug, chances are, you don't need
-  this directive.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt
deleted file mode 100644
index 06bab00..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Output.TidyFormat
-TYPE: bool
-VERSION: 1.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Determines whether or not to run Tidy on the final output for pretty
-    formatting reasons, such as indentation and wrap.
-</p>
-<p>
-    This can greatly improve readability for editors who are hand-editing
-    the HTML, but is by no means necessary as HTML Purifier has already
-    fixed all major errors the HTML may have had. Tidy is a non-default
-    extension, and this directive will silently fail if Tidy is not
-    available.
-</p>
-<p>
-    If you are looking to make the overall look of your page's source
-    better, I recommend running Tidy on the entire page rather than just
-    user-content (after all, the indentation relative to the containing
-    blocks will be incorrect).
-</p>
---ALIASES--
-Core.TidyFormat
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt
deleted file mode 100644
index 071bc02..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Test.ForceNoIconv
-TYPE: bool
-DEFAULT: false
---DESCRIPTION--
-When set to true, HTMLPurifier_Encoder will act as if iconv does not exist
-and use only pure PHP implementations.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
deleted file mode 100644
index eb97307..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-URI.AllowedSchemes
-TYPE: lookup
---DEFAULT--
-array (
-  'http' => true,
-  'https' => true,
-  'mailto' => true,
-  'ftp' => true,
-  'nntp' => true,
-  'news' => true,
-  'tel' => true,
-)
---DESCRIPTION--
-Whitelist that defines the schemes that a URI is allowed to have.  This
-prevents XSS attacks from using pseudo-schemes like javascript or mocha.
-There is also support for the <code>data</code> and <code>file</code>
-URI schemes, but they are not enabled by default.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt
deleted file mode 100644
index 876f068..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Base.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-URI.Base
-TYPE: string/null
-VERSION: 2.1.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    The base URI is the URI of the document this purified HTML will be
-    inserted into.  This information is important if HTML Purifier needs
-    to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute
-    is on.  You may use a non-absolute URI for this value, but behavior
-    may vary (%URI.MakeAbsolute deals nicely with both absolute and
-    relative paths, but forwards-compatibility is not guaranteed).
-    <strong>Warning:</strong> If set, the scheme on this URI
-    overrides the one specified by %URI.DefaultScheme.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
deleted file mode 100644
index 834bc08..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-URI.DefaultScheme
-TYPE: string/null
-DEFAULT: 'http'
---DESCRIPTION--
-
-<p>
-    Defines through what scheme the output will be served, in order to
-    select the proper object validator when no scheme information is present.
-</p>
-
-<p>
-    Starting with HTML Purifier 4.9.0, the default scheme can be null, in
-    which case we reject all URIs which do not have explicit schemes.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt
deleted file mode 100644
index f05312b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-URI.DefinitionID
-TYPE: string/null
-VERSION: 2.1.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Unique identifier for a custom-built URI definition. If you  want
-    to add custom URIFilters, you must specify this value.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt
deleted file mode 100644
index 80cfea9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-URI.DefinitionRev
-TYPE: int
-VERSION: 2.1.0
-DEFAULT: 1
---DESCRIPTION--
-
-<p>
-    Revision identifier for your custom definition. See
-    %HTML.DefinitionRev for details.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt
deleted file mode 100644
index 71ce025..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-URI.Disable
-TYPE: bool
-VERSION: 1.3.0
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-    Disables all URIs in all forms. Not sure why you'd want to do that
-    (after all, the Internet's founded on the notion of a hyperlink).
-</p>
-
---ALIASES--
-Attr.DisableURI
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt
deleted file mode 100644
index 13c122c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-URI.DisableExternal
-TYPE: bool
-VERSION: 1.2.0
-DEFAULT: false
---DESCRIPTION--
-Disables links to external websites.  This is a highly effective anti-spam
-and anti-pagerank-leech measure, but comes at a hefty price: nolinks or
-images outside of your domain will be allowed.  Non-linkified URIs will
-still be preserved.  If you want to be able to link to subdomains or use
-absolute URIs, specify %URI.Host for your website.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt
deleted file mode 100644
index abcc1ef..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-URI.DisableExternalResources
-TYPE: bool
-VERSION: 1.3.0
-DEFAULT: false
---DESCRIPTION--
-Disables the embedding of external resources, preventing users from
-embedding things like images from other hosts. This prevents access
-tracking (good for email viewers), bandwidth leeching, cross-site request
-forging, goatse.cx posting, and other nasties, but also results in a loss
-of end-user functionality (they can't directly post a pic they posted from
-Flickr anymore). Use it if you don't have a robust user-content moderation
-team.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt
deleted file mode 100644
index f891de4..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-URI.DisableResources
-TYPE: bool
-VERSION: 4.2.0
-DEFAULT: false
---DESCRIPTION--
-<p>
-    Disables embedding resources, essentially meaning no pictures. You can
-    still link to them though. See %URI.DisableExternalResources for why
-    this might be a good idea.
-</p>
-<p>
-    <em>Note:</em> While this directive has been available since 1.3.0,
-    it didn't actually start doing anything until 4.2.0.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt
deleted file mode 100644
index ee83b12..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Host.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-URI.Host
-TYPE: string/null
-VERSION: 1.2.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Defines the domain name of the server, so we can determine whether or
-    an absolute URI is from your website or not.  Not strictly necessary,
-    as users should be using relative URIs to reference resources on your
-    website.  It will, however, let you use absolute URIs to link to
-    subdomains of the domain you post here: i.e. example.com will allow
-    sub.example.com.  However, higher up domains will still be excluded:
-    if you set %URI.Host to sub.example.com, example.com will be blocked.
-    <strong>Note:</strong> This directive overrides %URI.Base because
-    a given page may be on a sub-domain, but you wish HTML Purifier to be
-    more relaxed and allow some of the parent domains too.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt
deleted file mode 100644
index 0b6df76..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-URI.HostBlacklist
-TYPE: list
-VERSION: 1.3.0
-DEFAULT: array()
---DESCRIPTION--
-List of strings that are forbidden in the host of any URI. Use it to kill
-domain names of spam, etc. Note that it will catch anything in the domain,
-so <tt>moo.com</tt> will catch <tt>moo.com.example.com</tt>.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt
deleted file mode 100644
index 4214900..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-URI.MakeAbsolute
-TYPE: bool
-VERSION: 2.1.0
-DEFAULT: false
---DESCRIPTION--
-
-<p>
-    Converts all URIs into absolute forms. This is useful when the HTML
-    being filtered assumes a specific base path, but will actually be
-    viewed in a different context (and setting an alternate base URI is
-    not possible). %URI.Base must be set for this directive to work.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
deleted file mode 100644
index 58c81dc..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
+++ /dev/null
@@ -1,83 +0,0 @@
-URI.Munge
-TYPE: string/null
-VERSION: 1.3.0
-DEFAULT: NULL
---DESCRIPTION--
-
-<p>
-    Munges all browsable (usually http, https and ftp)
-    absolute URIs into another URI, usually a URI redirection service.
-    This directive accepts a URI, formatted with a <code>%s</code> where
-    the url-encoded original URI should be inserted (sample:
-    <code>http://www.google.com/url?q=%s</code>).
-</p>
-<p>
-    Uses for this directive:
-</p>
-<ul>
-    <li>
-        Prevent PageRank leaks, while being fairly transparent
-        to users (you may also want to add some client side JavaScript to
-        override the text in the statusbar). <strong>Notice</strong>:
-        Many security experts believe that this form of protection does not deter spam-bots.
-    </li>
-    <li>
-        Redirect users to a splash page telling them they are leaving your
-        website. While this is poor usability practice, it is often mandated
-        in corporate environments.
-    </li>
-</ul>
-<p>
-    Prior to HTML Purifier 3.1.1, this directive also enabled the munging
-    of browsable external resources, which could break things if your redirection
-    script was a splash page or used <code>meta</code> tags. To revert to
-    previous behavior, please use %URI.MungeResources.
-</p>
-<p>
-    You may want to also use %URI.MungeSecretKey along with this directive
-    in order to enforce what URIs your redirector script allows. Open
-    redirector scripts can be a security risk and negatively affect the
-    reputation of your domain name.
-</p>
-<p>
-    Starting with HTML Purifier 3.1.1, there is also these substitutions:
-</p>
-<table>
-    <thead>
-        <tr>
-            <th>Key</th>
-            <th>Description</th>
-            <th>Example <code>&lt;a href=""&gt;</code></th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr>
-            <td>%r</td>
-            <td>1 - The URI embeds a resource<br />(blank) - The URI is merely a link</td>
-            <td></td>
-        </tr>
-        <tr>
-            <td>%n</td>
-            <td>The name of the tag this URI came from</td>
-            <td>a</td>
-        </tr>
-        <tr>
-            <td>%m</td>
-            <td>The name of the attribute this URI came from</td>
-            <td>href</td>
-        </tr>
-        <tr>
-            <td>%p</td>
-            <td>The name of the CSS property this URI came from, or blank if irrelevant</td>
-            <td></td>
-        </tr>
-    </tbody>
-</table>
-<p>
-    Admittedly, these letters are somewhat arbitrary; the only stipulation
-    was that they couldn't be a through f. r is for resource (I would have preferred
-    e, but you take what you can get), n is for name, m
-    was picked because it came after n (and I couldn't use a), p is for
-    property.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt
deleted file mode 100644
index 6fce0fd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-URI.MungeResources
-TYPE: bool
-VERSION: 3.1.1
-DEFAULT: false
---DESCRIPTION--
-<p>
-    If true, any URI munging directives like %URI.Munge
-    will also apply to embedded resources, such as <code>&lt;img src=""&gt;</code>.
-    Be careful enabling this directive if you have a redirector script
-    that does not use the <code>Location</code> HTTP header; all of your images
-    and other embedded resources will break.
-</p>
-<p>
-    <strong>Warning:</strong> It is strongly advised you use this in conjunction
-    %URI.MungeSecretKey to mitigate the security risk of an open redirector.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
deleted file mode 100644
index 1e17c1d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-URI.MungeSecretKey
-TYPE: string/null
-VERSION: 3.1.1
-DEFAULT: NULL
---DESCRIPTION--
-<p>
-    This directive enables secure checksum generation along with %URI.Munge.
-    It should be set to a secure key that is not shared with anyone else.
-    The checksum can be placed in the URI using %t. Use of this checksum
-    affords an additional level of protection by allowing a redirector
-    to check if a URI has passed through HTML Purifier with this line:
-</p>
-
-<pre>$checksum === hash_hmac("sha256", $url, $secret_key)</pre>
-
-<p>
-    If the output is TRUE, the redirector script should accept the URI.
-</p>
-
-<p>
-    Please note that it would still be possible for an attacker to procure
-    secure hashes en-mass by abusing your website's Preview feature or the
-    like, but this service affords an additional level of protection
-    that should be combined with website blacklisting.
-</p>
-
-<p>
-    Remember this has no effect if %URI.Munge is not on.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt
deleted file mode 100644
index 23331a4..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-URI.OverrideAllowedSchemes
-TYPE: bool
-DEFAULT: true
---DESCRIPTION--
-If this is set to true (which it is by default), you can override
-%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the
-registry.  If false, you will also have to update that directive in order
-to add more schemes.
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt
deleted file mode 100644
index 7908483..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/URI.SafeIframeRegexp.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-URI.SafeIframeRegexp
-TYPE: string/null
-VERSION: 4.4.0
-DEFAULT: NULL
---DESCRIPTION--
-<p>
-    A PCRE regular expression that will be matched against an iframe URI.  This is
-    a relatively inflexible scheme, but works well enough for the most common
-    use-case of iframes: embedded video.  This directive only has an effect if
-    %HTML.SafeIframe is enabled.  Here are some example values:
-</p>
-<ul>
-    <li><code>%^http://www.youtube.com/embed/%</code> - Allow YouTube videos</li>
-    <li><code>%^http://player.vimeo.com/video/%</code> - Allow Vimeo videos</li>
-    <li><code>%^http://(www.youtube.com/embed/|player.vimeo.com/video/)%</code> - Allow both</li>
-</ul>
-<p>
-    Note that this directive does not give you enough granularity to, say, disable
-    all <code>autoplay</code> videos.  Pipe up on the HTML Purifier forums if this
-    is a capability you want.
-</p>
---# vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini
deleted file mode 100644
index 5de4505..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ConfigSchema/schema/info.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-name = "HTML Purifier"
-
-; vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php
deleted file mode 100644
index 543e3f8..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ContentSets.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-
-/**
- * @todo Unit test
- */
-class HTMLPurifier_ContentSets
-{
-
-    /**
-     * List of content set strings (pipe separators) indexed by name.
-     * @type array
-     */
-    public $info = array();
-
-    /**
-     * List of content set lookups (element => true) indexed by name.
-     * @type array
-     * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
-     */
-    public $lookup = array();
-
-    /**
-     * Synchronized list of defined content sets (keys of info).
-     * @type array
-     */
-    protected $keys = array();
-    /**
-     * Synchronized list of defined content values (values of info).
-     * @type array
-     */
-    protected $values = array();
-
-    /**
-     * Merges in module's content sets, expands identifiers in the content
-     * sets and populates the keys, values and lookup member variables.
-     * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule
-     */
-    public function __construct($modules)
-    {
-        if (!is_array($modules)) {
-            $modules = array($modules);
-        }
-        // populate content_sets based on module hints
-        // sorry, no way of overloading
-        foreach ($modules as $module) {
-            foreach ($module->content_sets as $key => $value) {
-                $temp = $this->convertToLookup($value);
-                if (isset($this->lookup[$key])) {
-                    // add it into the existing content set
-                    $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
-                } else {
-                    $this->lookup[$key] = $temp;
-                }
-            }
-        }
-        $old_lookup = false;
-        while ($old_lookup !== $this->lookup) {
-            $old_lookup = $this->lookup;
-            foreach ($this->lookup as $i => $set) {
-                $add = array();
-                foreach ($set as $element => $x) {
-                    if (isset($this->lookup[$element])) {
-                        $add += $this->lookup[$element];
-                        unset($this->lookup[$i][$element]);
-                    }
-                }
-                $this->lookup[$i] += $add;
-            }
-        }
-
-        foreach ($this->lookup as $key => $lookup) {
-            $this->info[$key] = implode(' | ', array_keys($lookup));
-        }
-        $this->keys   = array_keys($this->info);
-        $this->values = array_values($this->info);
-    }
-
-    /**
-     * Accepts a definition; generates and assigns a ChildDef for it
-     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference
-     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
-     */
-    public function generateChildDef(&$def, $module)
-    {
-        if (!empty($def->child)) { // already done!
-            return;
-        }
-        $content_model = $def->content_model;
-        if (is_string($content_model)) {
-            // Assume that $this->keys is alphanumeric
-            $def->content_model = preg_replace_callback(
-                '/\b(' . implode('|', $this->keys) . ')\b/',
-                array($this, 'generateChildDefCallback'),
-                $content_model
-            );
-            //$def->content_model = str_replace(
-            //    $this->keys, $this->values, $content_model);
-        }
-        $def->child = $this->getChildDef($def, $module);
-    }
-
-    public function generateChildDefCallback($matches)
-    {
-        return $this->info[$matches[0]];
-    }
-
-    /**
-     * Instantiates a ChildDef based on content_model and content_model_type
-     * member variables in HTMLPurifier_ElementDef
-     * @note This will also defer to modules for custom HTMLPurifier_ChildDef
-     *       subclasses that need content set expansion
-     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted
-     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
-     * @return HTMLPurifier_ChildDef corresponding to ElementDef
-     */
-    public function getChildDef($def, $module)
-    {
-        $value = $def->content_model;
-        if (is_object($value)) {
-            trigger_error(
-                'Literal object child definitions should be stored in '.
-                'ElementDef->child not ElementDef->content_model',
-                E_USER_NOTICE
-            );
-            return $value;
-        }
-        switch ($def->content_model_type) {
-            case 'required':
-                return new HTMLPurifier_ChildDef_Required($value);
-            case 'optional':
-                return new HTMLPurifier_ChildDef_Optional($value);
-            case 'empty':
-                return new HTMLPurifier_ChildDef_Empty();
-            case 'custom':
-                return new HTMLPurifier_ChildDef_Custom($value);
-        }
-        // defer to its module
-        $return = false;
-        if ($module->defines_child_def) { // save a func call
-            $return = $module->getChildDef($def);
-        }
-        if ($return !== false) {
-            return $return;
-        }
-        // error-out
-        trigger_error(
-            'Could not determine which ChildDef class to instantiate',
-            E_USER_ERROR
-        );
-        return false;
-    }
-
-    /**
-     * Converts a string list of elements separated by pipes into
-     * a lookup array.
-     * @param string $string List of elements
-     * @return array Lookup array of elements
-     */
-    protected function convertToLookup($string)
-    {
-        $array = explode('|', str_replace(' ', '', $string));
-        $ret = array();
-        foreach ($array as $k) {
-            $ret[$k] = true;
-        }
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php
deleted file mode 100644
index 00e509c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Context.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-/**
- * Registry object that contains information about the current context.
- * @warning Is a bit buggy when variables are set to null: it thinks
- *          they don't exist! So use false instead, please.
- * @note Since the variables Context deals with may not be objects,
- *       references are very important here! Do not remove!
- */
-class HTMLPurifier_Context
-{
-
-    /**
-     * Private array that stores the references.
-     * @type array
-     */
-    private $_storage = array();
-
-    /**
-     * Registers a variable into the context.
-     * @param string $name String name
-     * @param mixed $ref Reference to variable to be registered
-     */
-    public function register($name, &$ref)
-    {
-        if (array_key_exists($name, $this->_storage)) {
-            trigger_error(
-                "Name $name produces collision, cannot re-register",
-                E_USER_ERROR
-            );
-            return;
-        }
-        $this->_storage[$name] =& $ref;
-    }
-
-    /**
-     * Retrieves a variable reference from the context.
-     * @param string $name String name
-     * @param bool $ignore_error Boolean whether or not to ignore error
-     * @return mixed
-     */
-    public function &get($name, $ignore_error = false)
-    {
-        if (!array_key_exists($name, $this->_storage)) {
-            if (!$ignore_error) {
-                trigger_error(
-                    "Attempted to retrieve non-existent variable $name",
-                    E_USER_ERROR
-                );
-            }
-            $var = null; // so we can return by reference
-            return $var;
-        }
-        return $this->_storage[$name];
-    }
-
-    /**
-     * Destroys a variable in the context.
-     * @param string $name String name
-     */
-    public function destroy($name)
-    {
-        if (!array_key_exists($name, $this->_storage)) {
-            trigger_error(
-                "Attempted to destroy non-existent variable $name",
-                E_USER_ERROR
-            );
-            return;
-        }
-        unset($this->_storage[$name]);
-    }
-
-    /**
-     * Checks whether or not the variable exists.
-     * @param string $name String name
-     * @return bool
-     */
-    public function exists($name)
-    {
-        return array_key_exists($name, $this->_storage);
-    }
-
-    /**
-     * Loads a series of variables from an associative array
-     * @param array $context_array Assoc array of variables to load
-     */
-    public function loadArray($context_array)
-    {
-        foreach ($context_array as $key => $discard) {
-            $this->register($key, $context_array[$key]);
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php
deleted file mode 100644
index bc6d433..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Definition.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-/**
- * Super-class for definition datatype objects, implements serialization
- * functions for the class.
- */
-abstract class HTMLPurifier_Definition
-{
-
-    /**
-     * Has setup() been called yet?
-     * @type bool
-     */
-    public $setup = false;
-
-    /**
-     * If true, write out the final definition object to the cache after
-     * setup.  This will be true only if all invocations to get a raw
-     * definition object are also optimized.  This does not cause file
-     * system thrashing because on subsequent calls the cached object
-     * is used and any writes to the raw definition object are short
-     * circuited.  See enduser-customize.html for the high-level
-     * picture.
-     * @type bool
-     */
-    public $optimized = null;
-
-    /**
-     * What type of definition is it?
-     * @type string
-     */
-    public $type;
-
-    /**
-     * Sets up the definition object into the final form, something
-     * not done by the constructor
-     * @param HTMLPurifier_Config $config
-     */
-    abstract protected function doSetup($config);
-
-    /**
-     * Setup function that aborts if already setup
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        if ($this->setup) {
-            return;
-        }
-        $this->setup = true;
-        $this->doSetup($config);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php
deleted file mode 100644
index 9aa8ff3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-/**
- * Abstract class representing Definition cache managers that implements
- * useful common methods and is a factory.
- * @todo Create a separate maintenance file advanced users can use to
- *       cache their custom HTMLDefinition, which can be loaded
- *       via a configuration directive
- * @todo Implement memcached
- */
-abstract class HTMLPurifier_DefinitionCache
-{
-    /**
-     * @type string
-     */
-    public $type;
-
-    /**
-     * @param string $type Type of definition objects this instance of the
-     *      cache will handle.
-     */
-    public function __construct($type)
-    {
-        $this->type = $type;
-    }
-
-    /**
-     * Generates a unique identifier for a particular configuration
-     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
-     * @return string
-     */
-    public function generateKey($config)
-    {
-        return $config->version . ',' . // possibly replace with function calls
-               $config->getBatchSerial($this->type) . ',' .
-               $config->get($this->type . '.DefinitionRev');
-    }
-
-    /**
-     * Tests whether or not a key is old with respect to the configuration's
-     * version and revision number.
-     * @param string $key Key to test
-     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against
-     * @return bool
-     */
-    public function isOld($key, $config)
-    {
-        if (substr_count($key, ',') < 2) {
-            return true;
-        }
-        list($version, $hash, $revision) = explode(',', $key, 3);
-        $compare = version_compare($version, $config->version);
-        // version mismatch, is always old
-        if ($compare != 0) {
-            return true;
-        }
-        // versions match, ids match, check revision number
-        if ($hash == $config->getBatchSerial($this->type) &&
-            $revision < $config->get($this->type . '.DefinitionRev')) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Checks if a definition's type jives with the cache's type
-     * @note Throws an error on failure
-     * @param HTMLPurifier_Definition $def Definition object to check
-     * @return bool true if good, false if not
-     */
-    public function checkDefType($def)
-    {
-        if ($def->type !== $this->type) {
-            trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}");
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Adds a definition object to the cache
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     */
-    abstract public function add($def, $config);
-
-    /**
-     * Unconditionally saves a definition object to the cache
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     */
-    abstract public function set($def, $config);
-
-    /**
-     * Replace an object in the cache
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     */
-    abstract public function replace($def, $config);
-
-    /**
-     * Retrieves a definition object from the cache
-     * @param HTMLPurifier_Config $config
-     */
-    abstract public function get($config);
-
-    /**
-     * Removes a definition object to the cache
-     * @param HTMLPurifier_Config $config
-     */
-    abstract public function remove($config);
-
-    /**
-     * Clears all objects from cache
-     * @param HTMLPurifier_Config $config
-     */
-    abstract public function flush($config);
-
-    /**
-     * Clears all expired (older version or revision) objects from cache
-     * @note Be careful implementing this method as flush. Flush must
-     *       not interfere with other Definition types, and cleanup()
-     *       should not be repeatedly called by userland code.
-     * @param HTMLPurifier_Config $config
-     */
-    abstract public function cleanup($config);
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php
deleted file mode 100644
index b57a51b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache
-{
-
-    /**
-     * Cache object we are decorating
-     * @type HTMLPurifier_DefinitionCache
-     */
-    public $cache;
-
-    /**
-     * The name of the decorator
-     * @var string
-     */
-    public $name;
-
-    public function __construct()
-    {
-    }
-
-    /**
-     * Lazy decorator function
-     * @param HTMLPurifier_DefinitionCache $cache Reference to cache object to decorate
-     * @return HTMLPurifier_DefinitionCache_Decorator
-     */
-    public function decorate(&$cache)
-    {
-        $decorator = $this->copy();
-        // reference is necessary for mocks in PHP 4
-        $decorator->cache =& $cache;
-        $decorator->type = $cache->type;
-        return $decorator;
-    }
-
-    /**
-     * Cross-compatible clone substitute
-     * @return HTMLPurifier_DefinitionCache_Decorator
-     */
-    public function copy()
-    {
-        return new HTMLPurifier_DefinitionCache_Decorator();
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function add($def, $config)
-    {
-        return $this->cache->add($def, $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function set($def, $config)
-    {
-        return $this->cache->set($def, $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function replace($def, $config)
-    {
-        return $this->cache->replace($def, $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function get($config)
-    {
-        return $this->cache->get($config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function remove($config)
-    {
-        return $this->cache->remove($config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function flush($config)
-    {
-        return $this->cache->flush($config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function cleanup($config)
-    {
-        return $this->cache->cleanup($config);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php
deleted file mode 100644
index 4991777..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-/**
- * Definition cache decorator class that cleans up the cache
- * whenever there is a cache miss.
- */
-class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends HTMLPurifier_DefinitionCache_Decorator
-{
-    /**
-     * @type string
-     */
-    public $name = 'Cleanup';
-
-    /**
-     * @return HTMLPurifier_DefinitionCache_Decorator_Cleanup
-     */
-    public function copy()
-    {
-        return new HTMLPurifier_DefinitionCache_Decorator_Cleanup();
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function add($def, $config)
-    {
-        $status = parent::add($def, $config);
-        if (!$status) {
-            parent::cleanup($config);
-        }
-        return $status;
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function set($def, $config)
-    {
-        $status = parent::set($def, $config);
-        if (!$status) {
-            parent::cleanup($config);
-        }
-        return $status;
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function replace($def, $config)
-    {
-        $status = parent::replace($def, $config);
-        if (!$status) {
-            parent::cleanup($config);
-        }
-        return $status;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function get($config)
-    {
-        $ret = parent::get($config);
-        if (!$ret) {
-            parent::cleanup($config);
-        }
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php
deleted file mode 100644
index d529dce..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Memory.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-/**
- * Definition cache decorator class that saves all cache retrievals
- * to PHP's memory; good for unit tests or circumstances where
- * there are lots of configuration objects floating around.
- */
-class HTMLPurifier_DefinitionCache_Decorator_Memory extends HTMLPurifier_DefinitionCache_Decorator
-{
-    /**
-     * @type array
-     */
-    protected $definitions;
-
-    /**
-     * @type string
-     */
-    public $name = 'Memory';
-
-    /**
-     * @return HTMLPurifier_DefinitionCache_Decorator_Memory
-     */
-    public function copy()
-    {
-        return new HTMLPurifier_DefinitionCache_Decorator_Memory();
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function add($def, $config)
-    {
-        $status = parent::add($def, $config);
-        if ($status) {
-            $this->definitions[$this->generateKey($config)] = $def;
-        }
-        return $status;
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function set($def, $config)
-    {
-        $status = parent::set($def, $config);
-        if ($status) {
-            $this->definitions[$this->generateKey($config)] = $def;
-        }
-        return $status;
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function replace($def, $config)
-    {
-        $status = parent::replace($def, $config);
-        if ($status) {
-            $this->definitions[$this->generateKey($config)] = $def;
-        }
-        return $status;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function get($config)
-    {
-        $key = $this->generateKey($config);
-        if (isset($this->definitions[$key])) {
-            return $this->definitions[$key];
-        }
-        $this->definitions[$key] = parent::get($config);
-        return $this->definitions[$key];
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in
deleted file mode 100644
index b1fec8d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-require_once 'HTMLPurifier/DefinitionCache/Decorator.php';
-
-/**
- * Definition cache decorator template.
- */
-class HTMLPurifier_DefinitionCache_Decorator_Template extends HTMLPurifier_DefinitionCache_Decorator
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Template'; // replace this
-
-    public function copy()
-    {
-        // replace class name with yours
-        return new HTMLPurifier_DefinitionCache_Decorator_Template();
-    }
-
-    // remove methods you don't need
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function add($def, $config)
-    {
-        return parent::add($def, $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function set($def, $config)
-    {
-        return parent::set($def, $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function replace($def, $config)
-    {
-        return parent::replace($def, $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function get($config)
-    {
-        return parent::get($config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function flush($config)
-    {
-        return parent::flush($config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return mixed
-     */
-    public function cleanup($config)
-    {
-        return parent::cleanup($config);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php
deleted file mode 100644
index d9a75ce..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Null.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-/**
- * Null cache object to use when no caching is on.
- */
-class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache
-{
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function add($def, $config)
-    {
-        return false;
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function set($def, $config)
-    {
-        return false;
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function replace($def, $config)
-    {
-        return false;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function remove($config)
-    {
-        return false;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function get($config)
-    {
-        return false;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function flush($config)
-    {
-        return false;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function cleanup($config)
-    {
-        return false;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php
deleted file mode 100644
index b82c6bb..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer.php
+++ /dev/null
@@ -1,311 +0,0 @@
-<?php
-
-class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCache
-{
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return int|bool
-     */
-    public function add($def, $config)
-    {
-        if (!$this->checkDefType($def)) {
-            return;
-        }
-        $file = $this->generateFilePath($config);
-        if (file_exists($file)) {
-            return false;
-        }
-        if (!$this->_prepareDir($config)) {
-            return false;
-        }
-        return $this->_write($file, serialize($def), $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return int|bool
-     */
-    public function set($def, $config)
-    {
-        if (!$this->checkDefType($def)) {
-            return;
-        }
-        $file = $this->generateFilePath($config);
-        if (!$this->_prepareDir($config)) {
-            return false;
-        }
-        return $this->_write($file, serialize($def), $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Definition $def
-     * @param HTMLPurifier_Config $config
-     * @return int|bool
-     */
-    public function replace($def, $config)
-    {
-        if (!$this->checkDefType($def)) {
-            return;
-        }
-        $file = $this->generateFilePath($config);
-        if (!file_exists($file)) {
-            return false;
-        }
-        if (!$this->_prepareDir($config)) {
-            return false;
-        }
-        return $this->_write($file, serialize($def), $config);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool|HTMLPurifier_Config
-     */
-    public function get($config)
-    {
-        $file = $this->generateFilePath($config);
-        if (!file_exists($file)) {
-            return false;
-        }
-        return unserialize(file_get_contents($file));
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function remove($config)
-    {
-        $file = $this->generateFilePath($config);
-        if (!file_exists($file)) {
-            return false;
-        }
-        return unlink($file);
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function flush($config)
-    {
-        if (!$this->_prepareDir($config)) {
-            return false;
-        }
-        $dir = $this->generateDirectoryPath($config);
-        $dh = opendir($dir);
-        // Apparently, on some versions of PHP, readdir will return
-        // an empty string if you pass an invalid argument to readdir.
-        // So you need this test.  See #49.
-        if (false === $dh) {
-            return false;
-        }
-        while (false !== ($filename = readdir($dh))) {
-            if (empty($filename)) {
-                continue;
-            }
-            if ($filename[0] === '.') {
-                continue;
-            }
-            unlink($dir . '/' . $filename);
-        }
-        closedir($dh);
-        return true;
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function cleanup($config)
-    {
-        if (!$this->_prepareDir($config)) {
-            return false;
-        }
-        $dir = $this->generateDirectoryPath($config);
-        $dh = opendir($dir);
-        // See #49 (and above).
-        if (false === $dh) {
-            return false;
-        }
-        while (false !== ($filename = readdir($dh))) {
-            if (empty($filename)) {
-                continue;
-            }
-            if ($filename[0] === '.') {
-                continue;
-            }
-            $key = substr($filename, 0, strlen($filename) - 4);
-            if ($this->isOld($key, $config)) {
-                unlink($dir . '/' . $filename);
-            }
-        }
-        closedir($dh);
-        return true;
-    }
-
-    /**
-     * Generates the file path to the serial file corresponding to
-     * the configuration and definition name
-     * @param HTMLPurifier_Config $config
-     * @return string
-     * @todo Make protected
-     */
-    public function generateFilePath($config)
-    {
-        $key = $this->generateKey($config);
-        return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
-    }
-
-    /**
-     * Generates the path to the directory contain this cache's serial files
-     * @param HTMLPurifier_Config $config
-     * @return string
-     * @note No trailing slash
-     * @todo Make protected
-     */
-    public function generateDirectoryPath($config)
-    {
-        $base = $this->generateBaseDirectoryPath($config);
-        return $base . '/' . $this->type;
-    }
-
-    /**
-     * Generates path to base directory that contains all definition type
-     * serials
-     * @param HTMLPurifier_Config $config
-     * @return mixed|string
-     * @todo Make protected
-     */
-    public function generateBaseDirectoryPath($config)
-    {
-        $base = $config->get('Cache.SerializerPath');
-        $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
-        return $base;
-    }
-
-    /**
-     * Convenience wrapper function for file_put_contents
-     * @param string $file File name to write to
-     * @param string $data Data to write into file
-     * @param HTMLPurifier_Config $config
-     * @return int|bool Number of bytes written if success, or false if failure.
-     */
-    private function _write($file, $data, $config)
-    {
-        $result = file_put_contents($file, $data);
-        if ($result !== false) {
-            // set permissions of the new file (no execute)
-            $chmod = $config->get('Cache.SerializerPermissions');
-            if ($chmod !== null) {
-                chmod($file, $chmod & 0666);
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * Prepares the directory that this type stores the serials in
-     * @param HTMLPurifier_Config $config
-     * @return bool True if successful
-     */
-    private function _prepareDir($config)
-    {
-        $directory = $this->generateDirectoryPath($config);
-        $chmod = $config->get('Cache.SerializerPermissions');
-        if ($chmod === null) {
-            if (!@mkdir($directory) && !is_dir($directory)) {
-                trigger_error(
-                    'Could not create directory ' . $directory . '',
-                    E_USER_WARNING
-                );
-                return false;
-            }
-            return true;
-        }
-        if (!is_dir($directory)) {
-            $base = $this->generateBaseDirectoryPath($config);
-            if (!is_dir($base)) {
-                trigger_error(
-                    'Base directory ' . $base . ' does not exist,
-                    please create or change using %Cache.SerializerPath',
-                    E_USER_WARNING
-                );
-                return false;
-            } elseif (!$this->_testPermissions($base, $chmod)) {
-                return false;
-            }
-            if (!@mkdir($directory, $chmod) && !is_dir($directory)) {
-                trigger_error(
-                    'Could not create directory ' . $directory . '',
-                    E_USER_WARNING
-                );
-                return false;
-            }
-            if (!$this->_testPermissions($directory, $chmod)) {
-                return false;
-            }
-        } elseif (!$this->_testPermissions($directory, $chmod)) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Tests permissions on a directory and throws out friendly
-     * error messages and attempts to chmod it itself if possible
-     * @param string $dir Directory path
-     * @param int $chmod Permissions
-     * @return bool True if directory is writable
-     */
-    private function _testPermissions($dir, $chmod)
-    {
-        // early abort, if it is writable, everything is hunky-dory
-        if (is_writable($dir)) {
-            return true;
-        }
-        if (!is_dir($dir)) {
-            // generally, you'll want to handle this beforehand
-            // so a more specific error message can be given
-            trigger_error(
-                'Directory ' . $dir . ' does not exist',
-                E_USER_WARNING
-            );
-            return false;
-        }
-        if (function_exists('posix_getuid') && $chmod !== null) {
-            // POSIX system, we can give more specific advice
-            if (fileowner($dir) === posix_getuid()) {
-                // we can chmod it ourselves
-                $chmod = $chmod | 0700;
-                if (chmod($dir, $chmod)) {
-                    return true;
-                }
-            } elseif (filegroup($dir) === posix_getgid()) {
-                $chmod = $chmod | 0070;
-            } else {
-                // PHP's probably running as nobody, so we'll
-                // need to give global permissions
-                $chmod = $chmod | 0777;
-            }
-            trigger_error(
-                'Directory ' . $dir . ' not writable, ' .
-                'please chmod to ' . decoct($chmod),
-                E_USER_WARNING
-            );
-        } else {
-            // generic error message
-            trigger_error(
-                'Directory ' . $dir . ' not writable, ' .
-                'please alter file permissions',
-                E_USER_WARNING
-            );
-        }
-        return false;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README
deleted file mode 100644
index 2e35c1c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/README
+++ /dev/null
@@ -1,3 +0,0 @@
-This is a dummy file to prevent Git from ignoring this empty directory.
-
-    vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php
deleted file mode 100644
index fd1cc9b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCacheFactory.php
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-
-/**
- * Responsible for creating definition caches.
- */
-class HTMLPurifier_DefinitionCacheFactory
-{
-    /**
-     * @type array
-     */
-    protected $caches = array('Serializer' => array());
-
-    /**
-     * @type array
-     */
-    protected $implementations = array();
-
-    /**
-     * @type HTMLPurifier_DefinitionCache_Decorator[]
-     */
-    protected $decorators = array();
-
-    /**
-     * Initialize default decorators
-     */
-    public function setup()
-    {
-        $this->addDecorator('Cleanup');
-    }
-
-    /**
-     * Retrieves an instance of global definition cache factory.
-     * @param HTMLPurifier_DefinitionCacheFactory $prototype
-     * @return HTMLPurifier_DefinitionCacheFactory
-     */
-    public static function instance($prototype = null)
-    {
-        static $instance;
-        if ($prototype !== null) {
-            $instance = $prototype;
-        } elseif ($instance === null || $prototype === true) {
-            $instance = new HTMLPurifier_DefinitionCacheFactory();
-            $instance->setup();
-        }
-        return $instance;
-    }
-
-    /**
-     * Registers a new definition cache object
-     * @param string $short Short name of cache object, for reference
-     * @param string $long Full class name of cache object, for construction
-     */
-    public function register($short, $long)
-    {
-        $this->implementations[$short] = $long;
-    }
-
-    /**
-     * Factory method that creates a cache object based on configuration
-     * @param string $type Name of definitions handled by cache
-     * @param HTMLPurifier_Config $config Config instance
-     * @return mixed
-     */
-    public function create($type, $config)
-    {
-        $method = $config->get('Cache.DefinitionImpl');
-        if ($method === null) {
-            return new HTMLPurifier_DefinitionCache_Null($type);
-        }
-        if (!empty($this->caches[$method][$type])) {
-            return $this->caches[$method][$type];
-        }
-        if (isset($this->implementations[$method]) &&
-            class_exists($class = $this->implementations[$method], false)) {
-            $cache = new $class($type);
-        } else {
-            if ($method != 'Serializer') {
-                trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING);
-            }
-            $cache = new HTMLPurifier_DefinitionCache_Serializer($type);
-        }
-        foreach ($this->decorators as $decorator) {
-            $new_cache = $decorator->decorate($cache);
-            // prevent infinite recursion in PHP 4
-            unset($cache);
-            $cache = $new_cache;
-        }
-        $this->caches[$method][$type] = $cache;
-        return $this->caches[$method][$type];
-    }
-
-    /**
-     * Registers a decorator to add to all new cache objects
-     * @param HTMLPurifier_DefinitionCache_Decorator|string $decorator An instance or the name of a decorator
-     */
-    public function addDecorator($decorator)
-    {
-        if (is_string($decorator)) {
-            $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";
-            $decorator = new $class;
-        }
-        $this->decorators[$decorator->name] = $decorator;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php
deleted file mode 100644
index 4acd06e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Doctype.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * Represents a document type, contains information on which modules
- * need to be loaded.
- * @note This class is inspected by Printer_HTMLDefinition->renderDoctype.
- *       If structure changes, please update that function.
- */
-class HTMLPurifier_Doctype
-{
-    /**
-     * Full name of doctype
-     * @type string
-     */
-    public $name;
-
-    /**
-     * List of standard modules (string identifiers or literal objects)
-     * that this doctype uses
-     * @type array
-     */
-    public $modules = array();
-
-    /**
-     * List of modules to use for tidying up code
-     * @type array
-     */
-    public $tidyModules = array();
-
-    /**
-     * Is the language derived from XML (i.e. XHTML)?
-     * @type bool
-     */
-    public $xml = true;
-
-    /**
-     * List of aliases for this doctype
-     * @type array
-     */
-    public $aliases = array();
-
-    /**
-     * Public DTD identifier
-     * @type string
-     */
-    public $dtdPublic;
-
-    /**
-     * System DTD identifier
-     * @type string
-     */
-    public $dtdSystem;
-
-    public function __construct(
-        $name = null,
-        $xml = true,
-        $modules = array(),
-        $tidyModules = array(),
-        $aliases = array(),
-        $dtd_public = null,
-        $dtd_system = null
-    ) {
-        $this->name         = $name;
-        $this->xml          = $xml;
-        $this->modules      = $modules;
-        $this->tidyModules  = $tidyModules;
-        $this->aliases      = $aliases;
-        $this->dtdPublic    = $dtd_public;
-        $this->dtdSystem    = $dtd_system;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php
deleted file mode 100644
index acc1d64..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DoctypeRegistry.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-class HTMLPurifier_DoctypeRegistry
-{
-
-    /**
-     * Hash of doctype names to doctype objects.
-     * @type array
-     */
-    protected $doctypes;
-
-    /**
-     * Lookup table of aliases to real doctype names.
-     * @type array
-     */
-    protected $aliases;
-
-    /**
-     * Registers a doctype to the registry
-     * @note Accepts a fully-formed doctype object, or the
-     *       parameters for constructing a doctype object
-     * @param string $doctype Name of doctype or literal doctype object
-     * @param bool $xml
-     * @param array $modules Modules doctype will load
-     * @param array $tidy_modules Modules doctype will load for certain modes
-     * @param array $aliases Alias names for doctype
-     * @param string $dtd_public
-     * @param string $dtd_system
-     * @return HTMLPurifier_Doctype Editable registered doctype
-     */
-    public function register(
-        $doctype,
-        $xml = true,
-        $modules = array(),
-        $tidy_modules = array(),
-        $aliases = array(),
-        $dtd_public = null,
-        $dtd_system = null
-    ) {
-        if (!is_array($modules)) {
-            $modules = array($modules);
-        }
-        if (!is_array($tidy_modules)) {
-            $tidy_modules = array($tidy_modules);
-        }
-        if (!is_array($aliases)) {
-            $aliases = array($aliases);
-        }
-        if (!is_object($doctype)) {
-            $doctype = new HTMLPurifier_Doctype(
-                $doctype,
-                $xml,
-                $modules,
-                $tidy_modules,
-                $aliases,
-                $dtd_public,
-                $dtd_system
-            );
-        }
-        $this->doctypes[$doctype->name] = $doctype;
-        $name = $doctype->name;
-        // hookup aliases
-        foreach ($doctype->aliases as $alias) {
-            if (isset($this->doctypes[$alias])) {
-                continue;
-            }
-            $this->aliases[$alias] = $name;
-        }
-        // remove old aliases
-        if (isset($this->aliases[$name])) {
-            unset($this->aliases[$name]);
-        }
-        return $doctype;
-    }
-
-    /**
-     * Retrieves reference to a doctype of a certain name
-     * @note This function resolves aliases
-     * @note When possible, use the more fully-featured make()
-     * @param string $doctype Name of doctype
-     * @return HTMLPurifier_Doctype Editable doctype object
-     */
-    public function get($doctype)
-    {
-        if (isset($this->aliases[$doctype])) {
-            $doctype = $this->aliases[$doctype];
-        }
-        if (!isset($this->doctypes[$doctype])) {
-            trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);
-            $anon = new HTMLPurifier_Doctype($doctype);
-            return $anon;
-        }
-        return $this->doctypes[$doctype];
-    }
-
-    /**
-     * Creates a doctype based on a configuration object,
-     * will perform initialization on the doctype
-     * @note Use this function to get a copy of doctype that config
-     *       can hold on to (this is necessary in order to tell
-     *       Generator whether or not the current document is XML
-     *       based or not).
-     * @param HTMLPurifier_Config $config
-     * @return HTMLPurifier_Doctype
-     */
-    public function make($config)
-    {
-        return clone $this->get($this->getDoctypeFromConfig($config));
-    }
-
-    /**
-     * Retrieves the doctype from the configuration object
-     * @param HTMLPurifier_Config $config
-     * @return string
-     */
-    public function getDoctypeFromConfig($config)
-    {
-        // recommended test
-        $doctype = $config->get('HTML.Doctype');
-        if (!empty($doctype)) {
-            return $doctype;
-        }
-        $doctype = $config->get('HTML.CustomDoctype');
-        if (!empty($doctype)) {
-            return $doctype;
-        }
-        // backwards-compatibility
-        if ($config->get('HTML.XHTML')) {
-            $doctype = 'XHTML 1.0';
-        } else {
-            $doctype = 'HTML 4.01';
-        }
-        if ($config->get('HTML.Strict')) {
-            $doctype .= ' Strict';
-        } else {
-            $doctype .= ' Transitional';
-        }
-        return $doctype;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php
deleted file mode 100644
index 57cfd2b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ElementDef.php
+++ /dev/null
@@ -1,216 +0,0 @@
-<?php
-
-/**
- * Structure that stores an HTML element definition. Used by
- * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
- * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.
- *       Please update that class too.
- * @warning If you add new properties to this class, you MUST update
- *          the mergeIn() method.
- */
-class HTMLPurifier_ElementDef
-{
-    /**
-     * Does the definition work by itself, or is it created solely
-     * for the purpose of merging into another definition?
-     * @type bool
-     */
-    public $standalone = true;
-
-    /**
-     * Associative array of attribute name to HTMLPurifier_AttrDef.
-     * @type array
-     * @note Before being processed by HTMLPurifier_AttrCollections
-     *       when modules are finalized during
-     *       HTMLPurifier_HTMLDefinition->setup(), this array may also
-     *       contain an array at index 0 that indicates which attribute
-     *       collections to load into the full array. It may also
-     *       contain string indentifiers in lieu of HTMLPurifier_AttrDef,
-     *       see HTMLPurifier_AttrTypes on how they are expanded during
-     *       HTMLPurifier_HTMLDefinition->setup() processing.
-     */
-    public $attr = array();
-
-    // XXX: Design note: currently, it's not possible to override
-    // previously defined AttrTransforms without messing around with
-    // the final generated config. This is by design; a previous version
-    // used an associated list of attr_transform, but it was extremely
-    // easy to accidentally override other attribute transforms by
-    // forgetting to specify an index (and just using 0.)  While we
-    // could check this by checking the index number and complaining,
-    // there is a second problem which is that it is not at all easy to
-    // tell when something is getting overridden. Combine this with a
-    // codebase where this isn't really being used, and it's perfect for
-    // nuking.
-
-    /**
-     * List of tags HTMLPurifier_AttrTransform to be done before validation.
-     * @type array
-     */
-    public $attr_transform_pre = array();
-
-    /**
-     * List of tags HTMLPurifier_AttrTransform to be done after validation.
-     * @type array
-     */
-    public $attr_transform_post = array();
-
-    /**
-     * HTMLPurifier_ChildDef of this tag.
-     * @type HTMLPurifier_ChildDef
-     */
-    public $child;
-
-    /**
-     * Abstract string representation of internal ChildDef rules.
-     * @see HTMLPurifier_ContentSets for how this is parsed and then transformed
-     * into an HTMLPurifier_ChildDef.
-     * @warning This is a temporary variable that is not available after
-     *      being processed by HTMLDefinition
-     * @type string
-     */
-    public $content_model;
-
-    /**
-     * Value of $child->type, used to determine which ChildDef to use,
-     * used in combination with $content_model.
-     * @warning This must be lowercase
-     * @warning This is a temporary variable that is not available after
-     *      being processed by HTMLDefinition
-     * @type string
-     */
-    public $content_model_type;
-
-    /**
-     * Does the element have a content model (#PCDATA | Inline)*? This
-     * is important for chameleon ins and del processing in
-     * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
-     * have to worry about this one.
-     * @type bool
-     */
-    public $descendants_are_inline = false;
-
-    /**
-     * List of the names of required attributes this element has.
-     * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement()
-     * @type array
-     */
-    public $required_attr = array();
-
-    /**
-     * Lookup table of tags excluded from all descendants of this tag.
-     * @type array
-     * @note SGML permits exclusions for all descendants, but this is
-     *       not possible with DTDs or XML Schemas. W3C has elected to
-     *       use complicated compositions of content_models to simulate
-     *       exclusion for children, but we go the simpler, SGML-style
-     *       route of flat-out exclusions, which correctly apply to
-     *       all descendants and not just children. Note that the XHTML
-     *       Modularization Abstract Modules are blithely unaware of such
-     *       distinctions.
-     */
-    public $excludes = array();
-
-    /**
-     * This tag is explicitly auto-closed by the following tags.
-     * @type array
-     */
-    public $autoclose = array();
-
-    /**
-     * If a foreign element is found in this element, test if it is
-     * allowed by this sub-element; if it is, instead of closing the
-     * current element, place it inside this element.
-     * @type string
-     */
-    public $wrap;
-
-    /**
-     * Whether or not this is a formatting element affected by the
-     * "Active Formatting Elements" algorithm.
-     * @type bool
-     */
-    public $formatting;
-
-    /**
-     * Low-level factory constructor for creating new standalone element defs
-     */
-    public static function create($content_model, $content_model_type, $attr)
-    {
-        $def = new HTMLPurifier_ElementDef();
-        $def->content_model = $content_model;
-        $def->content_model_type = $content_model_type;
-        $def->attr = $attr;
-        return $def;
-    }
-
-    /**
-     * Merges the values of another element definition into this one.
-     * Values from the new element def take precedence if a value is
-     * not mergeable.
-     * @param HTMLPurifier_ElementDef $def
-     */
-    public function mergeIn($def)
-    {
-        // later keys takes precedence
-        foreach ($def->attr as $k => $v) {
-            if ($k === 0) {
-                // merge in the includes
-                // sorry, no way to override an include
-                foreach ($v as $v2) {
-                    $this->attr[0][] = $v2;
-                }
-                continue;
-            }
-            if ($v === false) {
-                if (isset($this->attr[$k])) {
-                    unset($this->attr[$k]);
-                }
-                continue;
-            }
-            $this->attr[$k] = $v;
-        }
-        $this->_mergeAssocArray($this->excludes, $def->excludes);
-        $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre);
-        $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post);
-
-        if (!empty($def->content_model)) {
-            $this->content_model =
-                str_replace("#SUPER", (string)$this->content_model, $def->content_model);
-            $this->child = false;
-        }
-        if (!empty($def->content_model_type)) {
-            $this->content_model_type = $def->content_model_type;
-            $this->child = false;
-        }
-        if (!is_null($def->child)) {
-            $this->child = $def->child;
-        }
-        if (!is_null($def->formatting)) {
-            $this->formatting = $def->formatting;
-        }
-        if ($def->descendants_are_inline) {
-            $this->descendants_are_inline = $def->descendants_are_inline;
-        }
-    }
-
-    /**
-     * Merges one array into another, removes values which equal false
-     * @param $a1 Array by reference that is merged into
-     * @param $a2 Array that merges into $a1
-     */
-    private function _mergeAssocArray(&$a1, $a2)
-    {
-        foreach ($a2 as $k => $v) {
-            if ($v === false) {
-                if (isset($a1[$k])) {
-                    unset($a1[$k]);
-                }
-                continue;
-            }
-            $a1[$k] = $v;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php
deleted file mode 100644
index d4791cc..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Encoder.php
+++ /dev/null
@@ -1,617 +0,0 @@
-<?php
-
-/**
- * A UTF-8 specific character encoder that handles cleaning and transforming.
- * @note All functions in this class should be static.
- */
-class HTMLPurifier_Encoder
-{
-
-    /**
-     * Constructor throws fatal error if you attempt to instantiate class
-     */
-    private function __construct()
-    {
-        trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
-    }
-
-    /**
-     * Error-handler that mutes errors, alternative to shut-up operator.
-     */
-    public static function muteErrorHandler()
-    {
-    }
-
-    /**
-     * iconv wrapper which mutes errors, but doesn't work around bugs.
-     * @param string $in Input encoding
-     * @param string $out Output encoding
-     * @param string $text The text to convert
-     * @return string
-     */
-    public static function unsafeIconv($in, $out, $text)
-    {
-        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
-        $r = iconv($in, $out, $text);
-        restore_error_handler();
-        return $r;
-    }
-
-    /**
-     * iconv wrapper which mutes errors and works around bugs.
-     * @param string $in Input encoding
-     * @param string $out Output encoding
-     * @param string $text The text to convert
-     * @param int $max_chunk_size
-     * @return string
-     */
-    public static function iconv($in, $out, $text, $max_chunk_size = 8000)
-    {
-        $code = self::testIconvTruncateBug();
-        if ($code == self::ICONV_OK) {
-            return self::unsafeIconv($in, $out, $text);
-        } elseif ($code == self::ICONV_TRUNCATES) {
-            // we can only work around this if the input character set
-            // is utf-8
-            if ($in == 'utf-8') {
-                if ($max_chunk_size < 4) {
-                    trigger_error('max_chunk_size is too small', E_USER_WARNING);
-                    return false;
-                }
-                // split into 8000 byte chunks, but be careful to handle
-                // multibyte boundaries properly
-                if (($c = strlen($text)) <= $max_chunk_size) {
-                    return self::unsafeIconv($in, $out, $text);
-                }
-                $r = '';
-                $i = 0;
-                while (true) {
-                    if ($i + $max_chunk_size >= $c) {
-                        $r .= self::unsafeIconv($in, $out, substr($text, $i));
-                        break;
-                    }
-                    // wibble the boundary
-                    if (0x80 != (0xC0 & ord($text[$i + $max_chunk_size]))) {
-                        $chunk_size = $max_chunk_size;
-                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 1]))) {
-                        $chunk_size = $max_chunk_size - 1;
-                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 2]))) {
-                        $chunk_size = $max_chunk_size - 2;
-                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 3]))) {
-                        $chunk_size = $max_chunk_size - 3;
-                    } else {
-                        return false; // rather confusing UTF-8...
-                    }
-                    $chunk = substr($text, $i, $chunk_size); // substr doesn't mind overlong lengths
-                    $r .= self::unsafeIconv($in, $out, $chunk);
-                    $i += $chunk_size;
-                }
-                return $r;
-            } else {
-                return false;
-            }
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Cleans a UTF-8 string for well-formedness and SGML validity
-     *
-     * It will parse according to UTF-8 and return a valid UTF8 string, with
-     * non-SGML codepoints excluded.
-     *
-     * Specifically, it will permit:
-     * \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}
-     * Source: https://www.w3.org/TR/REC-xml/#NT-Char
-     * Arguably this function should be modernized to the HTML5 set
-     * of allowed characters:
-     * https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream
-     * which simultaneously expand and restrict the set of allowed characters.
-     *
-     * @param string $str The string to clean
-     * @param bool $force_php
-     * @return string
-     *
-     * @note Just for reference, the non-SGML code points are 0 to 31 and
-     *       127 to 159, inclusive.  However, we allow code points 9, 10
-     *       and 13, which are the tab, line feed and carriage return
-     *       respectively. 128 and above the code points map to multibyte
-     *       UTF-8 representations.
-     *
-     * @note Fallback code adapted from utf8ToUnicode by Henri Sivonen and
-     *       hsivonen@iki.fi at <http://iki.fi/hsivonen/php-utf8/> under the
-     *       LGPL license.  Notes on what changed are inside, but in general,
-     *       the original code transformed UTF-8 text into an array of integer
-     *       Unicode codepoints. Understandably, transforming that back to
-     *       a string would be somewhat expensive, so the function was modded to
-     *       directly operate on the string.  However, this discourages code
-     *       reuse, and the logic enumerated here would be useful for any
-     *       function that needs to be able to understand UTF-8 characters.
-     *       As of right now, only smart lossless character encoding converters
-     *       would need that, and I'm probably not going to implement them.
-     */
-    public static function cleanUTF8($str, $force_php = false)
-    {
-        // UTF-8 validity is checked since PHP 4.3.5
-        // This is an optimization: if the string is already valid UTF-8, no
-        // need to do PHP stuff. 99% of the time, this will be the case.
-        if (preg_match(
-            '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
-            $str
-        )) {
-            return $str;
-        }
-
-        $mState = 0; // cached expected number of octets after the current octet
-                     // until the beginning of the next UTF8 character sequence
-        $mUcs4  = 0; // cached Unicode character
-        $mBytes = 1; // cached expected number of octets in the current sequence
-
-        // original code involved an $out that was an array of Unicode
-        // codepoints.  Instead of having to convert back into UTF-8, we've
-        // decided to directly append valid UTF-8 characters onto a string
-        // $out once they're done.  $char accumulates raw bytes, while $mUcs4
-        // turns into the Unicode code point, so there's some redundancy.
-
-        $out = '';
-        $char = '';
-
-        $len = strlen($str);
-        for ($i = 0; $i < $len; $i++) {
-            $in = ord($str[$i]);
-            $char .= $str[$i]; // append byte to char
-            if (0 == $mState) {
-                // When mState is zero we expect either a US-ASCII character
-                // or a multi-octet sequence.
-                if (0 == (0x80 & ($in))) {
-                    // US-ASCII, pass straight through.
-                    if (($in <= 31 || $in == 127) &&
-                        !($in == 9 || $in == 13 || $in == 10) // save \r\t\n
-                    ) {
-                        // control characters, remove
-                    } else {
-                        $out .= $char;
-                    }
-                    // reset
-                    $char = '';
-                    $mBytes = 1;
-                } elseif (0xC0 == (0xE0 & ($in))) {
-                    // First octet of 2 octet sequence
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x1F) << 6;
-                    $mState = 1;
-                    $mBytes = 2;
-                } elseif (0xE0 == (0xF0 & ($in))) {
-                    // First octet of 3 octet sequence
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x0F) << 12;
-                    $mState = 2;
-                    $mBytes = 3;
-                } elseif (0xF0 == (0xF8 & ($in))) {
-                    // First octet of 4 octet sequence
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x07) << 18;
-                    $mState = 3;
-                    $mBytes = 4;
-                } elseif (0xF8 == (0xFC & ($in))) {
-                    // First octet of 5 octet sequence.
-                    //
-                    // This is illegal because the encoded codepoint must be
-                    // either:
-                    // (a) not the shortest form or
-                    // (b) outside the Unicode range of 0-0x10FFFF.
-                    // Rather than trying to resynchronize, we will carry on
-                    // until the end of the sequence and let the later error
-                    // handling code catch it.
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 0x03) << 24;
-                    $mState = 4;
-                    $mBytes = 5;
-                } elseif (0xFC == (0xFE & ($in))) {
-                    // First octet of 6 octet sequence, see comments for 5
-                    // octet sequence.
-                    $mUcs4 = ($in);
-                    $mUcs4 = ($mUcs4 & 1) << 30;
-                    $mState = 5;
-                    $mBytes = 6;
-                } else {
-                    // Current octet is neither in the US-ASCII range nor a
-                    // legal first octet of a multi-octet sequence.
-                    $mState = 0;
-                    $mUcs4  = 0;
-                    $mBytes = 1;
-                    $char = '';
-                }
-            } else {
-                // When mState is non-zero, we expect a continuation of the
-                // multi-octet sequence
-                if (0x80 == (0xC0 & ($in))) {
-                    // Legal continuation.
-                    $shift = ($mState - 1) * 6;
-                    $tmp = $in;
-                    $tmp = ($tmp & 0x0000003F) << $shift;
-                    $mUcs4 |= $tmp;
-
-                    if (0 == --$mState) {
-                        // End of the multi-octet sequence. mUcs4 now contains
-                        // the final Unicode codepoint to be output
-
-                        // Check for illegal sequences and codepoints.
-
-                        // From Unicode 3.1, non-shortest form is illegal
-                        if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
-                            ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
-                            ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
-                            (4 < $mBytes) ||
-                            // From Unicode 3.2, surrogate characters = illegal
-                            (($mUcs4 & 0xFFFFF800) == 0xD800) ||
-                            // Codepoints outside the Unicode range are illegal
-                            ($mUcs4 > 0x10FFFF)
-                        ) {
-
-                        } elseif (0xFEFF != $mUcs4 && // omit BOM
-                            // check for valid Char unicode codepoints
-                            (
-                                0x9 == $mUcs4 ||
-                                0xA == $mUcs4 ||
-                                0xD == $mUcs4 ||
-                                (0x20 <= $mUcs4 && 0x7E >= $mUcs4) ||
-                                // 7F-9F is not strictly prohibited by XML,
-                                // but it is non-SGML, and thus we don't allow it
-                                (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
-                                (0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) ||
-                                (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
-                            )
-                        ) {
-                            $out .= $char;
-                        }
-                        // initialize UTF8 cache (reset)
-                        $mState = 0;
-                        $mUcs4  = 0;
-                        $mBytes = 1;
-                        $char = '';
-                    }
-                } else {
-                    // ((0xC0 & (*in) != 0x80) && (mState != 0))
-                    // Incomplete multi-octet sequence.
-                    // used to result in complete fail, but we'll reset
-                    $mState = 0;
-                    $mUcs4  = 0;
-                    $mBytes = 1;
-                    $char ='';
-                }
-            }
-        }
-        return $out;
-    }
-
-    /**
-     * Translates a Unicode codepoint into its corresponding UTF-8 character.
-     * @note Based on Feyd's function at
-     *       <http://forums.devnetwork.net/viewtopic.php?p=191404#191404>,
-     *       which is in public domain.
-     * @note While we're going to do code point parsing anyway, a good
-     *       optimization would be to refuse to translate code points that
-     *       are non-SGML characters.  However, this could lead to duplication.
-     * @note This is very similar to the unichr function in
-     *       maintenance/generate-entity-file.php (although this is superior,
-     *       due to its sanity checks).
-     */
-
-    // +----------+----------+----------+----------+
-    // | 33222222 | 22221111 | 111111   |          |
-    // | 10987654 | 32109876 | 54321098 | 76543210 | bit
-    // +----------+----------+----------+----------+
-    // |          |          |          | 0xxxxxxx | 1 byte 0x00000000..0x0000007F
-    // |          |          | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF
-    // |          | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF
-    // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF
-    // +----------+----------+----------+----------+
-    // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF)
-    // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes
-    // +----------+----------+----------+----------+
-
-    public static function unichr($code)
-    {
-        if ($code > 1114111 or $code < 0 or
-          ($code >= 55296 and $code <= 57343) ) {
-            // bits are set outside the "valid" range as defined
-            // by UNICODE 4.1.0
-            return '';
-        }
-
-        $x = $y = $z = $w = 0;
-        if ($code < 128) {
-            // regular ASCII character
-            $x = $code;
-        } else {
-            // set up bits for UTF-8
-            $x = ($code & 63) | 128;
-            if ($code < 2048) {
-                $y = (($code & 2047) >> 6) | 192;
-            } else {
-                $y = (($code & 4032) >> 6) | 128;
-                if ($code < 65536) {
-                    $z = (($code >> 12) & 15) | 224;
-                } else {
-                    $z = (($code >> 12) & 63) | 128;
-                    $w = (($code >> 18) & 7)  | 240;
-                }
-            }
-        }
-        // set up the actual character
-        $ret = '';
-        if ($w) {
-            $ret .= chr($w);
-        }
-        if ($z) {
-            $ret .= chr($z);
-        }
-        if ($y) {
-            $ret .= chr($y);
-        }
-        $ret .= chr($x);
-
-        return $ret;
-    }
-
-    /**
-     * @return bool
-     */
-    public static function iconvAvailable()
-    {
-        static $iconv = null;
-        if ($iconv === null) {
-            $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE;
-        }
-        return $iconv;
-    }
-
-    /**
-     * Convert a string to UTF-8 based on configuration.
-     * @param string $str The string to convert
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public static function convertToUTF8($str, $config, $context)
-    {
-        $encoding = $config->get('Core.Encoding');
-        if ($encoding === 'utf-8') {
-            return $str;
-        }
-        static $iconv = null;
-        if ($iconv === null) {
-            $iconv = self::iconvAvailable();
-        }
-        if ($iconv && !$config->get('Test.ForceNoIconv')) {
-            // unaffected by bugs, since UTF-8 support all characters
-            $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str);
-            if ($str === false) {
-                // $encoding is not a valid encoding
-                trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR);
-                return '';
-            }
-            // If the string is bjorked by Shift_JIS or a similar encoding
-            // that doesn't support all of ASCII, convert the naughty
-            // characters to their true byte-wise ASCII/UTF-8 equivalents.
-            $str = strtr($str, self::testEncodingSupportsASCII($encoding));
-            return $str;
-        } elseif ($encoding === 'iso-8859-1' && function_exists('mb_convert_encoding')) {
-            $str = mb_convert_encoding($str, 'UTF-8', 'ISO-8859-1');
-            return $str;
-        }
-        $bug = HTMLPurifier_Encoder::testIconvTruncateBug();
-        if ($bug == self::ICONV_OK) {
-            trigger_error('Encoding not supported, please install iconv', E_USER_ERROR);
-        } else {
-            trigger_error(
-                'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' .
-                'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541',
-                E_USER_ERROR
-            );
-        }
-    }
-
-    /**
-     * Converts a string from UTF-8 based on configuration.
-     * @param string $str The string to convert
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     * @note Currently, this is a lossy conversion, with unexpressable
-     *       characters being omitted.
-     */
-    public static function convertFromUTF8($str, $config, $context)
-    {
-        $encoding = $config->get('Core.Encoding');
-        if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
-            $str = self::convertToASCIIDumbLossless($str);
-        }
-        if ($encoding === 'utf-8') {
-            return $str;
-        }
-        static $iconv = null;
-        if ($iconv === null) {
-            $iconv = self::iconvAvailable();
-        }
-        if ($iconv && !$config->get('Test.ForceNoIconv')) {
-            // Undo our previous fix in convertToUTF8, otherwise iconv will barf
-            $ascii_fix = self::testEncodingSupportsASCII($encoding);
-            if (!$escape && !empty($ascii_fix)) {
-                $clear_fix = array();
-                foreach ($ascii_fix as $utf8 => $native) {
-                    $clear_fix[$utf8] = '';
-                }
-                $str = strtr($str, $clear_fix);
-            }
-            $str = strtr($str, array_flip($ascii_fix));
-            // Normal stuff
-            $str = self::iconv('utf-8', $encoding . '//IGNORE', $str);
-            return $str;
-        } elseif ($encoding === 'iso-8859-1' && function_exists('mb_convert_encoding')) {
-            $str = mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
-            return $str;
-        }
-        trigger_error('Encoding not supported', E_USER_ERROR);
-        // You might be tempted to assume that the ASCII representation
-        // might be OK, however, this is *not* universally true over all
-        // encodings.  So we take the conservative route here, rather
-        // than forcibly turn on %Core.EscapeNonASCIICharacters
-    }
-
-    /**
-     * Lossless (character-wise) conversion of HTML to ASCII
-     * @param string $str UTF-8 string to be converted to ASCII
-     * @return string ASCII encoded string with non-ASCII character entity-ized
-     * @warning Adapted from MediaWiki, claiming fair use: this is a common
-     *       algorithm. If you disagree with this license fudgery,
-     *       implement it yourself.
-     * @note Uses decimal numeric entities since they are best supported.
-     * @note This is a DUMB function: it has no concept of keeping
-     *       character entities that the projected character encoding
-     *       can allow. We could possibly implement a smart version
-     *       but that would require it to also know which Unicode
-     *       codepoints the charset supported (not an easy task).
-     * @note Sort of with cleanUTF8() but it assumes that $str is
-     *       well-formed UTF-8
-     */
-    public static function convertToASCIIDumbLossless($str)
-    {
-        $bytesleft = 0;
-        $result = '';
-        $working = 0;
-        $len = strlen($str);
-        for ($i = 0; $i < $len; $i++) {
-            $bytevalue = ord($str[$i]);
-            if ($bytevalue <= 0x7F) { //0xxx xxxx
-                $result .= chr($bytevalue);
-                $bytesleft = 0;
-            } elseif ($bytevalue <= 0xBF) { //10xx xxxx
-                $working = $working << 6;
-                $working += ($bytevalue & 0x3F);
-                $bytesleft--;
-                if ($bytesleft <= 0) {
-                    $result .= "&#" . $working . ";";
-                }
-            } elseif ($bytevalue <= 0xDF) { //110x xxxx
-                $working = $bytevalue & 0x1F;
-                $bytesleft = 1;
-            } elseif ($bytevalue <= 0xEF) { //1110 xxxx
-                $working = $bytevalue & 0x0F;
-                $bytesleft = 2;
-            } else { //1111 0xxx
-                $working = $bytevalue & 0x07;
-                $bytesleft = 3;
-            }
-        }
-        return $result;
-    }
-
-    /** No bugs detected in iconv. */
-    const ICONV_OK = 0;
-
-    /** Iconv truncates output if converting from UTF-8 to another
-     *  character set with //IGNORE, and a non-encodable character is found */
-    const ICONV_TRUNCATES = 1;
-
-    /** Iconv does not support //IGNORE, making it unusable for
-     *  transcoding purposes */
-    const ICONV_UNUSABLE = 2;
-
-    /**
-     * glibc iconv has a known bug where it doesn't handle the magic
-     * //IGNORE stanza correctly.  In particular, rather than ignore
-     * characters, it will return an EILSEQ after consuming some number
-     * of characters, and expect you to restart iconv as if it were
-     * an E2BIG.  Old versions of PHP did not respect the errno, and
-     * returned the fragment, so as a result you would see iconv
-     * mysteriously truncating output. We can work around this by
-     * manually chopping our input into segments of about 8000
-     * characters, as long as PHP ignores the error code.  If PHP starts
-     * paying attention to the error code, iconv becomes unusable.
-     *
-     * @return int Error code indicating severity of bug.
-     */
-    public static function testIconvTruncateBug()
-    {
-        static $code = null;
-        if ($code === null) {
-            // better not use iconv, otherwise infinite loop!
-            $r = self::unsafeIconv('utf-8', 'ascii//IGNORE', "\xCE\xB1" . str_repeat('a', 9000));
-            if ($r === false) {
-                $code = self::ICONV_UNUSABLE;
-            } elseif (($c = strlen($r)) < 9000) {
-                $code = self::ICONV_TRUNCATES;
-            } elseif ($c > 9000) {
-                trigger_error(
-                    'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' .
-                    'include your iconv version as per phpversion()',
-                    E_USER_ERROR
-                );
-            } else {
-                $code = self::ICONV_OK;
-            }
-        }
-        return $code;
-    }
-
-    /**
-     * This expensive function tests whether or not a given character
-     * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will
-     * fail this test, and require special processing. Variable width
-     * encodings shouldn't ever fail.
-     *
-     * @param string $encoding Encoding name to test, as per iconv format
-     * @param bool $bypass Whether or not to bypass the precompiled arrays.
-     * @return Array of UTF-8 characters to their corresponding ASCII,
-     *      which can be used to "undo" any overzealous iconv action.
-     */
-    public static function testEncodingSupportsASCII($encoding, $bypass = false)
-    {
-        // All calls to iconv here are unsafe, proof by case analysis:
-        // If ICONV_OK, no difference.
-        // If ICONV_TRUNCATE, all calls involve one character inputs,
-        // so bug is not triggered.
-        // If ICONV_UNUSABLE, this call is irrelevant
-        static $encodings = array();
-        if (!$bypass) {
-            if (isset($encodings[$encoding])) {
-                return $encodings[$encoding];
-            }
-            $lenc = strtolower($encoding);
-            switch ($lenc) {
-                case 'shift_jis':
-                    return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~');
-                case 'johab':
-                    return array("\xE2\x82\xA9" => '\\');
-            }
-            if (strpos($lenc, 'iso-8859-') === 0) {
-                return array();
-            }
-        }
-        $ret = array();
-        if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) {
-            return false;
-        }
-        for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars
-            $c = chr($i); // UTF-8 char
-            $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion
-            if ($r === '' ||
-                // This line is needed for iconv implementations that do not
-                // omit characters that do not exist in the target character set
-                ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c)
-            ) {
-                // Reverse engineer: what's the UTF-8 equiv of this byte
-                // sequence? This assumes that there's no variable width
-                // encoding that doesn't support ASCII.
-                $ret[self::unsafeIconv($encoding, 'UTF-8//IGNORE', $c)] = $c;
-            }
-        }
-        $encodings[$encoding] = $ret;
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php
deleted file mode 100644
index f12ff13..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * Object that provides entity lookup table from entity name to character
- */
-class HTMLPurifier_EntityLookup
-{
-    /**
-     * Assoc array of entity name to character represented.
-     * @type array
-     */
-    public $table;
-
-    /**
-     * Sets up the entity lookup table from the serialized file contents.
-     * @param bool $file
-     * @note The serialized contents are versioned, but were generated
-     *       using the maintenance script generate_entity_file.php
-     * @warning This is not in constructor to help enforce the Singleton
-     */
-    public function setup($file = false)
-    {
-        if (!$file) {
-            $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser';
-        }
-        $this->table = unserialize(file_get_contents($file));
-    }
-
-    /**
-     * Retrieves sole instance of the object.
-     * @param bool|HTMLPurifier_EntityLookup $prototype Optional prototype of custom lookup table to overload with.
-     * @return HTMLPurifier_EntityLookup
-     */
-    public static function instance($prototype = false)
-    {
-        // no references, since PHP doesn't copy unless modified
-        static $instance = null;
-        if ($prototype) {
-            $instance = $prototype;
-        } elseif (!$instance) {
-            $instance = new HTMLPurifier_EntityLookup();
-            $instance->setup();
-        }
-        return $instance;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser
deleted file mode 100644
index e8b0812..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityLookup/entities.ser
+++ /dev/null
@@ -1 +0,0 @@
-a:253:{s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:6:"there4";s:3:"∴";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:4:"sup2";s:2:"²";s:4:"sup3";s:2:"³";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"sup1";s:2:"¹";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"frac14";s:2:"¼";s:6:"frac12";s:2:"½";s:6:"frac34";s:2:"¾";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";}
\ No newline at end of file
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php
deleted file mode 100644
index 3ef2d09..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/EntityParser.php
+++ /dev/null
@@ -1,285 +0,0 @@
-<?php
-
-// if want to implement error collecting here, we'll need to use some sort
-// of global data (probably trigger_error) because it's impossible to pass
-// $config or $context to the callback functions.
-
-/**
- * Handles referencing and derefencing character entities
- */
-class HTMLPurifier_EntityParser
-{
-
-    /**
-     * Reference to entity lookup table.
-     * @type HTMLPurifier_EntityLookup
-     */
-    protected $_entity_lookup;
-
-    /**
-     * Callback regex string for entities in text.
-     * @type string
-     */
-    protected $_textEntitiesRegex;
-
-    /**
-     * Callback regex string for entities in attributes.
-     * @type string
-     */
-    protected $_attrEntitiesRegex;
-
-    /**
-     * Tests if the beginning of a string is a semi-optional regex
-     */
-    protected $_semiOptionalPrefixRegex;
-
-    public function __construct() {
-        // From
-        // http://stackoverflow.com/questions/15532252/why-is-reg-being-rendered-as-without-the-bounding-semicolon
-        $semi_optional = "quot|QUOT|lt|LT|gt|GT|amp|AMP|AElig|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|Iacute|Icirc|Igrave|Iuml|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml";
-
-        // NB: three empty captures to put the fourth match in the right
-        // place
-        $this->_semiOptionalPrefixRegex = "/&()()()($semi_optional)/";
-
-        $this->_textEntitiesRegex =
-            '/&(?:'.
-            // hex
-            '[#]x([a-fA-F0-9]+);?|'.
-            // dec
-            '[#]0*(\d+);?|'.
-            // string (mandatory semicolon)
-            // NB: order matters: match semicolon preferentially
-            '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
-            // string (optional semicolon)
-            "($semi_optional)".
-            ')/';
-
-        $this->_attrEntitiesRegex =
-            '/&(?:'.
-            // hex
-            '[#]x([a-fA-F0-9]+);?|'.
-            // dec
-            '[#]0*(\d+);?|'.
-            // string (mandatory semicolon)
-            // NB: order matters: match semicolon preferentially
-            '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
-            // string (optional semicolon)
-            // don't match if trailing is equals or alphanumeric (URL
-            // like)
-            "($semi_optional)(?![=;A-Za-z0-9])".
-            ')/';
-
-    }
-
-    /**
-     * Substitute entities with the parsed equivalents.  Use this on
-     * textual data in an HTML document (as opposed to attributes.)
-     *
-     * @param string $string String to have entities parsed.
-     * @return string Parsed string.
-     */
-    public function substituteTextEntities($string)
-    {
-        return preg_replace_callback(
-            $this->_textEntitiesRegex,
-            array($this, 'entityCallback'),
-            $string
-        );
-    }
-
-    /**
-     * Substitute entities with the parsed equivalents.  Use this on
-     * attribute contents in documents.
-     *
-     * @param string $string String to have entities parsed.
-     * @return string Parsed string.
-     */
-    public function substituteAttrEntities($string)
-    {
-        return preg_replace_callback(
-            $this->_attrEntitiesRegex,
-            array($this, 'entityCallback'),
-            $string
-        );
-    }
-
-    /**
-     * Callback function for substituteNonSpecialEntities() that does the work.
-     *
-     * @param array $matches  PCRE matches array, with 0 the entire match, and
-     *                  either index 1, 2 or 3 set with a hex value, dec value,
-     *                  or string (respectively).
-     * @return string Replacement string.
-     */
-
-    protected function entityCallback($matches)
-    {
-        $entity = $matches[0];
-        $hex_part = @$matches[1];
-        $dec_part = @$matches[2];
-        $named_part = empty($matches[3]) ? (empty($matches[4]) ? "" : $matches[4]) : $matches[3];
-        if ($hex_part !== NULL && $hex_part !== "") {
-            return HTMLPurifier_Encoder::unichr(hexdec($hex_part));
-        } elseif ($dec_part !== NULL && $dec_part !== "") {
-            return HTMLPurifier_Encoder::unichr((int) $dec_part);
-        } else {
-            if (!$this->_entity_lookup) {
-                $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
-            }
-            if (isset($this->_entity_lookup->table[$named_part])) {
-                return $this->_entity_lookup->table[$named_part];
-            } else {
-                // exact match didn't match anything, so test if
-                // any of the semicolon optional match the prefix.
-                // Test that this is an EXACT match is important to
-                // prevent infinite loop
-                if (!empty($matches[3])) {
-                    return preg_replace_callback(
-                        $this->_semiOptionalPrefixRegex,
-                        array($this, 'entityCallback'),
-                        $entity
-                    );
-                }
-                return $entity;
-            }
-        }
-    }
-
-    // LEGACY CODE BELOW
-
-    /**
-     * Callback regex string for parsing entities.
-     * @type string
-     */
-    protected $_substituteEntitiesRegex =
-        '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/';
-        //     1. hex             2. dec      3. string (XML style)
-
-    /**
-     * Decimal to parsed string conversion table for special entities.
-     * @type array
-     */
-    protected $_special_dec2str =
-            array(
-                    34 => '"',
-                    38 => '&',
-                    39 => "'",
-                    60 => '<',
-                    62 => '>'
-            );
-
-    /**
-     * Stripped entity names to decimal conversion table for special entities.
-     * @type array
-     */
-    protected $_special_ent2dec =
-            array(
-                    'quot' => 34,
-                    'amp'  => 38,
-                    'lt'   => 60,
-                    'gt'   => 62
-            );
-
-    /**
-     * Substitutes non-special entities with their parsed equivalents. Since
-     * running this whenever you have parsed character is t3h 5uck, we run
-     * it before everything else.
-     *
-     * @param string $string String to have non-special entities parsed.
-     * @return string Parsed string.
-     */
-    public function substituteNonSpecialEntities($string)
-    {
-        // it will try to detect missing semicolons, but don't rely on it
-        return preg_replace_callback(
-            $this->_substituteEntitiesRegex,
-            array($this, 'nonSpecialEntityCallback'),
-            $string
-        );
-    }
-
-    /**
-     * Callback function for substituteNonSpecialEntities() that does the work.
-     *
-     * @param array $matches  PCRE matches array, with 0 the entire match, and
-     *                  either index 1, 2 or 3 set with a hex value, dec value,
-     *                  or string (respectively).
-     * @return string Replacement string.
-     */
-
-    protected function nonSpecialEntityCallback($matches)
-    {
-        // replaces all but big five
-        $entity = $matches[0];
-        $is_num = (@$matches[0][1] === '#');
-        if ($is_num) {
-            $is_hex = (@$entity[2] === 'x');
-            $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
-            // abort for special characters
-            if (isset($this->_special_dec2str[$code])) {
-                return $entity;
-            }
-            return HTMLPurifier_Encoder::unichr($code);
-        } else {
-            if (isset($this->_special_ent2dec[$matches[3]])) {
-                return $entity;
-            }
-            if (!$this->_entity_lookup) {
-                $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
-            }
-            if (isset($this->_entity_lookup->table[$matches[3]])) {
-                return $this->_entity_lookup->table[$matches[3]];
-            } else {
-                return $entity;
-            }
-        }
-    }
-
-    /**
-     * Substitutes only special entities with their parsed equivalents.
-     *
-     * @notice We try to avoid calling this function because otherwise, it
-     * would have to be called a lot (for every parsed section).
-     *
-     * @param string $string String to have non-special entities parsed.
-     * @return string Parsed string.
-     */
-    public function substituteSpecialEntities($string)
-    {
-        return preg_replace_callback(
-            $this->_substituteEntitiesRegex,
-            array($this, 'specialEntityCallback'),
-            $string
-        );
-    }
-
-    /**
-     * Callback function for substituteSpecialEntities() that does the work.
-     *
-     * This callback has same syntax as nonSpecialEntityCallback().
-     *
-     * @param array $matches  PCRE-style matches array, with 0 the entire match, and
-     *                  either index 1, 2 or 3 set with a hex value, dec value,
-     *                  or string (respectively).
-     * @return string Replacement string.
-     */
-    protected function specialEntityCallback($matches)
-    {
-        $entity = $matches[0];
-        $is_num = (@$matches[0][1] === '#');
-        if ($is_num) {
-            $is_hex = (@$entity[2] === 'x');
-            $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
-            return isset($this->_special_dec2str[$int]) ?
-                $this->_special_dec2str[$int] :
-                $entity;
-        } else {
-            return isset($this->_special_ent2dec[$matches[3]]) ?
-                $this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] :
-                $entity;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php
deleted file mode 100644
index d47e3f2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorCollector.php
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-
-/**
- * Error collection class that enables HTML Purifier to report HTML
- * problems back to the user
- */
-class HTMLPurifier_ErrorCollector
-{
-
-    /**
-     * Identifiers for the returned error array. These are purposely numeric
-     * so list() can be used.
-     */
-    const LINENO   = 0;
-    const SEVERITY = 1;
-    const MESSAGE  = 2;
-    const CHILDREN = 3;
-
-    /**
-     * @type array
-     */
-    protected $errors;
-
-    /**
-     * @type array
-     */
-    protected $_current;
-
-    /**
-     * @type array
-     */
-    protected $_stacks = array(array());
-
-    /**
-     * @type HTMLPurifier_Language
-     */
-    protected $locale;
-
-    /**
-     * @type HTMLPurifier_Generator
-     */
-    protected $generator;
-
-    /**
-     * @type HTMLPurifier_Context
-     */
-    protected $context;
-
-    /**
-     * @type array
-     */
-    protected $lines = array();
-
-    /**
-     * @param HTMLPurifier_Context $context
-     */
-    public function __construct($context)
-    {
-        $this->locale    =& $context->get('Locale');
-        $this->context   = $context;
-        $this->_current  =& $this->_stacks[0];
-        $this->errors    =& $this->_stacks[0];
-    }
-
-    /**
-     * Sends an error message to the collector for later use
-     * @param int $severity Error severity, PHP error style (don't use E_USER_)
-     * @param string $msg Error message text
-     */
-    public function send($severity, $msg)
-    {
-        $args = array();
-        if (func_num_args() > 2) {
-            $args = func_get_args();
-            array_shift($args);
-            unset($args[0]);
-        }
-
-        $token = $this->context->get('CurrentToken', true);
-        $line  = $token ? $token->line : $this->context->get('CurrentLine', true);
-        $col   = $token ? $token->col  : $this->context->get('CurrentCol', true);
-        $attr  = $this->context->get('CurrentAttr', true);
-
-        // perform special substitutions, also add custom parameters
-        $subst = array();
-        if (!is_null($token)) {
-            $args['CurrentToken'] = $token;
-        }
-        if (!is_null($attr)) {
-            $subst['$CurrentAttr.Name'] = $attr;
-            if (isset($token->attr[$attr])) {
-                $subst['$CurrentAttr.Value'] = $token->attr[$attr];
-            }
-        }
-
-        if (empty($args)) {
-            $msg = $this->locale->getMessage($msg);
-        } else {
-            $msg = $this->locale->formatMessage($msg, $args);
-        }
-
-        if (!empty($subst)) {
-            $msg = strtr($msg, $subst);
-        }
-
-        // (numerically indexed)
-        $error = array(
-            self::LINENO   => $line,
-            self::SEVERITY => $severity,
-            self::MESSAGE  => $msg,
-            self::CHILDREN => array()
-        );
-        $this->_current[] = $error;
-
-        // NEW CODE BELOW ...
-        // Top-level errors are either:
-        //  TOKEN type, if $value is set appropriately, or
-        //  "syntax" type, if $value is null
-        $new_struct = new HTMLPurifier_ErrorStruct();
-        $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
-        if ($token) {
-            $new_struct->value = clone $token;
-        }
-        if (is_int($line) && is_int($col)) {
-            if (isset($this->lines[$line][$col])) {
-                $struct = $this->lines[$line][$col];
-            } else {
-                $struct = $this->lines[$line][$col] = $new_struct;
-            }
-            // These ksorts may present a performance problem
-            ksort($this->lines[$line], SORT_NUMERIC);
-        } else {
-            if (isset($this->lines[-1])) {
-                $struct = $this->lines[-1];
-            } else {
-                $struct = $this->lines[-1] = $new_struct;
-            }
-        }
-        ksort($this->lines, SORT_NUMERIC);
-
-        // Now, check if we need to operate on a lower structure
-        if (!empty($attr)) {
-            $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);
-            if (!$struct->value) {
-                $struct->value = array($attr, 'PUT VALUE HERE');
-            }
-        }
-        if (!empty($cssprop)) {
-            $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);
-            if (!$struct->value) {
-                // if we tokenize CSS this might be a little more difficult to do
-                $struct->value = array($cssprop, 'PUT VALUE HERE');
-            }
-        }
-
-        // Ok, structs are all setup, now time to register the error
-        $struct->addError($severity, $msg);
-    }
-
-    /**
-     * Retrieves raw error data for custom formatter to use
-     */
-    public function getRaw()
-    {
-        return $this->errors;
-    }
-
-    /**
-     * Default HTML formatting implementation for error messages
-     * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature
-     * @param array $errors Errors array to display; used for recursion.
-     * @return string
-     */
-    public function getHTMLFormatted($config, $errors = null)
-    {
-        $ret = array();
-
-        $this->generator = new HTMLPurifier_Generator($config, $this->context);
-        if ($errors === null) {
-            $errors = $this->errors;
-        }
-
-        // 'At line' message needs to be removed
-
-        // generation code for new structure goes here. It needs to be recursive.
-        foreach ($this->lines as $line => $col_array) {
-            if ($line == -1) {
-                continue;
-            }
-            foreach ($col_array as $col => $struct) {
-                $this->_renderStruct($ret, $struct, $line, $col);
-            }
-        }
-        if (isset($this->lines[-1])) {
-            $this->_renderStruct($ret, $this->lines[-1]);
-        }
-
-        if (empty($errors)) {
-            return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
-        } else {
-            return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
-        }
-
-    }
-
-    private function _renderStruct(&$ret, $struct, $line = null, $col = null)
-    {
-        $stack = array($struct);
-        $context_stack = array(array());
-        while ($current = array_pop($stack)) {
-            $context = array_pop($context_stack);
-            foreach ($current->errors as $error) {
-                list($severity, $msg) = $error;
-                $string = '';
-                $string .= '<div>';
-                // W3C uses an icon to indicate the severity of the error.
-                $error = $this->locale->getErrorName($severity);
-                $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> ";
-                if (!is_null($line) && !is_null($col)) {
-                    $string .= "<em class=\"location\">Line $line, Column $col: </em> ";
-                } else {
-                    $string .= '<em class="location">End of Document: </em> ';
-                }
-                $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> ';
-                $string .= '</div>';
-                // Here, have a marker for the character on the column appropriate.
-                // Be sure to clip extremely long lines.
-                //$string .= '<pre>';
-                //$string .= '';
-                //$string .= '</pre>';
-                $ret[] = $string;
-            }
-            foreach ($current->children as $array) {
-                $context[] = $current;
-                $stack = array_merge($stack, array_reverse($array, true));
-                for ($i = count($array); $i > 0; $i--) {
-                    $context_stack[] = $context;
-                }
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php
deleted file mode 100644
index cf869d3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ErrorStruct.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * Records errors for particular segments of an HTML document such as tokens,
- * attributes or CSS properties. They can contain error structs (which apply
- * to components of what they represent), but their main purpose is to hold
- * errors applying to whatever struct is being used.
- */
-class HTMLPurifier_ErrorStruct
-{
-
-    /**
-     * Possible values for $children first-key. Note that top-level structures
-     * are automatically token-level.
-     */
-    const TOKEN     = 0;
-    const ATTR      = 1;
-    const CSSPROP   = 2;
-
-    /**
-     * Type of this struct.
-     * @type string
-     */
-    public $type;
-
-    /**
-     * Value of the struct we are recording errors for. There are various
-     * values for this:
-     *  - TOKEN: Instance of HTMLPurifier_Token
-     *  - ATTR: array('attr-name', 'value')
-     *  - CSSPROP: array('prop-name', 'value')
-     * @type mixed
-     */
-    public $value;
-
-    /**
-     * Errors registered for this structure.
-     * @type array
-     */
-    public $errors = array();
-
-    /**
-     * Child ErrorStructs that are from this structure. For example, a TOKEN
-     * ErrorStruct would contain ATTR ErrorStructs. This is a multi-dimensional
-     * array in structure: [TYPE]['identifier']
-     * @type array
-     */
-    public $children = array();
-
-    /**
-     * @param string $type
-     * @param string $id
-     * @return mixed
-     */
-    public function getChild($type, $id)
-    {
-        if (!isset($this->children[$type][$id])) {
-            $this->children[$type][$id] = new HTMLPurifier_ErrorStruct();
-            $this->children[$type][$id]->type = $type;
-        }
-        return $this->children[$type][$id];
-    }
-
-    /**
-     * @param int $severity
-     * @param string $message
-     */
-    public function addError($severity, $message)
-    {
-        $this->errors[] = array($severity, $message);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php
deleted file mode 100644
index be85b4c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Exception.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-/**
- * Global exception class for HTML Purifier; any exceptions we throw
- * are from here.
- */
-class HTMLPurifier_Exception extends Exception
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter.php
deleted file mode 100644
index c1f41ee..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Represents a pre or post processing filter on HTML Purifier's output
- *
- * Sometimes, a little ad-hoc fixing of HTML has to be done before
- * it gets sent through HTML Purifier: you can use filters to acheive
- * this effect. For instance, YouTube videos can be preserved using
- * this manner. You could have used a decorator for this task, but
- * PHP's support for them is not terribly robust, so we're going
- * to just loop through the filters.
- *
- * Filters should be exited first in, last out. If there are three filters,
- * named 1, 2 and 3, the order of execution should go 1->preFilter,
- * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter,
- * 1->postFilter.
- *
- * @note Methods are not declared abstract as it is perfectly legitimate
- *       for an implementation not to want anything to happen on a step
- */
-
-class HTMLPurifier_Filter
-{
-
-    /**
-     * Name of the filter for identification purposes.
-     * @type string
-     */
-    public $name;
-
-    /**
-     * Pre-processor function, handles HTML before HTML Purifier
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public function preFilter($html, $config, $context)
-    {
-        return $html;
-    }
-
-    /**
-     * Post-processor function, handles HTML after HTML Purifier
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public function postFilter($html, $config, $context)
-    {
-        return $html;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php
deleted file mode 100644
index 66f70b0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php
+++ /dev/null
@@ -1,341 +0,0 @@
-<?php
-
-// why is this a top level function? Because PHP 5.2.0 doesn't seem to
-// understand how to interpret this filter if it's a static method.
-// It's all really silly, but if we go this route it might be reasonable
-// to coalesce all of these methods into one.
-function htmlpurifier_filter_extractstyleblocks_muteerrorhandler()
-{
-}
-
-/**
- * This filter extracts <style> blocks from input HTML, cleans them up
- * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks')
- * so they can be used elsewhere in the document.
- *
- * @note
- *      See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for
- *      sample usage.
- *
- * @note
- *      This filter can also be used on stylesheets not included in the
- *      document--something purists would probably prefer. Just directly
- *      call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS()
- */
-class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
-{
-    /**
-     * @type string
-     */
-    public $name = 'ExtractStyleBlocks';
-
-    /**
-     * @type array
-     */
-    private $_styleMatches = array();
-
-    /**
-     * @type csstidy
-     */
-    private $_tidy;
-
-    /**
-     * @type HTMLPurifier_AttrDef_HTML_ID
-     */
-    private $_id_attrdef;
-
-    /**
-     * @type HTMLPurifier_AttrDef_CSS_Ident
-     */
-    private $_class_attrdef;
-
-    /**
-     * @type HTMLPurifier_AttrDef_Enum
-     */
-    private $_enum_attrdef;
-
-    public function __construct()
-    {
-        $this->_tidy = new csstidy();
-        $this->_tidy->set_cfg('lowercase_s', false);
-        $this->_id_attrdef = new HTMLPurifier_AttrDef_HTML_ID(true);
-        $this->_class_attrdef = new HTMLPurifier_AttrDef_CSS_Ident();
-        $this->_enum_attrdef = new HTMLPurifier_AttrDef_Enum(
-            array(
-                'first-child',
-                'link',
-                'visited',
-                'active',
-                'hover',
-                'focus'
-            )
-        );
-    }
-
-    /**
-     * Save the contents of CSS blocks to style matches
-     * @param array $matches preg_replace style $matches array
-     */
-    protected function styleCallback($matches)
-    {
-        $this->_styleMatches[] = $matches[1];
-    }
-
-    /**
-     * Removes inline <style> tags from HTML, saves them for later use
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     * @todo Extend to indicate non-text/css style blocks
-     */
-    public function preFilter($html, $config, $context)
-    {
-        $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');
-        if ($tidy !== null) {
-            $this->_tidy = $tidy;
-        }
-        // NB: this must be NON-greedy because if we have
-        // <style>foo</style>  <style>bar</style>
-        // we must not grab foo</style>  <style>bar
-        $html = preg_replace_callback('#<style(?:\s.*)?>(.*)<\/style>#isU', array($this, 'styleCallback'), $html);
-        $style_blocks = $this->_styleMatches;
-        $this->_styleMatches = array(); // reset
-        $context->register('StyleBlocks', $style_blocks); // $context must not be reused
-        if ($this->_tidy) {
-            foreach ($style_blocks as &$style) {
-                $style = $this->cleanCSS($style, $config, $context);
-            }
-        }
-        return $html;
-    }
-
-    /**
-     * Takes CSS (the stuff found in <style>) and cleans it.
-     * @warning Requires CSSTidy <http://csstidy.sourceforge.net/>
-     * @param string $css CSS styling to clean
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @throws HTMLPurifier_Exception
-     * @return string Cleaned CSS
-     */
-    public function cleanCSS($css, $config, $context)
-    {
-        // prepare scope
-        $scope = $config->get('Filter.ExtractStyleBlocks.Scope');
-        if ($scope !== null) {
-            $scopes = array_map('trim', explode(',', $scope));
-        } else {
-            $scopes = array();
-        }
-        // remove comments from CSS
-        $css = trim($css);
-        if (strncmp('<!--', $css, 4) === 0) {
-            $css = substr($css, 4);
-        }
-        if (strlen($css) > 3 && substr($css, -3) == '-->') {
-            $css = substr($css, 0, -3);
-        }
-        $css = trim($css);
-        set_error_handler('htmlpurifier_filter_extractstyleblocks_muteerrorhandler');
-        $this->_tidy->parse($css);
-        restore_error_handler();
-        $css_definition = $config->getDefinition('CSS');
-        $html_definition = $config->getDefinition('HTML');
-        $new_css = array();
-        foreach ($this->_tidy->css as $k => $decls) {
-            // $decls are all CSS declarations inside an @ selector
-            $new_decls = array();
-            foreach ($decls as $selector => $style) {
-                $selector = trim($selector);
-                if ($selector === '') {
-                    continue;
-                } // should not happen
-                // Parse the selector
-                // Here is the relevant part of the CSS grammar:
-                //
-                // ruleset
-                //   : selector [ ',' S* selector ]* '{' ...
-                // selector
-                //   : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?
-                // combinator
-                //   : '+' S*
-                //   : '>' S*
-                // simple_selector
-                //   : element_name [ HASH | class | attrib | pseudo ]*
-                //   | [ HASH | class | attrib | pseudo ]+
-                // element_name
-                //   : IDENT | '*'
-                //   ;
-                // class
-                //   : '.' IDENT
-                //   ;
-                // attrib
-                //   : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
-                //     [ IDENT | STRING ] S* ]? ']'
-                //   ;
-                // pseudo
-                //   : ':' [ IDENT | FUNCTION S* [IDENT S*]? ')' ]
-                //   ;
-                //
-                // For reference, here are the relevant tokens:
-                //
-                // HASH         #{name}
-                // IDENT        {ident}
-                // INCLUDES     ==
-                // DASHMATCH    |=
-                // STRING       {string}
-                // FUNCTION     {ident}\(
-                //
-                // And the lexical scanner tokens
-                //
-                // name         {nmchar}+
-                // nmchar       [_a-z0-9-]|{nonascii}|{escape}
-                // nonascii     [\240-\377]
-                // escape       {unicode}|\\[^\r\n\f0-9a-f]
-                // unicode      \\{h}}{1,6}(\r\n|[ \t\r\n\f])?
-                // ident        -?{nmstart}{nmchar*}
-                // nmstart      [_a-z]|{nonascii}|{escape}
-                // string       {string1}|{string2}
-                // string1      \"([^\n\r\f\\"]|\\{nl}|{escape})*\"
-                // string2      \'([^\n\r\f\\"]|\\{nl}|{escape})*\'
-                //
-                // We'll implement a subset (in order to reduce attack
-                // surface); in particular:
-                //
-                //      - No Unicode support
-                //      - No escapes support
-                //      - No string support (by proxy no attrib support)
-                //      - element_name is matched against allowed
-                //        elements (some people might find this
-                //        annoying...)
-                //      - Pseudo-elements one of :first-child, :link,
-                //        :visited, :active, :hover, :focus
-
-                // handle ruleset
-                $selectors = array_map('trim', explode(',', $selector));
-                $new_selectors = array();
-                foreach ($selectors as $sel) {
-                    // split on +, > and spaces
-                    $basic_selectors = preg_split('/\s*([+> ])\s*/', $sel, -1, PREG_SPLIT_DELIM_CAPTURE);
-                    // even indices are chunks, odd indices are
-                    // delimiters
-                    $nsel = null;
-                    $delim = null; // guaranteed to be non-null after
-                    // two loop iterations
-                    for ($i = 0, $c = count($basic_selectors); $i < $c; $i++) {
-                        $x = $basic_selectors[$i];
-                        if ($i % 2) {
-                            // delimiter
-                            if ($x === ' ') {
-                                $delim = ' ';
-                            } else {
-                                $delim = ' ' . $x . ' ';
-                            }
-                        } else {
-                            // simple selector
-                            $components = preg_split('/([#.:])/', $x, -1, PREG_SPLIT_DELIM_CAPTURE);
-                            $sdelim = null;
-                            $nx = null;
-                            for ($j = 0, $cc = count($components); $j < $cc; $j++) {
-                                $y = $components[$j];
-                                if ($j === 0) {
-                                    if ($y === '*' || isset($html_definition->info[$y = strtolower($y)])) {
-                                        $nx = $y;
-                                    } else {
-                                        // $nx stays null; this matters
-                                        // if we don't manage to find
-                                        // any valid selector content,
-                                        // in which case we ignore the
-                                        // outer $delim
-                                    }
-                                } elseif ($j % 2) {
-                                    // set delimiter
-                                    $sdelim = $y;
-                                } else {
-                                    $attrdef = null;
-                                    if ($sdelim === '#') {
-                                        $attrdef = $this->_id_attrdef;
-                                    } elseif ($sdelim === '.') {
-                                        $attrdef = $this->_class_attrdef;
-                                    } elseif ($sdelim === ':') {
-                                        $attrdef = $this->_enum_attrdef;
-                                    } else {
-                                        throw new HTMLPurifier_Exception('broken invariant sdelim and preg_split');
-                                    }
-                                    $r = $attrdef->validate($y, $config, $context);
-                                    if ($r !== false) {
-                                        if ($r !== true) {
-                                            $y = $r;
-                                        }
-                                        if ($nx === null) {
-                                            $nx = '';
-                                        }
-                                        $nx .= $sdelim . $y;
-                                    }
-                                }
-                            }
-                            if ($nx !== null) {
-                                if ($nsel === null) {
-                                    $nsel = $nx;
-                                } else {
-                                    $nsel .= $delim . $nx;
-                                }
-                            } else {
-                                // delimiters to the left of invalid
-                                // basic selector ignored
-                            }
-                        }
-                    }
-                    if ($nsel !== null) {
-                        if (!empty($scopes)) {
-                            foreach ($scopes as $s) {
-                                $new_selectors[] = "$s $nsel";
-                            }
-                        } else {
-                            $new_selectors[] = $nsel;
-                        }
-                    }
-                }
-                if (empty($new_selectors)) {
-                    continue;
-                }
-                $selector = implode(', ', $new_selectors);
-                foreach ($style as $name => $value) {
-                    if (!isset($css_definition->info[$name])) {
-                        unset($style[$name]);
-                        continue;
-                    }
-                    $def = $css_definition->info[$name];
-                    $ret = $def->validate($value, $config, $context);
-                    if ($ret === false) {
-                        unset($style[$name]);
-                    } else {
-                        $style[$name] = $ret;
-                    }
-                }
-                $new_decls[$selector] = $style;
-            }
-            $new_css[$k] = $new_decls;
-        }
-        // remove stuff that shouldn't be used, could be reenabled
-        // after security risks are analyzed
-        $this->_tidy->css = $new_css;
-        $this->_tidy->import = array();
-        $this->_tidy->charset = null;
-        $this->_tidy->namespace = null;
-        $css = $this->_tidy->print->plain();
-        // we are going to escape any special characters <>& to ensure
-        // that no funny business occurs (i.e. </style> in a font-family prop).
-        if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
-            $css = str_replace(
-                array('<', '>', '&'),
-                array('\3C ', '\3E ', '\26 '),
-                $css
-            );
-        }
-        return $css;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php
deleted file mode 100644
index 276d836..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'YouTube';
-
-    /**
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public function preFilter($html, $config, $context)
-    {
-        $pre_regex = '#<object[^>]+>.+?' .
-            '(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
-        $pre_replace = '<span class="youtube-embed">\1</span>';
-        return preg_replace($pre_regex, $pre_replace, $html);
-    }
-
-    /**
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public function postFilter($html, $config, $context)
-    {
-        $post_regex = '#<span class="youtube-embed">((?:v|cp)/[A-Za-z0-9\-_=]+)</span>#';
-        return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html);
-    }
-
-    /**
-     * @param $url
-     * @return string
-     */
-    protected function armorUrl($url)
-    {
-        return str_replace('--', '-&#45;', $url);
-    }
-
-    /**
-     * @param array $matches
-     * @return string
-     */
-    protected function postFilterCallback($matches)
-    {
-        $url = $this->armorUrl($matches[1]);
-        return '<object width="425" height="350" type="application/x-shockwave-flash" ' .
-        'data="//www.youtube.com/' . $url . '">' .
-        '<param name="movie" value="//www.youtube.com/' . $url . '"></param>' .
-        '<!--[if IE]>' .
-        '<embed src="//www.youtube.com/' . $url . '"' .
-        'type="application/x-shockwave-flash"' .
-        'wmode="transparent" width="425" height="350" />' .
-        '<![endif]-->' .
-        '</object>';
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php
deleted file mode 100644
index eb56e2d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php
+++ /dev/null
@@ -1,286 +0,0 @@
-<?php
-
-/**
- * Generates HTML from tokens.
- * @todo Refactor interface so that configuration/context is determined
- *       upon instantiation, no need for messy generateFromTokens() calls
- * @todo Make some of the more internal functions protected, and have
- *       unit tests work around that
- */
-class HTMLPurifier_Generator
-{
-
-    /**
-     * Whether or not generator should produce XML output.
-     * @type bool
-     */
-    private $_xhtml = true;
-
-    /**
-     * :HACK: Whether or not generator should comment the insides of <script> tags.
-     * @type bool
-     */
-    private $_scriptFix = false;
-
-    /**
-     * Cache of HTMLDefinition during HTML output to determine whether or
-     * not attributes should be minimized.
-     * @type HTMLPurifier_HTMLDefinition
-     */
-    private $_def;
-
-    /**
-     * Cache of %Output.SortAttr.
-     * @type bool
-     */
-    private $_sortAttr;
-
-    /**
-     * Cache of %Output.FlashCompat.
-     * @type bool
-     */
-    private $_flashCompat;
-
-    /**
-     * Cache of %Output.FixInnerHTML.
-     * @type bool
-     */
-    private $_innerHTMLFix;
-
-    /**
-     * Stack for keeping track of object information when outputting IE
-     * compatibility code.
-     * @type array
-     */
-    private $_flashStack = array();
-
-    /**
-     * Configuration for the generator
-     * @type HTMLPurifier_Config
-     */
-    protected $config;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     */
-    public function __construct($config, $context)
-    {
-        $this->config = $config;
-        $this->_scriptFix = $config->get('Output.CommentScriptContents');
-        $this->_innerHTMLFix = $config->get('Output.FixInnerHTML');
-        $this->_sortAttr = $config->get('Output.SortAttr');
-        $this->_flashCompat = $config->get('Output.FlashCompat');
-        $this->_def = $config->getHTMLDefinition();
-        $this->_xhtml = $this->_def->doctype->xml;
-    }
-
-    /**
-     * Generates HTML from an array of tokens.
-     * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token
-     * @return string Generated HTML
-     */
-    public function generateFromTokens($tokens)
-    {
-        if (!$tokens) {
-            return '';
-        }
-
-        // Basic algorithm
-        $html = '';
-        for ($i = 0, $size = count($tokens); $i < $size; $i++) {
-            if ($this->_scriptFix && $tokens[$i]->name === 'script'
-                && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) {
-                // script special case
-                // the contents of the script block must be ONE token
-                // for this to work.
-                $html .= $this->generateFromToken($tokens[$i++]);
-                $html .= $this->generateScriptFromToken($tokens[$i++]);
-            }
-            $html .= $this->generateFromToken($tokens[$i]);
-        }
-
-        // Tidy cleanup
-        if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
-            $tidy = new Tidy;
-            $tidy->parseString(
-                $html,
-                array(
-                   'indent'=> true,
-                   'output-xhtml' => $this->_xhtml,
-                   'show-body-only' => true,
-                   'indent-spaces' => 2,
-                   'wrap' => 68,
-                ),
-                'utf8'
-            );
-            $tidy->cleanRepair();
-            $html = (string) $tidy; // explicit cast necessary
-        }
-
-        // Normalize newlines to system defined value
-        if ($this->config->get('Core.NormalizeNewlines')) {
-            $nl = $this->config->get('Output.Newline');
-            if ($nl === null) {
-                $nl = PHP_EOL;
-            }
-            if ($nl !== "\n") {
-                $html = str_replace("\n", $nl, $html);
-            }
-        }
-        return $html;
-    }
-
-    /**
-     * Generates HTML from a single token.
-     * @param HTMLPurifier_Token $token HTMLPurifier_Token object.
-     * @return string Generated HTML
-     */
-    public function generateFromToken($token)
-    {
-        if (!$token instanceof HTMLPurifier_Token) {
-            trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
-            return '';
-
-        } elseif ($token instanceof HTMLPurifier_Token_Start) {
-            $attr = $this->generateAttributes($token->attr, $token->name);
-            if ($this->_flashCompat) {
-                if ($token->name == "object") {
-                    $flash = new stdClass();
-                    $flash->attr = $token->attr;
-                    $flash->param = array();
-                    $this->_flashStack[] = $flash;
-                }
-            }
-            return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>';
-
-        } elseif ($token instanceof HTMLPurifier_Token_End) {
-            $_extra = '';
-            if ($this->_flashCompat) {
-                if ($token->name == "object" && !empty($this->_flashStack)) {
-                    // doesn't do anything for now
-                }
-            }
-            return $_extra . '</' . $token->name . '>';
-
-        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
-            if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) {
-                $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value'];
-            }
-            $attr = $this->generateAttributes($token->attr, $token->name);
-             return '<' . $token->name . ($attr ? ' ' : '') . $attr .
-                ( $this->_xhtml ? ' /': '' ) // <br /> v. <br>
-                . '>';
-
-        } elseif ($token instanceof HTMLPurifier_Token_Text) {
-            return $this->escape($token->data, ENT_NOQUOTES);
-
-        } elseif ($token instanceof HTMLPurifier_Token_Comment) {
-            return '<!--' . $token->data . '-->';
-        } else {
-            return '';
-
-        }
-    }
-
-    /**
-     * Special case processor for the contents of script tags
-     * @param HTMLPurifier_Token $token HTMLPurifier_Token object.
-     * @return string
-     * @warning This runs into problems if there's already a literal
-     *          --> somewhere inside the script contents.
-     */
-    public function generateScriptFromToken($token)
-    {
-        if (!$token instanceof HTMLPurifier_Token_Text) {
-            return $this->generateFromToken($token);
-        }
-        // Thanks <http://lachy.id.au/log/2005/05/script-comments>
-        $data = preg_replace('#//\s*$#', '', $token->data);
-        return '<!--//--><![CDATA[//><!--' . "\n" . trim($data) . "\n" . '//--><!]]>';
-    }
-
-    /**
-     * Generates attribute declarations from attribute array.
-     * @note This does not include the leading or trailing space.
-     * @param array $assoc_array_of_attributes Attribute array
-     * @param string $element Name of element attributes are for, used to check
-     *        attribute minimization.
-     * @return string Generated HTML fragment for insertion.
-     */
-    public function generateAttributes($assoc_array_of_attributes, $element = '')
-    {
-        $html = '';
-        if ($this->_sortAttr) {
-            ksort($assoc_array_of_attributes);
-        }
-        foreach ($assoc_array_of_attributes as $key => $value) {
-            if (!$this->_xhtml) {
-                // Remove namespaced attributes
-                if (strpos($key, ':') !== false) {
-                    continue;
-                }
-                // Check if we should minimize the attribute: val="val" -> val
-                if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
-                    $html .= $key . ' ';
-                    continue;
-                }
-            }
-            // Workaround for Internet Explorer innerHTML bug.
-            // Essentially, Internet Explorer, when calculating
-            // innerHTML, omits quotes if there are no instances of
-            // angled brackets, quotes or spaces.  However, when parsing
-            // HTML (for example, when you assign to innerHTML), it
-            // treats backticks as quotes.  Thus,
-            //      <img alt="``" />
-            // becomes
-            //      <img alt=`` />
-            // becomes
-            //      <img alt='' />
-            // Fortunately, all we need to do is trigger an appropriate
-            // quoting style, which we do by adding an extra space.
-            // This also is consistent with the W3C spec, which states
-            // that user agents may ignore leading or trailing
-            // whitespace (in fact, most don't, at least for attributes
-            // like alt, but an extra space at the end is barely
-            // noticeable).  Still, we have a configuration knob for
-            // this, since this transformation is not necesary if you
-            // don't process user input with innerHTML or you don't plan
-            // on supporting Internet Explorer.
-            if ($this->_innerHTMLFix) {
-                if (strpos($value, '`') !== false) {
-                    // check if correct quoting style would not already be
-                    // triggered
-                    if (strcspn($value, '"\' <>') === strlen($value)) {
-                        // protect!
-                        $value .= ' ';
-                    }
-                }
-            }
-            $html .= $key.'="'.$this->escape($value).'" ';
-        }
-        return rtrim($html);
-    }
-
-    /**
-     * Escapes raw text data.
-     * @todo This really ought to be protected, but until we have a facility
-     *       for properly generating HTML here w/o using tokens, it stays
-     *       public.
-     * @param string $string String data to escape for HTML.
-     * @param int $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
-     *               permissible for non-attribute output.
-     * @return string escaped data.
-     */
-    public function escape($string, $quote = null)
-    {
-        // Workaround for APC bug on Mac Leopard reported by sidepodcast
-        // http://htmlpurifier.org/phorum/read.php?3,4823,4846
-        if ($quote === null) {
-            $quote = ENT_COMPAT;
-        }
-        return htmlspecialchars($string, $quote, 'UTF-8');
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php
deleted file mode 100644
index 9b7b334..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLDefinition.php
+++ /dev/null
@@ -1,493 +0,0 @@
-<?php
-
-/**
- * Definition of the purified HTML that describes allowed children,
- * attributes, and many other things.
- *
- * Conventions:
- *
- * All member variables that are prefixed with info
- * (including the main $info array) are used by HTML Purifier internals
- * and should not be directly edited when customizing the HTMLDefinition.
- * They can usually be set via configuration directives or custom
- * modules.
- *
- * On the other hand, member variables without the info prefix are used
- * internally by the HTMLDefinition and MUST NOT be used by other HTML
- * Purifier internals. Many of them, however, are public, and may be
- * edited by userspace code to tweak the behavior of HTMLDefinition.
- *
- * @note This class is inspected by Printer_HTMLDefinition; please
- *       update that class if things here change.
- *
- * @warning Directives that change this object's structure must be in
- *          the HTML or Attr namespace!
- */
-class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
-{
-
-    // FULLY-PUBLIC VARIABLES ---------------------------------------------
-
-    /**
-     * Associative array of element names to HTMLPurifier_ElementDef.
-     * @type HTMLPurifier_ElementDef[]
-     */
-    public $info = array();
-
-    /**
-     * Associative array of global attribute name to attribute definition.
-     * @type array
-     */
-    public $info_global_attr = array();
-
-    /**
-     * String name of parent element HTML will be going into.
-     * @type string
-     */
-    public $info_parent = 'div';
-
-    /**
-     * Definition for parent element, allows parent element to be a
-     * tag that's not allowed inside the HTML fragment.
-     * @type HTMLPurifier_ElementDef
-     */
-    public $info_parent_def;
-
-    /**
-     * String name of element used to wrap inline elements in block context.
-     * @type string
-     * @note This is rarely used except for BLOCKQUOTEs in strict mode
-     */
-    public $info_block_wrapper = 'p';
-
-    /**
-     * Associative array of deprecated tag name to HTMLPurifier_TagTransform.
-     * @type array
-     */
-    public $info_tag_transform = array();
-
-    /**
-     * Indexed list of HTMLPurifier_AttrTransform to be performed before validation.
-     * @type HTMLPurifier_AttrTransform[]
-     */
-    public $info_attr_transform_pre = array();
-
-    /**
-     * Indexed list of HTMLPurifier_AttrTransform to be performed after validation.
-     * @type HTMLPurifier_AttrTransform[]
-     */
-    public $info_attr_transform_post = array();
-
-    /**
-     * Nested lookup array of content set name (Block, Inline) to
-     * element name to whether or not it belongs in that content set.
-     * @type array
-     */
-    public $info_content_sets = array();
-
-    /**
-     * Indexed list of HTMLPurifier_Injector to be used.
-     * @type HTMLPurifier_Injector[]
-     */
-    public $info_injector = array();
-
-    /**
-     * Doctype object
-     * @type HTMLPurifier_Doctype
-     */
-    public $doctype;
-
-
-
-    // RAW CUSTOMIZATION STUFF --------------------------------------------
-
-    /**
-     * Adds a custom attribute to a pre-existing element
-     * @note This is strictly convenience, and does not have a corresponding
-     *       method in HTMLPurifier_HTMLModule
-     * @param string $element_name Element name to add attribute to
-     * @param string $attr_name Name of attribute
-     * @param mixed $def Attribute definition, can be string or object, see
-     *             HTMLPurifier_AttrTypes for details
-     */
-    public function addAttribute($element_name, $attr_name, $def)
-    {
-        $module = $this->getAnonymousModule();
-        if (!isset($module->info[$element_name])) {
-            $element = $module->addBlankElement($element_name);
-        } else {
-            $element = $module->info[$element_name];
-        }
-        $element->attr[$attr_name] = $def;
-    }
-
-    /**
-     * Adds a custom element to your HTML definition
-     * @see HTMLPurifier_HTMLModule::addElement() for detailed
-     *       parameter and return value descriptions.
-     */
-    public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array())
-    {
-        $module = $this->getAnonymousModule();
-        // assume that if the user is calling this, the element
-        // is safe. This may not be a good idea
-        $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);
-        return $element;
-    }
-
-    /**
-     * Adds a blank element to your HTML definition, for overriding
-     * existing behavior
-     * @param string $element_name
-     * @return HTMLPurifier_ElementDef
-     * @see HTMLPurifier_HTMLModule::addBlankElement() for detailed
-     *       parameter and return value descriptions.
-     */
-    public function addBlankElement($element_name)
-    {
-        $module  = $this->getAnonymousModule();
-        $element = $module->addBlankElement($element_name);
-        return $element;
-    }
-
-    /**
-     * Retrieves a reference to the anonymous module, so you can
-     * bust out advanced features without having to make your own
-     * module.
-     * @return HTMLPurifier_HTMLModule
-     */
-    public function getAnonymousModule()
-    {
-        if (!$this->_anonModule) {
-            $this->_anonModule = new HTMLPurifier_HTMLModule();
-            $this->_anonModule->name = 'Anonymous';
-        }
-        return $this->_anonModule;
-    }
-
-    private $_anonModule = null;
-
-    // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
-
-    /**
-     * @type string
-     */
-    public $type = 'HTML';
-
-    /**
-     * @type HTMLPurifier_HTMLModuleManager
-     */
-    public $manager;
-
-    /**
-     * Performs low-cost, preliminary initialization.
-     */
-    public function __construct()
-    {
-        $this->manager = new HTMLPurifier_HTMLModuleManager();
-    }
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    protected function doSetup($config)
-    {
-        $this->processModules($config);
-        $this->setupConfigStuff($config);
-        unset($this->manager);
-
-        // cleanup some of the element definitions
-        foreach ($this->info as $k => $v) {
-            unset($this->info[$k]->content_model);
-            unset($this->info[$k]->content_model_type);
-        }
-    }
-
-    /**
-     * Extract out the information from the manager
-     * @param HTMLPurifier_Config $config
-     */
-    protected function processModules($config)
-    {
-        if ($this->_anonModule) {
-            // for user specific changes
-            // this is late-loaded so we don't have to deal with PHP4
-            // reference wonky-ness
-            $this->manager->addModule($this->_anonModule);
-            unset($this->_anonModule);
-        }
-
-        $this->manager->setup($config);
-        $this->doctype = $this->manager->doctype;
-
-        foreach ($this->manager->modules as $module) {
-            foreach ($module->info_tag_transform as $k => $v) {
-                if ($v === false) {
-                    unset($this->info_tag_transform[$k]);
-                } else {
-                    $this->info_tag_transform[$k] = $v;
-                }
-            }
-            foreach ($module->info_attr_transform_pre as $k => $v) {
-                if ($v === false) {
-                    unset($this->info_attr_transform_pre[$k]);
-                } else {
-                    $this->info_attr_transform_pre[$k] = $v;
-                }
-            }
-            foreach ($module->info_attr_transform_post as $k => $v) {
-                if ($v === false) {
-                    unset($this->info_attr_transform_post[$k]);
-                } else {
-                    $this->info_attr_transform_post[$k] = $v;
-                }
-            }
-            foreach ($module->info_injector as $k => $v) {
-                if ($v === false) {
-                    unset($this->info_injector[$k]);
-                } else {
-                    $this->info_injector[$k] = $v;
-                }
-            }
-        }
-        $this->info = $this->manager->getElements();
-        $this->info_content_sets = $this->manager->contentSets->lookup;
-    }
-
-    /**
-     * Sets up stuff based on config. We need a better way of doing this.
-     * @param HTMLPurifier_Config $config
-     */
-    protected function setupConfigStuff($config)
-    {
-        $block_wrapper = $config->get('HTML.BlockWrapper');
-        if (isset($this->info_content_sets['Block'][$block_wrapper])) {
-            $this->info_block_wrapper = $block_wrapper;
-        } else {
-            trigger_error(
-                'Cannot use non-block element as block wrapper',
-                E_USER_ERROR
-            );
-        }
-
-        $parent = $config->get('HTML.Parent');
-        $def = $this->manager->getElement($parent, true);
-        if ($def) {
-            $this->info_parent = $parent;
-            $this->info_parent_def = $def;
-        } else {
-            trigger_error(
-                'Cannot use unrecognized element as parent',
-                E_USER_ERROR
-            );
-            $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
-        }
-
-        // support template text
-        $support = "(for information on implementing this, see the support forums) ";
-
-        // setup allowed elements -----------------------------------------
-
-        $allowed_elements = $config->get('HTML.AllowedElements');
-        $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early
-
-        if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
-            $allowed = $config->get('HTML.Allowed');
-            if (is_string($allowed)) {
-                list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
-            }
-        }
-
-        if (is_array($allowed_elements)) {
-            foreach ($this->info as $name => $d) {
-                if (!isset($allowed_elements[$name])) {
-                    unset($this->info[$name]);
-                }
-                unset($allowed_elements[$name]);
-            }
-            // emit errors
-            foreach ($allowed_elements as $element => $d) {
-                $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful!
-                trigger_error("Element '$element' is not supported $support", E_USER_WARNING);
-            }
-        }
-
-        // setup allowed attributes ---------------------------------------
-
-        $allowed_attributes_mutable = $allowed_attributes; // by copy!
-        if (is_array($allowed_attributes)) {
-            // This actually doesn't do anything, since we went away from
-            // global attributes. It's possible that userland code uses
-            // it, but HTMLModuleManager doesn't!
-            foreach ($this->info_global_attr as $attr => $x) {
-                $keys = array($attr, "*@$attr", "*.$attr");
-                $delete = true;
-                foreach ($keys as $key) {
-                    if ($delete && isset($allowed_attributes[$key])) {
-                        $delete = false;
-                    }
-                    if (isset($allowed_attributes_mutable[$key])) {
-                        unset($allowed_attributes_mutable[$key]);
-                    }
-                }
-                if ($delete) {
-                    unset($this->info_global_attr[$attr]);
-                }
-            }
-
-            foreach ($this->info as $tag => $info) {
-                foreach ($info->attr as $attr => $x) {
-                    $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");
-                    $delete = true;
-                    foreach ($keys as $key) {
-                        if ($delete && isset($allowed_attributes[$key])) {
-                            $delete = false;
-                        }
-                        if (isset($allowed_attributes_mutable[$key])) {
-                            unset($allowed_attributes_mutable[$key]);
-                        }
-                    }
-                    if ($delete) {
-                        if ($this->info[$tag]->attr[$attr]->required) {
-                            trigger_error(
-                                "Required attribute '$attr' in element '$tag' " .
-                                "was not allowed, which means '$tag' will not be allowed either",
-                                E_USER_WARNING
-                            );
-                        }
-                        unset($this->info[$tag]->attr[$attr]);
-                    }
-                }
-            }
-            // emit errors
-            foreach ($allowed_attributes_mutable as $elattr => $d) {
-                $bits = preg_split('/[.@]/', $elattr, 2);
-                $c = count($bits);
-                switch ($c) {
-                    case 2:
-                        if ($bits[0] !== '*') {
-                            $element = htmlspecialchars($bits[0]);
-                            $attribute = htmlspecialchars($bits[1]);
-                            if (!isset($this->info[$element])) {
-                                trigger_error(
-                                    "Cannot allow attribute '$attribute' if element " .
-                                    "'$element' is not allowed/supported $support"
-                                );
-                            } else {
-                                trigger_error(
-                                    "Attribute '$attribute' in element '$element' not supported $support",
-                                    E_USER_WARNING
-                                );
-                            }
-                            break;
-                        }
-                        // otherwise fall through
-                    case 1:
-                        $attribute = htmlspecialchars($bits[0]);
-                        trigger_error(
-                            "Global attribute '$attribute' is not ".
-                            "supported in any elements $support",
-                            E_USER_WARNING
-                        );
-                        break;
-                }
-            }
-        }
-
-        // setup forbidden elements ---------------------------------------
-
-        $forbidden_elements   = $config->get('HTML.ForbiddenElements');
-        $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
-
-        foreach ($this->info as $tag => $info) {
-            if (isset($forbidden_elements[$tag])) {
-                unset($this->info[$tag]);
-                continue;
-            }
-            foreach ($info->attr as $attr => $x) {
-                if (isset($forbidden_attributes["$tag@$attr"]) ||
-                    isset($forbidden_attributes["*@$attr"]) ||
-                    isset($forbidden_attributes[$attr])
-                ) {
-                    unset($this->info[$tag]->attr[$attr]);
-                    continue;
-                } elseif (isset($forbidden_attributes["$tag.$attr"])) { // this segment might get removed eventually
-                    // $tag.$attr are not user supplied, so no worries!
-                    trigger_error(
-                        "Error with $tag.$attr: tag.attr syntax not supported for " .
-                        "HTML.ForbiddenAttributes; use tag@attr instead",
-                        E_USER_WARNING
-                    );
-                }
-            }
-        }
-        foreach ($forbidden_attributes as $key => $v) {
-            if (strlen($key) < 2) {
-                continue;
-            }
-            if ($key[0] != '*') {
-                continue;
-            }
-            if ($key[1] == '.') {
-                trigger_error(
-                    "Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead",
-                    E_USER_WARNING
-                );
-            }
-        }
-
-        // setup injectors -----------------------------------------------------
-        foreach ($this->info_injector as $i => $injector) {
-            if ($injector->checkNeeded($config) !== false) {
-                // remove injector that does not have it's required
-                // elements/attributes present, and is thus not needed.
-                unset($this->info_injector[$i]);
-            }
-        }
-    }
-
-    /**
-     * Parses a TinyMCE-flavored Allowed Elements and Attributes list into
-     * separate lists for processing. Format is element[attr1|attr2],element2...
-     * @warning Although it's largely drawn from TinyMCE's implementation,
-     *      it is different, and you'll probably have to modify your lists
-     * @param array $list String list to parse
-     * @return array
-     * @todo Give this its own class, probably static interface
-     */
-    public function parseTinyMCEAllowedList($list)
-    {
-        $list = str_replace(array(' ', "\t"), '', $list);
-
-        $elements = array();
-        $attributes = array();
-
-        $chunks = preg_split('/(,|[\n\r]+)/', $list);
-        foreach ($chunks as $chunk) {
-            if (empty($chunk)) {
-                continue;
-            }
-            // remove TinyMCE element control characters
-            if (!strpos($chunk, '[')) {
-                $element = $chunk;
-                $attr = false;
-            } else {
-                list($element, $attr) = explode('[', $chunk);
-            }
-            if ($element !== '*') {
-                $elements[$element] = true;
-            }
-            if (!$attr) {
-                continue;
-            }
-            $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ]
-            $attr = explode('|', $attr);
-            foreach ($attr as $key) {
-                $attributes["$element.$key"] = true;
-            }
-        }
-        return array($elements, $attributes);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php
deleted file mode 100644
index 9dbb987..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule.php
+++ /dev/null
@@ -1,285 +0,0 @@
-<?php
-
-/**
- * Represents an XHTML 1.1 module, with information on elements, tags
- * and attributes.
- * @note Even though this is technically XHTML 1.1, it is also used for
- *       regular HTML parsing. We are using modulization as a convenient
- *       way to represent the internals of HTMLDefinition, and our
- *       implementation is by no means conforming and does not directly
- *       use the normative DTDs or XML schemas.
- * @note The public variables in a module should almost directly
- *       correspond to the variables in HTMLPurifier_HTMLDefinition.
- *       However, the prefix info carries no special meaning in these
- *       objects (include it anyway if that's the correspondence though).
- * @todo Consider making some member functions protected
- */
-
-class HTMLPurifier_HTMLModule
-{
-
-    // -- Overloadable ----------------------------------------------------
-
-    /**
-     * Short unique string identifier of the module.
-     * @type string
-     */
-    public $name;
-
-    /**
-     * Informally, a list of elements this module changes.
-     * Not used in any significant way.
-     * @type array
-     */
-    public $elements = array();
-
-    /**
-     * Associative array of element names to element definitions.
-     * Some definitions may be incomplete, to be merged in later
-     * with the full definition.
-     * @type array
-     */
-    public $info = array();
-
-    /**
-     * Associative array of content set names to content set additions.
-     * This is commonly used to, say, add an A element to the Inline
-     * content set. This corresponds to an internal variable $content_sets
-     * and NOT info_content_sets member variable of HTMLDefinition.
-     * @type array
-     */
-    public $content_sets = array();
-
-    /**
-     * Associative array of attribute collection names to attribute
-     * collection additions. More rarely used for adding attributes to
-     * the global collections. Example is the StyleAttribute module adding
-     * the style attribute to the Core. Corresponds to HTMLDefinition's
-     * attr_collections->info, since the object's data is only info,
-     * with extra behavior associated with it.
-     * @type array
-     */
-    public $attr_collections = array();
-
-    /**
-     * Associative array of deprecated tag name to HTMLPurifier_TagTransform.
-     * @type array
-     */
-    public $info_tag_transform = array();
-
-    /**
-     * List of HTMLPurifier_AttrTransform to be performed before validation.
-     * @type array
-     */
-    public $info_attr_transform_pre = array();
-
-    /**
-     * List of HTMLPurifier_AttrTransform to be performed after validation.
-     * @type array
-     */
-    public $info_attr_transform_post = array();
-
-    /**
-     * List of HTMLPurifier_Injector to be performed during well-formedness fixing.
-     * An injector will only be invoked if all of it's pre-requisites are met;
-     * if an injector fails setup, there will be no error; it will simply be
-     * silently disabled.
-     * @type array
-     */
-    public $info_injector = array();
-
-    /**
-     * Boolean flag that indicates whether or not getChildDef is implemented.
-     * For optimization reasons: may save a call to a function. Be sure
-     * to set it if you do implement getChildDef(), otherwise it will have
-     * no effect!
-     * @type bool
-     */
-    public $defines_child_def = false;
-
-    /**
-     * Boolean flag whether or not this module is safe. If it is not safe, all
-     * of its members are unsafe. Modules are safe by default (this might be
-     * slightly dangerous, but it doesn't make much sense to force HTML Purifier,
-     * which is based off of safe HTML, to explicitly say, "This is safe," even
-     * though there are modules which are "unsafe")
-     *
-     * @type bool
-     * @note Previously, safety could be applied at an element level granularity.
-     *       We've removed this ability, so in order to add "unsafe" elements
-     *       or attributes, a dedicated module with this property set to false
-     *       must be used.
-     */
-    public $safe = true;
-
-    /**
-     * Retrieves a proper HTMLPurifier_ChildDef subclass based on
-     * content_model and content_model_type member variables of
-     * the HTMLPurifier_ElementDef class. There is a similar function
-     * in HTMLPurifier_HTMLDefinition.
-     * @param HTMLPurifier_ElementDef $def
-     * @return HTMLPurifier_ChildDef subclass
-     */
-    public function getChildDef($def)
-    {
-        return false;
-    }
-
-    // -- Convenience -----------------------------------------------------
-
-    /**
-     * Convenience function that sets up a new element
-     * @param string $element Name of element to add
-     * @param string|bool $type What content set should element be registered to?
-     *              Set as false to skip this step.
-     * @param string|HTMLPurifier_ChildDef $contents Allowed children in form of:
-     *              "$content_model_type: $content_model"
-     * @param array|string $attr_includes What attribute collections to register to
-     *              element?
-     * @param array $attr What unique attributes does the element define?
-     * @see HTMLPurifier_ElementDef:: for in-depth descriptions of these parameters.
-     * @return HTMLPurifier_ElementDef Created element definition object, so you
-     *         can set advanced parameters
-     */
-    public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array())
-    {
-        $this->elements[] = $element;
-        // parse content_model
-        list($content_model_type, $content_model) = $this->parseContents($contents);
-        // merge in attribute inclusions
-        $this->mergeInAttrIncludes($attr, $attr_includes);
-        // add element to content sets
-        if ($type) {
-            $this->addElementToContentSet($element, $type);
-        }
-        // create element
-        $this->info[$element] = HTMLPurifier_ElementDef::create(
-            $content_model,
-            $content_model_type,
-            $attr
-        );
-        // literal object $contents means direct child manipulation
-        if (!is_string($contents)) {
-            $this->info[$element]->child = $contents;
-        }
-        return $this->info[$element];
-    }
-
-    /**
-     * Convenience function that creates a totally blank, non-standalone
-     * element.
-     * @param string $element Name of element to create
-     * @return HTMLPurifier_ElementDef Created element
-     */
-    public function addBlankElement($element)
-    {
-        if (!isset($this->info[$element])) {
-            $this->elements[] = $element;
-            $this->info[$element] = new HTMLPurifier_ElementDef();
-            $this->info[$element]->standalone = false;
-        } else {
-            trigger_error("Definition for $element already exists in module, cannot redefine");
-        }
-        return $this->info[$element];
-    }
-
-    /**
-     * Convenience function that registers an element to a content set
-     * @param string $element Element to register
-     * @param string $type Name content set (warning: case sensitive, usually upper-case
-     *        first letter)
-     */
-    public function addElementToContentSet($element, $type)
-    {
-        if (!isset($this->content_sets[$type])) {
-            $this->content_sets[$type] = '';
-        } else {
-            $this->content_sets[$type] .= ' | ';
-        }
-        $this->content_sets[$type] .= $element;
-    }
-
-    /**
-     * Convenience function that transforms single-string contents
-     * into separate content model and content model type
-     * @param string $contents Allowed children in form of:
-     *                  "$content_model_type: $content_model"
-     * @return array
-     * @note If contents is an object, an array of two nulls will be
-     *       returned, and the callee needs to take the original $contents
-     *       and use it directly.
-     */
-    public function parseContents($contents)
-    {
-        if (!is_string($contents)) {
-            return array(null, null);
-        } // defer
-        switch ($contents) {
-            // check for shorthand content model forms
-            case 'Empty':
-                return array('empty', '');
-            case 'Inline':
-                return array('optional', 'Inline | #PCDATA');
-            case 'Flow':
-                return array('optional', 'Flow | #PCDATA');
-        }
-        list($content_model_type, $content_model) = explode(':', $contents);
-        $content_model_type = strtolower(trim($content_model_type));
-        $content_model = trim($content_model);
-        return array($content_model_type, $content_model);
-    }
-
-    /**
-     * Convenience function that merges a list of attribute includes into
-     * an attribute array.
-     * @param array $attr Reference to attr array to modify
-     * @param array $attr_includes Array of includes / string include to merge in
-     */
-    public function mergeInAttrIncludes(&$attr, $attr_includes)
-    {
-        if (!is_array($attr_includes)) {
-            if (empty($attr_includes)) {
-                $attr_includes = array();
-            } else {
-                $attr_includes = array($attr_includes);
-            }
-        }
-        $attr[0] = $attr_includes;
-    }
-
-    /**
-     * Convenience function that generates a lookup table with boolean
-     * true as value.
-     * @param string $list List of values to turn into a lookup
-     * @note You can also pass an arbitrary number of arguments in
-     *       place of the regular argument
-     * @return array array equivalent of list
-     */
-    public function makeLookup($list)
-    {
-        $args = func_get_args();
-        if (is_string($list)) {
-            $list = $args;
-        }
-        $ret = array();
-        foreach ($list as $value) {
-            if (is_null($value)) {
-                continue;
-            }
-            $ret[$value] = true;
-        }
-        return $ret;
-    }
-
-    /**
-     * Lazy load construction of the module after determining whether
-     * or not it's needed, and also when a finalized configuration object
-     * is available.
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php
deleted file mode 100644
index 1e67c79..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Bdo.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Bi-directional Text Module, defines elements that
- * declare directionality of content. Text Extension Module.
- */
-class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Bdo';
-
-    /**
-     * @type array
-     */
-    public $attr_collections = array(
-        'I18N' => array('dir' => false)
-    );
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $bdo = $this->addElement(
-            'bdo',
-            'Inline',
-            'Inline',
-            array('Core', 'Lang'),
-            array(
-                'dir' => 'Enum#ltr,rtl', // required
-                // The Abstract Module specification has the attribute
-                // inclusions wrong for bdo: bdo allows Lang
-            )
-        );
-        $bdo->attr_transform_post[] = new HTMLPurifier_AttrTransform_BdoDir();
-
-        $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl';
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php
deleted file mode 100644
index 7220c14..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/CommonAttributes.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'CommonAttributes';
-
-    /**
-     * @type array
-     */
-    public $attr_collections = array(
-        'Core' => array(
-            0 => array('Style'),
-            // 'xml:space' => false,
-            'class' => 'Class',
-            'id' => 'ID',
-            'title' => 'CDATA',
-            'contenteditable' => 'ContentEditable',
-        ),
-        'Lang' => array(),
-        'I18N' => array(
-            0 => array('Lang'), // proprietary, for xml:lang/lang
-        ),
-        'Common' => array(
-            0 => array('Core', 'I18N')
-        )
-    );
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php
deleted file mode 100644
index a9042a3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Edit.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
- * Module.
- */
-class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Edit';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';
-        $attr = array(
-            'cite' => 'URI',
-            // 'datetime' => 'Datetime', // not implemented
-        );
-        $this->addElement('del', 'Inline', $contents, 'Common', $attr);
-        $this->addElement('ins', 'Inline', $contents, 'Common', $attr);
-    }
-
-    // HTML 4.01 specifies that ins/del must not contain block
-    // elements when used in an inline context, chameleon is
-    // a complicated workaround to acheive this effect
-
-    // Inline context ! Block context (exclamation mark is
-    // separator, see getChildDef for parsing)
-
-    /**
-     * @type bool
-     */
-    public $defines_child_def = true;
-
-    /**
-     * @param HTMLPurifier_ElementDef $def
-     * @return HTMLPurifier_ChildDef_Chameleon
-     */
-    public function getChildDef($def)
-    {
-        if ($def->content_model_type != 'chameleon') {
-            return false;
-        }
-        $value = explode('!', $def->content_model);
-        return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php
deleted file mode 100644
index eb0edcf..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Forms.php
+++ /dev/null
@@ -1,194 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Forms module, defines all form-related elements found in HTML 4.
- */
-class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Forms';
-
-    /**
-     * @type bool
-     */
-    public $safe = false;
-
-    /**
-     * @type array
-     */
-    public $content_sets = array(
-        'Block' => 'Form',
-        'Inline' => 'Formctrl',
-    );
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        if ($config->get('HTML.Forms')) {
-            $this->safe = true;
-        }
-
-        $form = $this->addElement(
-            'form',
-            'Form',
-            'Required: Heading | List | Block | fieldset',
-            'Common',
-            array(
-                'accept' => 'ContentTypes',
-                'accept-charset' => 'Charsets',
-                'action*' => 'URI',
-                'method' => 'Enum#get,post',
-                // really ContentType, but these two are the only ones used today
-                'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data',
-            )
-        );
-        $form->excludes = array('form' => true);
-
-        $input = $this->addElement(
-            'input',
-            'Formctrl',
-            'Empty',
-            'Common',
-            array(
-                'accept' => 'ContentTypes',
-                'accesskey' => 'Character',
-                'alt' => 'Text',
-                'checked' => 'Bool#checked',
-                'disabled' => 'Bool#disabled',
-                'maxlength' => 'Number',
-                'name' => 'CDATA',
-                'readonly' => 'Bool#readonly',
-                'size' => 'Number',
-                'src' => 'URI#embedded',
-                'tabindex' => 'Number',
-                'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image',
-                'value' => 'CDATA',
-            )
-        );
-        $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input();
-
-        $this->addElement(
-            'select',
-            'Formctrl',
-            'Required: optgroup | option',
-            'Common',
-            array(
-                'disabled' => 'Bool#disabled',
-                'multiple' => 'Bool#multiple',
-                'name' => 'CDATA',
-                'size' => 'Number',
-                'tabindex' => 'Number',
-            )
-        );
-
-        $this->addElement(
-            'option',
-            false,
-            'Optional: #PCDATA',
-            'Common',
-            array(
-                'disabled' => 'Bool#disabled',
-                'label' => 'Text',
-                'selected' => 'Bool#selected',
-                'value' => 'CDATA',
-            )
-        );
-        // It's illegal for there to be more than one selected, but not
-        // be multiple. Also, no selected means undefined behavior. This might
-        // be difficult to implement; perhaps an injector, or a context variable.
-
-        $textarea = $this->addElement(
-            'textarea',
-            'Formctrl',
-            'Optional: #PCDATA',
-            'Common',
-            array(
-                'accesskey' => 'Character',
-                'cols*' => 'Number',
-                'disabled' => 'Bool#disabled',
-                'name' => 'CDATA',
-                'readonly' => 'Bool#readonly',
-                'rows*' => 'Number',
-                'tabindex' => 'Number',
-            )
-        );
-        $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea();
-
-        $button = $this->addElement(
-            'button',
-            'Formctrl',
-            'Optional: #PCDATA | Heading | List | Block | Inline',
-            'Common',
-            array(
-                'accesskey' => 'Character',
-                'disabled' => 'Bool#disabled',
-                'name' => 'CDATA',
-                'tabindex' => 'Number',
-                'type' => 'Enum#button,submit,reset',
-                'value' => 'CDATA',
-            )
-        );
-
-        // For exclusions, ideally we'd specify content sets, not literal elements
-        $button->excludes = $this->makeLookup(
-            'form',
-            'fieldset', // Form
-            'input',
-            'select',
-            'textarea',
-            'label',
-            'button', // Formctrl
-            'a', // as per HTML 4.01 spec, this is omitted by modularization
-            'isindex',
-            'iframe' // legacy items
-        );
-
-        // Extra exclusion: img usemap="" is not permitted within this element.
-        // We'll omit this for now, since we don't have any good way of
-        // indicating it yet.
-
-        // This is HIGHLY user-unfriendly; we need a custom child-def for this
-        $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common');
-
-        $label = $this->addElement(
-            'label',
-            'Formctrl',
-            'Optional: #PCDATA | Inline',
-            'Common',
-            array(
-                'accesskey' => 'Character',
-                // 'for' => 'IDREF', // IDREF not implemented, cannot allow
-            )
-        );
-        $label->excludes = array('label' => true);
-
-        $this->addElement(
-            'legend',
-            false,
-            'Optional: #PCDATA | Inline',
-            'Common',
-            array(
-                'accesskey' => 'Character',
-            )
-        );
-
-        $this->addElement(
-            'optgroup',
-            false,
-            'Required: option',
-            'Common',
-            array(
-                'disabled' => 'Bool#disabled',
-                'label*' => 'Text',
-            )
-        );
-        // Don't forget an injector for <isindex>. This one's a little complex
-        // because it maps to multiple elements.
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php
deleted file mode 100644
index 72d7a31..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Hypertext.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Hypertext Module, defines hypertext links. Core Module.
- */
-class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Hypertext';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $a = $this->addElement(
-            'a',
-            'Inline',
-            'Inline',
-            'Common',
-            array(
-                // 'accesskey' => 'Character',
-                // 'charset' => 'Charset',
-                'href' => 'URI',
-                // 'hreflang' => 'LanguageCode',
-                'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'),
-                'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'),
-                // 'tabindex' => 'Number',
-                // 'type' => 'ContentType',
-            )
-        );
-        $a->formatting = true;
-        $a->excludes = array('a' => true);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php
deleted file mode 100644
index f7e7c91..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Iframe.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Iframe Module provides inline frames.
- *
- * @note This module is not considered safe unless an Iframe
- * whitelisting mechanism is specified.  Currently, the only
- * such mechanism is %URL.SafeIframeRegexp
- */
-class HTMLPurifier_HTMLModule_Iframe extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Iframe';
-
-    /**
-     * @type bool
-     */
-    public $safe = false;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        if ($config->get('HTML.SafeIframe')) {
-            $this->safe = true;
-        }
-        $this->addElement(
-            'iframe',
-            'Inline',
-            'Flow',
-            'Common',
-            array(
-                'src' => 'URI#embedded',
-                'width' => 'Length',
-                'height' => 'Length',
-                'name' => 'ID',
-                'scrolling' => 'Enum#yes,no,auto',
-                'frameborder' => 'Enum#0,1',
-                'longdesc' => 'URI',
-                'marginheight' => 'Pixels',
-                'marginwidth' => 'Pixels',
-            )
-        );
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php
deleted file mode 100644
index 0f5fdb3..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Image.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Image Module provides basic image embedding.
- * @note There is specialized code for removing empty images in
- *       HTMLPurifier_Strategy_RemoveForeignElements
- */
-class HTMLPurifier_HTMLModule_Image extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Image';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $max = $config->get('HTML.MaxImgLength');
-        $img = $this->addElement(
-            'img',
-            'Inline',
-            'Empty',
-            'Common',
-            array(
-                'alt*' => 'Text',
-                // According to the spec, it's Length, but percents can
-                // be abused, so we allow only Pixels.
-                'height' => 'Pixels#' . $max,
-                'width' => 'Pixels#' . $max,
-                'longdesc' => 'URI',
-                'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded
-            )
-        );
-        if ($max === null || $config->get('HTML.Trusted')) {
-            $img->attr['height'] =
-            $img->attr['width'] = 'Length';
-        }
-
-        // kind of strange, but splitting things up would be inefficient
-        $img->attr_transform_pre[] =
-        $img->attr_transform_post[] =
-            new HTMLPurifier_AttrTransform_ImgRequired();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php
deleted file mode 100644
index 86b5299..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Legacy.php
+++ /dev/null
@@ -1,186 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Legacy module defines elements that were previously
- * deprecated.
- *
- * @note Not all legacy elements have been implemented yet, which
- *       is a bit of a reverse problem as compared to browsers! In
- *       addition, this legacy module may implement a bit more than
- *       mandated by XHTML 1.1.
- *
- * This module can be used in combination with TransformToStrict in order
- * to transform as many deprecated elements as possible, but retain
- * questionably deprecated elements that do not have good alternatives
- * as well as transform elements that don't have an implementation.
- * See docs/ref-strictness.txt for more details.
- */
-
-class HTMLPurifier_HTMLModule_Legacy extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Legacy';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->addElement(
-            'basefont',
-            'Inline',
-            'Empty',
-            null,
-            array(
-                'color' => 'Color',
-                'face' => 'Text', // extremely broad, we should
-                'size' => 'Text', // tighten it
-                'id' => 'ID'
-            )
-        );
-        $this->addElement('center', 'Block', 'Flow', 'Common');
-        $this->addElement(
-            'dir',
-            'Block',
-            'Required: li',
-            'Common',
-            array(
-                'compact' => 'Bool#compact'
-            )
-        );
-        $this->addElement(
-            'font',
-            'Inline',
-            'Inline',
-            array('Core', 'I18N'),
-            array(
-                'color' => 'Color',
-                'face' => 'Text', // extremely broad, we should
-                'size' => 'Text', // tighten it
-            )
-        );
-        $this->addElement(
-            'menu',
-            'Block',
-            'Required: li',
-            'Common',
-            array(
-                'compact' => 'Bool#compact'
-            )
-        );
-
-        $s = $this->addElement('s', 'Inline', 'Inline', 'Common');
-        $s->formatting = true;
-
-        $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common');
-        $strike->formatting = true;
-
-        $u = $this->addElement('u', 'Inline', 'Inline', 'Common');
-        $u->formatting = true;
-
-        // setup modifications to old elements
-
-        $align = 'Enum#left,right,center,justify';
-
-        $address = $this->addBlankElement('address');
-        $address->content_model = 'Inline | #PCDATA | p';
-        $address->content_model_type = 'optional';
-        $address->child = false;
-
-        $blockquote = $this->addBlankElement('blockquote');
-        $blockquote->content_model = 'Flow | #PCDATA';
-        $blockquote->content_model_type = 'optional';
-        $blockquote->child = false;
-
-        $br = $this->addBlankElement('br');
-        $br->attr['clear'] = 'Enum#left,all,right,none';
-
-        $caption = $this->addBlankElement('caption');
-        $caption->attr['align'] = 'Enum#top,bottom,left,right';
-
-        $div = $this->addBlankElement('div');
-        $div->attr['align'] = $align;
-
-        $dl = $this->addBlankElement('dl');
-        $dl->attr['compact'] = 'Bool#compact';
-
-        for ($i = 1; $i <= 6; $i++) {
-            $h = $this->addBlankElement("h$i");
-            $h->attr['align'] = $align;
-        }
-
-        $hr = $this->addBlankElement('hr');
-        $hr->attr['align'] = $align;
-        $hr->attr['noshade'] = 'Bool#noshade';
-        $hr->attr['size'] = 'Pixels';
-        $hr->attr['width'] = 'Length';
-
-        $img = $this->addBlankElement('img');
-        $img->attr['align'] = 'IAlign';
-        $img->attr['border'] = 'Pixels';
-        $img->attr['hspace'] = 'Pixels';
-        $img->attr['vspace'] = 'Pixels';
-
-        // figure out this integer business
-
-        $li = $this->addBlankElement('li');
-        $li->attr['value'] = new HTMLPurifier_AttrDef_Integer();
-        $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle';
-
-        $ol = $this->addBlankElement('ol');
-        $ol->attr['compact'] = 'Bool#compact';
-        $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer();
-        $ol->attr['type'] = 'Enum#s:1,i,I,a,A';
-
-        $p = $this->addBlankElement('p');
-        $p->attr['align'] = $align;
-
-        $pre = $this->addBlankElement('pre');
-        $pre->attr['width'] = 'Number';
-
-        // script omitted
-
-        $table = $this->addBlankElement('table');
-        $table->attr['align'] = 'Enum#left,center,right';
-        $table->attr['bgcolor'] = 'Color';
-
-        $tr = $this->addBlankElement('tr');
-        $tr->attr['bgcolor'] = 'Color';
-
-        $th = $this->addBlankElement('th');
-        $th->attr['bgcolor'] = 'Color';
-        $th->attr['height'] = 'Length';
-        $th->attr['nowrap'] = 'Bool#nowrap';
-        $th->attr['width'] = 'Length';
-
-        $td = $this->addBlankElement('td');
-        $td->attr['bgcolor'] = 'Color';
-        $td->attr['height'] = 'Length';
-        $td->attr['nowrap'] = 'Bool#nowrap';
-        $td->attr['width'] = 'Length';
-
-        $ul = $this->addBlankElement('ul');
-        $ul->attr['compact'] = 'Bool#compact';
-        $ul->attr['type'] = 'Enum#square,disc,circle';
-
-        // "safe" modifications to "unsafe" elements
-        // WARNING: If you want to add support for an unsafe, legacy
-        // attribute, make a new TrustedLegacy module with the trusted
-        // bit set appropriately
-
-        $form = $this->addBlankElement('form');
-        $form->content_model = 'Flow | #PCDATA';
-        $form->content_model_type = 'optional';
-        $form->attr['target'] = 'FrameTarget';
-
-        $input = $this->addBlankElement('input');
-        $input->attr['align'] = 'IAlign';
-
-        $legend = $this->addBlankElement('legend');
-        $legend->attr['align'] = 'LAlign';
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php
deleted file mode 100644
index 7a20ff7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/List.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 List Module, defines list-oriented elements. Core Module.
- */
-class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'List';
-
-    // According to the abstract schema, the List content set is a fully formed
-    // one or more expr, but it invariably occurs in an optional declaration
-    // so we're not going to do that subtlety. It might cause trouble
-    // if a user defines "List" and expects that multiple lists are
-    // allowed to be specified, but then again, that's not very intuitive.
-    // Furthermore, the actual XML Schema may disagree. Regardless,
-    // we don't have support for such nested expressions without using
-    // the incredibly inefficient and draconic Custom ChildDef.
-
-    /**
-     * @type array
-     */
-    public $content_sets = array('Flow' => 'List');
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $ol = $this->addElement('ol', 'List', new HTMLPurifier_ChildDef_List(), 'Common');
-        $ul = $this->addElement('ul', 'List', new HTMLPurifier_ChildDef_List(), 'Common');
-        // XXX The wrap attribute is handled by MakeWellFormed.  This is all
-        // quite unsatisfactory, because we generated this
-        // *specifically* for lists, and now a big chunk of the handling
-        // is done properly by the List ChildDef.  So actually, we just
-        // want enough information to make autoclosing work properly,
-        // and then hand off the tricky stuff to the ChildDef.
-        $ol->wrap = 'li';
-        $ul->wrap = 'li';
-        $this->addElement('dl', 'List', 'Required: dt | dd', 'Common');
-
-        $this->addElement('li', false, 'Flow', 'Common');
-
-        $this->addElement('dd', false, 'Flow', 'Common');
-        $this->addElement('dt', false, 'Inline', 'Common');
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php
deleted file mode 100644
index 60c0545..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Name.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Name extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Name';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map');
-        foreach ($elements as $name) {
-            $element = $this->addBlankElement($name);
-            $element->attr['name'] = 'CDATA';
-            if (!$config->get('HTML.Attr.Name.UseCDATA')) {
-                $element->attr_transform_post[] = new HTMLPurifier_AttrTransform_NameSync();
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php
deleted file mode 100644
index dc9410a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Nofollow.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/**
- * Module adds the nofollow attribute transformation to a tags.  It
- * is enabled by HTML.Nofollow
- */
-class HTMLPurifier_HTMLModule_Nofollow extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Nofollow';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $a = $this->addBlankElement('a');
-        $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_Nofollow();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php
deleted file mode 100644
index da72225..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_NonXMLCommonAttributes extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'NonXMLCommonAttributes';
-
-    /**
-     * @type array
-     */
-    public $attr_collections = array(
-        'Lang' => array(
-            'lang' => 'LanguageCode',
-        )
-    );
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php
deleted file mode 100644
index 2f9efc5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Object.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Object Module, defines elements for generic object inclusion
- * @warning Users will commonly use <embed> to cater to legacy browsers: this
- *      module does not allow this sort of behavior
- */
-class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Object';
-
-    /**
-     * @type bool
-     */
-    public $safe = false;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->addElement(
-            'object',
-            'Inline',
-            'Optional: #PCDATA | Flow | param',
-            'Common',
-            array(
-                'archive' => 'URI',
-                'classid' => 'URI',
-                'codebase' => 'URI',
-                'codetype' => 'Text',
-                'data' => 'URI',
-                'declare' => 'Bool#declare',
-                'height' => 'Length',
-                'name' => 'CDATA',
-                'standby' => 'Text',
-                'tabindex' => 'Number',
-                'type' => 'ContentType',
-                'width' => 'Length'
-            )
-        );
-
-        $this->addElement(
-            'param',
-            false,
-            'Empty',
-            null,
-            array(
-                'id' => 'ID',
-                'name*' => 'Text',
-                'type' => 'Text',
-                'value' => 'Text',
-                'valuetype' => 'Enum#data,ref,object'
-            )
-        );
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php
deleted file mode 100644
index 6458ce9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Presentation.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Presentation Module, defines simple presentation-related
- * markup. Text Extension Module.
- * @note The official XML Schema and DTD specs further divide this into
- *       two modules:
- *          - Block Presentation (hr)
- *          - Inline Presentation (b, big, i, small, sub, sup, tt)
- *       We have chosen not to heed this distinction, as content_sets
- *       provides satisfactory disambiguation.
- */
-class HTMLPurifier_HTMLModule_Presentation extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Presentation';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->addElement('hr', 'Block', 'Empty', 'Common');
-        $this->addElement('sub', 'Inline', 'Inline', 'Common');
-        $this->addElement('sup', 'Inline', 'Inline', 'Common');
-        $b = $this->addElement('b', 'Inline', 'Inline', 'Common');
-        $b->formatting = true;
-        $big = $this->addElement('big', 'Inline', 'Inline', 'Common');
-        $big->formatting = true;
-        $i = $this->addElement('i', 'Inline', 'Inline', 'Common');
-        $i->formatting = true;
-        $small = $this->addElement('small', 'Inline', 'Inline', 'Common');
-        $small->formatting = true;
-        $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common');
-        $tt->formatting = true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php
deleted file mode 100644
index 5ee3c8e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Proprietary.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * Module defines proprietary tags and attributes in HTML.
- * @warning If this module is enabled, standards-compliance is off!
- */
-class HTMLPurifier_HTMLModule_Proprietary extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Proprietary';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->addElement(
-            'marquee',
-            'Inline',
-            'Flow',
-            'Common',
-            array(
-                'direction' => 'Enum#left,right,up,down',
-                'behavior' => 'Enum#alternate',
-                'width' => 'Length',
-                'height' => 'Length',
-                'scrolldelay' => 'Number',
-                'scrollamount' => 'Number',
-                'loop' => 'Number',
-                'bgcolor' => 'Color',
-                'hspace' => 'Pixels',
-                'vspace' => 'Pixels',
-            )
-        );
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php
deleted file mode 100644
index a0d4892..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Ruby.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Ruby Annotation Module, defines elements that indicate
- * short runs of text alongside base text for annotation or pronounciation.
- */
-class HTMLPurifier_HTMLModule_Ruby extends HTMLPurifier_HTMLModule
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Ruby';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->addElement(
-            'ruby',
-            'Inline',
-            'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))',
-            'Common'
-        );
-        $this->addElement('rbc', false, 'Required: rb', 'Common');
-        $this->addElement('rtc', false, 'Required: rt', 'Common');
-        $rb = $this->addElement('rb', false, 'Inline', 'Common');
-        $rb->excludes = array('ruby' => true);
-        $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number'));
-        $rt->excludes = array('ruby' => true);
-        $this->addElement('rp', false, 'Optional: #PCDATA', 'Common');
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php
deleted file mode 100644
index 04e6689..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeEmbed.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * A "safe" embed module. See SafeObject. This is a proprietary element.
- */
-class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'SafeEmbed';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $max = $config->get('HTML.MaxImgLength');
-        $embed = $this->addElement(
-            'embed',
-            'Inline',
-            'Empty',
-            'Common',
-            array(
-                'src*' => 'URI#embedded',
-                'type' => 'Enum#application/x-shockwave-flash',
-                'width' => 'Pixels#' . $max,
-                'height' => 'Pixels#' . $max,
-                'allowscriptaccess' => 'Enum#never',
-                'allownetworking' => 'Enum#internal',
-                'flashvars' => 'Text',
-                'wmode' => 'Enum#window,transparent,opaque',
-                'name' => 'ID',
-            )
-        );
-        $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php
deleted file mode 100644
index 1297f80..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeObject.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * A "safe" object module. In theory, objects permitted by this module will
- * be safe, and untrusted users can be allowed to embed arbitrary flash objects
- * (maybe other types too, but only Flash is supported as of right now).
- * Highly experimental.
- */
-class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'SafeObject';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        // These definitions are not intrinsically safe: the attribute transforms
-        // are a vital part of ensuring safety.
-
-        $max = $config->get('HTML.MaxImgLength');
-        $object = $this->addElement(
-            'object',
-            'Inline',
-            'Optional: param | Flow | #PCDATA',
-            'Common',
-            array(
-                // While technically not required by the spec, we're forcing
-                // it to this value.
-                'type' => 'Enum#application/x-shockwave-flash',
-                'width' => 'Pixels#' . $max,
-                'height' => 'Pixels#' . $max,
-                'data' => 'URI#embedded',
-                'codebase' => new HTMLPurifier_AttrDef_Enum(
-                    array(
-                        'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'
-                    )
-                ),
-            )
-        );
-        $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject();
-
-        $param = $this->addElement(
-            'param',
-            false,
-            'Empty',
-            false,
-            array(
-                'id' => 'ID',
-                'name*' => 'Text',
-                'value' => 'Text'
-            )
-        );
-        $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam();
-        $this->info_injector[] = 'SafeObject';
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php
deleted file mode 100644
index aea7584..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/SafeScripting.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * A "safe" script module. No inline JS is allowed, and pointed to JS
- * files must match whitelist.
- */
-class HTMLPurifier_HTMLModule_SafeScripting extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'SafeScripting';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        // These definitions are not intrinsically safe: the attribute transforms
-        // are a vital part of ensuring safety.
-
-        $allowed = $config->get('HTML.SafeScripting');
-        $script = $this->addElement(
-            'script',
-            'Inline',
-            'Optional:', // Not `Empty` to not allow to autoclose the <script /> tag @see https://www.w3.org/TR/html4/interact/scripts.html
-            null,
-            array(
-                // While technically not required by the spec, we're forcing
-                // it to this value.
-                'type' => 'Enum#text/javascript',
-                'src*' => new HTMLPurifier_AttrDef_Enum(array_keys($allowed), /*case sensitive*/ true)
-            )
-        );
-        $script->attr_transform_pre[] =
-        $script->attr_transform_post[] = new HTMLPurifier_AttrTransform_ScriptRequired();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php
deleted file mode 100644
index 8b28a7b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Scripting.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/*
-
-WARNING: THIS MODULE IS EXTREMELY DANGEROUS AS IT ENABLES INLINE SCRIPTING
-INSIDE HTML PURIFIER DOCUMENTS. USE ONLY WITH TRUSTED USER INPUT!!!
-
-*/
-
-/**
- * XHTML 1.1 Scripting module, defines elements that are used to contain
- * information pertaining to executable scripts or the lack of support
- * for executable scripts.
- * @note This module does not contain inline scripting elements
- */
-class HTMLPurifier_HTMLModule_Scripting extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Scripting';
-
-    /**
-     * @type array
-     */
-    public $elements = array('script', 'noscript');
-
-    /**
-     * @type array
-     */
-    public $content_sets = array('Block' => 'script | noscript', 'Inline' => 'script | noscript');
-
-    /**
-     * @type bool
-     */
-    public $safe = false;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        // TODO: create custom child-definition for noscript that
-        // auto-wraps stray #PCDATA in a similar manner to
-        // blockquote's custom definition (we would use it but
-        // blockquote's contents are optional while noscript's contents
-        // are required)
-
-        // TODO: convert this to new syntax, main problem is getting
-        // both content sets working
-
-        // In theory, this could be safe, but I don't see any reason to
-        // allow it.
-        $this->info['noscript'] = new HTMLPurifier_ElementDef();
-        $this->info['noscript']->attr = array(0 => array('Common'));
-        $this->info['noscript']->content_model = 'Heading | List | Block';
-        $this->info['noscript']->content_model_type = 'required';
-
-        $this->info['script'] = new HTMLPurifier_ElementDef();
-        $this->info['script']->attr = array(
-            'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')),
-            'src' => new HTMLPurifier_AttrDef_URI(true),
-            'type' => new HTMLPurifier_AttrDef_Enum(array('text/javascript'))
-        );
-        $this->info['script']->content_model = '#PCDATA';
-        $this->info['script']->content_model_type = 'optional';
-        $this->info['script']->attr_transform_pre[] =
-        $this->info['script']->attr_transform_post[] =
-            new HTMLPurifier_AttrTransform_ScriptRequired();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php
deleted file mode 100644
index 497b832..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/StyleAttribute.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Edit Module, defines editing-related elements. Text Extension
- * Module.
- */
-class HTMLPurifier_HTMLModule_StyleAttribute extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'StyleAttribute';
-
-    /**
-     * @type array
-     */
-    public $attr_collections = array(
-        // The inclusion routine differs from the Abstract Modules but
-        // is in line with the DTD and XML Schemas.
-        'Style' => array('style' => false), // see constructor
-        'Core' => array(0 => array('Style'))
-    );
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php
deleted file mode 100644
index 8a0b3b4..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Tables Module, fully defines accessible table elements.
- */
-class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Tables';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->addElement('caption', false, 'Inline', 'Common');
-
-        $this->addElement(
-            'table',
-            'Block',
-            new HTMLPurifier_ChildDef_Table(),
-            'Common',
-            array(
-                'border' => 'Pixels',
-                'cellpadding' => 'Length',
-                'cellspacing' => 'Length',
-                'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border',
-                'rules' => 'Enum#none,groups,rows,cols,all',
-                'summary' => 'Text',
-                'width' => 'Length'
-            )
-        );
-
-        // common attributes
-        $cell_align = array(
-            'align' => 'Enum#left,center,right,justify,char',
-            'charoff' => 'Length',
-            'valign' => 'Enum#top,middle,bottom,baseline',
-        );
-
-        $cell_t = array_merge(
-            array(
-                'abbr' => 'Text',
-                'colspan' => 'Number',
-                'rowspan' => 'Number',
-                // Apparently, as of HTML5 this attribute only applies
-                // to 'th' elements.
-                'scope' => 'Enum#row,col,rowgroup,colgroup',
-            ),
-            $cell_align
-        );
-        $this->addElement('td', false, 'Flow', 'Common', $cell_t);
-        $this->addElement('th', false, 'Flow', 'Common', $cell_t);
-
-        $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align);
-
-        $cell_col = array_merge(
-            array(
-                'span' => 'Number',
-                'width' => 'MultiLength',
-            ),
-            $cell_align
-        );
-        $this->addElement('col', false, 'Empty', 'Common', $cell_col);
-        $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col);
-
-        $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align);
-        $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align);
-        $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php
deleted file mode 100644
index b188ac9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Target Module, defines target attribute in link elements.
- */
-class HTMLPurifier_HTMLModule_Target extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Target';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $elements = array('a');
-        foreach ($elements as $name) {
-            $e = $this->addBlankElement($name);
-            $e->attr = array(
-                'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget()
-            );
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php
deleted file mode 100644
index 58ccc68..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * Module adds the target=blank attribute transformation to a tags.  It
- * is enabled by HTML.TargetBlank
- */
-class HTMLPurifier_HTMLModule_TargetBlank extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'TargetBlank';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $a = $this->addBlankElement('a');
-        $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetBlank();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php
deleted file mode 100644
index b967ff5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * Module adds the target-based noopener attribute transformation to a tags.  It
- * is enabled by HTML.TargetNoopener
- */
-class HTMLPurifier_HTMLModule_TargetNoopener extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'TargetNoopener';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config) {
-        $a = $this->addBlankElement('a');
-        $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoopener();
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php
deleted file mode 100644
index 32484d6..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * Module adds the target-based noreferrer attribute transformation to a tags.  It
- * is enabled by HTML.TargetNoreferrer
- */
-class HTMLPurifier_HTMLModule_TargetNoreferrer extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'TargetNoreferrer';
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config) {
-        $a = $this->addBlankElement('a');
-        $a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoreferrer();
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php
deleted file mode 100644
index 7a65e00..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-/**
- * XHTML 1.1 Text Module, defines basic text containers. Core Module.
- * @note In the normative XML Schema specification, this module
- *       is further abstracted into the following modules:
- *          - Block Phrasal (address, blockquote, pre, h1, h2, h3, h4, h5, h6)
- *          - Block Structural (div, p)
- *          - Inline Phrasal (abbr, acronym, cite, code, dfn, em, kbd, q, samp, strong, var)
- *          - Inline Structural (br, span)
- *       This module, functionally, does not distinguish between these
- *       sub-modules, but the code is internally structured to reflect
- *       these distinctions.
- */
-class HTMLPurifier_HTMLModule_Text extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'Text';
-
-    /**
-     * @type array
-     */
-    public $content_sets = array(
-        'Flow' => 'Heading | Block | Inline'
-    );
-
-    /**
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        // Inline Phrasal -------------------------------------------------
-        $this->addElement('abbr', 'Inline', 'Inline', 'Common');
-        $this->addElement('acronym', 'Inline', 'Inline', 'Common');
-        $this->addElement('cite', 'Inline', 'Inline', 'Common');
-        $this->addElement('dfn', 'Inline', 'Inline', 'Common');
-        $this->addElement('kbd', 'Inline', 'Inline', 'Common');
-        $this->addElement('q', 'Inline', 'Inline', 'Common', array('cite' => 'URI'));
-        $this->addElement('samp', 'Inline', 'Inline', 'Common');
-        $this->addElement('var', 'Inline', 'Inline', 'Common');
-
-        $em = $this->addElement('em', 'Inline', 'Inline', 'Common');
-        $em->formatting = true;
-
-        $strong = $this->addElement('strong', 'Inline', 'Inline', 'Common');
-        $strong->formatting = true;
-
-        $code = $this->addElement('code', 'Inline', 'Inline', 'Common');
-        $code->formatting = true;
-
-        // Inline Structural ----------------------------------------------
-        $this->addElement('span', 'Inline', 'Inline', 'Common');
-        $this->addElement('br', 'Inline', 'Empty', 'Core');
-
-        // Block Phrasal --------------------------------------------------
-        $this->addElement('address', 'Block', 'Inline', 'Common');
-        $this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI'));
-        $pre = $this->addElement('pre', 'Block', 'Inline', 'Common');
-        $pre->excludes = $this->makeLookup(
-            'img',
-            'big',
-            'small',
-            'object',
-            'applet',
-            'font',
-            'basefont'
-        );
-        $this->addElement('h1', 'Heading', 'Inline', 'Common');
-        $this->addElement('h2', 'Heading', 'Inline', 'Common');
-        $this->addElement('h3', 'Heading', 'Inline', 'Common');
-        $this->addElement('h4', 'Heading', 'Inline', 'Common');
-        $this->addElement('h5', 'Heading', 'Inline', 'Common');
-        $this->addElement('h6', 'Heading', 'Inline', 'Common');
-
-        // Block Structural -----------------------------------------------
-        $p = $this->addElement('p', 'Block', 'Inline', 'Common');
-        $p->autoclose = array_flip(
-            array("address", "blockquote", "center", "dir", "div", "dl", "fieldset", "ol", "p", "ul")
-        );
-
-        $this->addElement('div', 'Block', 'Flow', 'Common');
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php
deleted file mode 100644
index 12173ba..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php
+++ /dev/null
@@ -1,227 +0,0 @@
-<?php
-
-/**
- * Abstract class for a set of proprietary modules that clean up (tidy)
- * poorly written HTML.
- * @todo Figure out how to protect some of these methods/properties
- */
-class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule
-{
-    /**
-     * List of supported levels.
-     * Index zero is a special case "no fixes" level.
-     * @type array
-     */
-    public $levels = array(0 => 'none', 'light', 'medium', 'heavy');
-
-    /**
-     * Default level to place all fixes in.
-     * Disabled by default.
-     * @type string
-     */
-    public $defaultLevel = null;
-
-    /**
-     * Lists of fixes used by getFixesForLevel().
-     * Format is:
-     *      HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2');
-     * @type array
-     */
-    public $fixesForLevel = array(
-        'light' => array(),
-        'medium' => array(),
-        'heavy' => array()
-    );
-
-    /**
-     * Lazy load constructs the module by determining the necessary
-     * fixes to create and then delegating to the populate() function.
-     * @param HTMLPurifier_Config $config
-     * @todo Wildcard matching and error reporting when an added or
-     *       subtracted fix has no effect.
-     */
-    public function setup($config)
-    {
-        // create fixes, initialize fixesForLevel
-        $fixes = $this->makeFixes();
-        $this->makeFixesForLevel($fixes);
-
-        // figure out which fixes to use
-        $level = $config->get('HTML.TidyLevel');
-        $fixes_lookup = $this->getFixesForLevel($level);
-
-        // get custom fix declarations: these need namespace processing
-        $add_fixes = $config->get('HTML.TidyAdd');
-        $remove_fixes = $config->get('HTML.TidyRemove');
-
-        foreach ($fixes as $name => $fix) {
-            // needs to be refactored a little to implement globbing
-            if (isset($remove_fixes[$name]) ||
-                (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name]))) {
-                unset($fixes[$name]);
-            }
-        }
-
-        // populate this module with necessary fixes
-        $this->populate($fixes);
-    }
-
-    /**
-     * Retrieves all fixes per a level, returning fixes for that specific
-     * level as well as all levels below it.
-     * @param string $level level identifier, see $levels for valid values
-     * @return array Lookup up table of fixes
-     */
-    public function getFixesForLevel($level)
-    {
-        if ($level == $this->levels[0]) {
-            return array();
-        }
-        $activated_levels = array();
-        for ($i = 1, $c = count($this->levels); $i < $c; $i++) {
-            $activated_levels[] = $this->levels[$i];
-            if ($this->levels[$i] == $level) {
-                break;
-            }
-        }
-        if ($i == $c) {
-            trigger_error(
-                'Tidy level ' . htmlspecialchars($level) . ' not recognized',
-                E_USER_WARNING
-            );
-            return array();
-        }
-        $ret = array();
-        foreach ($activated_levels as $level) {
-            foreach ($this->fixesForLevel[$level] as $fix) {
-                $ret[$fix] = true;
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * Dynamically populates the $fixesForLevel member variable using
-     * the fixes array. It may be custom overloaded, used in conjunction
-     * with $defaultLevel, or not used at all.
-     * @param array $fixes
-     */
-    public function makeFixesForLevel($fixes)
-    {
-        if (!isset($this->defaultLevel)) {
-            return;
-        }
-        if (!isset($this->fixesForLevel[$this->defaultLevel])) {
-            trigger_error(
-                'Default level ' . $this->defaultLevel . ' does not exist',
-                E_USER_ERROR
-            );
-            return;
-        }
-        $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes);
-    }
-
-    /**
-     * Populates the module with transforms and other special-case code
-     * based on a list of fixes passed to it
-     * @param array $fixes Lookup table of fixes to activate
-     */
-    public function populate($fixes)
-    {
-        foreach ($fixes as $name => $fix) {
-            // determine what the fix is for
-            list($type, $params) = $this->getFixType($name);
-            switch ($type) {
-                case 'attr_transform_pre':
-                case 'attr_transform_post':
-                    $attr = $params['attr'];
-                    if (isset($params['element'])) {
-                        $element = $params['element'];
-                        if (empty($this->info[$element])) {
-                            $e = $this->addBlankElement($element);
-                        } else {
-                            $e = $this->info[$element];
-                        }
-                    } else {
-                        $type = "info_$type";
-                        $e = $this;
-                    }
-                    $e->{$type}[$attr] = $fix;
-                    break;
-                case 'tag_transform':
-                    $this->info_tag_transform[$params['element']] = $fix;
-                    break;
-                case 'child':
-                case 'content_model_type':
-                    $element = $params['element'];
-                    if (empty($this->info[$element])) {
-                        $e = $this->addBlankElement($element);
-                    } else {
-                        $e = $this->info[$element];
-                    }
-                    $e->$type = $fix;
-                    break;
-                default:
-                    trigger_error("Fix type $type not supported", E_USER_ERROR);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Parses a fix name and determines what kind of fix it is, as well
-     * as other information defined by the fix
-     * @param $name String name of fix
-     * @return array(string $fix_type, array $fix_parameters)
-     * @note $fix_parameters is type dependant, see populate() for usage
-     *       of these parameters
-     */
-    public function getFixType($name)
-    {
-        // parse it
-        $property = $attr = null;
-        if (strpos($name, '#') !== false) {
-            list($name, $property) = explode('#', $name);
-        }
-        if (strpos($name, '@') !== false) {
-            list($name, $attr) = explode('@', $name);
-        }
-
-        // figure out the parameters
-        $params = array();
-        if ($name !== '') {
-            $params['element'] = $name;
-        }
-        if (!is_null($attr)) {
-            $params['attr'] = $attr;
-        }
-
-        // special case: attribute transform
-        if (!is_null($attr)) {
-            if (is_null($property)) {
-                $property = 'pre';
-            }
-            $type = 'attr_transform_' . $property;
-            return array($type, $params);
-        }
-
-        // special case: tag transform
-        if (is_null($property)) {
-            return array('tag_transform', $params);
-        }
-
-        return array($property, $params);
-
-    }
-
-    /**
-     * Defines all fixes the module will perform in a compact
-     * associative array of fix name to fix implementation.
-     * @return array
-     */
-    public function makeFixes()
-    {
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php
deleted file mode 100644
index a995161..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * Name is deprecated, but allowed in strict doctypes, so onl
- */
-class HTMLPurifier_HTMLModule_Tidy_Name extends HTMLPurifier_HTMLModule_Tidy
-{
-    /**
-     * @type string
-     */
-    public $name = 'Tidy_Name';
-
-    /**
-     * @type string
-     */
-    public $defaultLevel = 'heavy';
-
-    /**
-     * @return array
-     */
-    public function makeFixes()
-    {
-        $r = array();
-        // @name for img, a -----------------------------------------------
-        // Technically, it's allowed even on strict, so we allow authors to use
-        // it. However, it's deprecated in future versions of XHTML.
-        $r['img@name'] =
-        $r['a@name'] = new HTMLPurifier_AttrTransform_Name();
-        return $r;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php
deleted file mode 100644
index 3326438..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Proprietary.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_Tidy
-{
-
-    /**
-     * @type string
-     */
-    public $name = 'Tidy_Proprietary';
-
-    /**
-     * @type string
-     */
-    public $defaultLevel = 'light';
-
-    /**
-     * @return array
-     */
-    public function makeFixes()
-    {
-        $r = array();
-        $r['table@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['td@background']    = new HTMLPurifier_AttrTransform_Background();
-        $r['th@background']    = new HTMLPurifier_AttrTransform_Background();
-        $r['tr@background']    = new HTMLPurifier_AttrTransform_Background();
-        $r['thead@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['tfoot@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['tbody@background'] = new HTMLPurifier_AttrTransform_Background();
-        $r['table@height']     = new HTMLPurifier_AttrTransform_Length('height');
-        return $r;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php
deleted file mode 100644
index 803c44f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Strict.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_Strict extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
-{
-    /**
-     * @type string
-     */
-    public $name = 'Tidy_Strict';
-
-    /**
-     * @type string
-     */
-    public $defaultLevel = 'light';
-
-    /**
-     * @return array
-     */
-    public function makeFixes()
-    {
-        $r = parent::makeFixes();
-        $r['blockquote#content_model_type'] = 'strictblockquote';
-        return $r;
-    }
-
-    /**
-     * @type bool
-     */
-    public $defines_child_def = true;
-
-    /**
-     * @param HTMLPurifier_ElementDef $def
-     * @return HTMLPurifier_ChildDef_StrictBlockquote
-     */
-    public function getChildDef($def)
-    {
-        if ($def->content_model_type != 'strictblockquote') {
-            return parent::getChildDef($def);
-        }
-        return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php
deleted file mode 100644
index c095ad9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Transitional.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_Transitional extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
-{
-    /**
-     * @type string
-     */
-    public $name = 'Tidy_Transitional';
-
-    /**
-     * @type string
-     */
-    public $defaultLevel = 'heavy';
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php
deleted file mode 100644
index 3ecddc4..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTML.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_XHTML extends HTMLPurifier_HTMLModule_Tidy
-{
-    /**
-     * @type string
-     */
-    public $name = 'Tidy_XHTML';
-
-    /**
-     * @type string
-     */
-    public $defaultLevel = 'medium';
-
-    /**
-     * @return array
-     */
-    public function makeFixes()
-    {
-        $r = array();
-        $r['@lang'] = new HTMLPurifier_AttrTransform_Lang();
-        return $r;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php
deleted file mode 100644
index 9ee3ffc..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php
+++ /dev/null
@@ -1,182 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4 extends HTMLPurifier_HTMLModule_Tidy
-{
-
-    /**
-     * @return array
-     */
-    public function makeFixes()
-    {
-        $r = array();
-
-        // == deprecated tag transforms ===================================
-
-        $r['font'] = new HTMLPurifier_TagTransform_Font();
-        $r['menu'] = new HTMLPurifier_TagTransform_Simple('ul');
-        $r['dir'] = new HTMLPurifier_TagTransform_Simple('ul');
-        $r['center'] = new HTMLPurifier_TagTransform_Simple('div', 'text-align:center;');
-        $r['u'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:underline;');
-        $r['s'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
-        $r['strike'] = new HTMLPurifier_TagTransform_Simple('span', 'text-decoration:line-through;');
-
-        // == deprecated attribute transforms =============================
-
-        $r['caption@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS(
-                'align',
-                array(
-                    // we're following IE's behavior, not Firefox's, due
-                    // to the fact that no one supports caption-side:right,
-                    // W3C included (with CSS 2.1). This is a slightly
-                    // unreasonable attribute!
-                    'left' => 'text-align:left;',
-                    'right' => 'text-align:right;',
-                    'top' => 'caption-side:top;',
-                    'bottom' => 'caption-side:bottom;' // not supported by IE
-                )
-            );
-
-        // @align for img -------------------------------------------------
-        $r['img@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS(
-                'align',
-                array(
-                    'left' => 'float:left;',
-                    'right' => 'float:right;',
-                    'top' => 'vertical-align:top;',
-                    'middle' => 'vertical-align:middle;',
-                    'bottom' => 'vertical-align:baseline;',
-                )
-            );
-
-        // @align for table -----------------------------------------------
-        $r['table@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS(
-                'align',
-                array(
-                    'left' => 'float:left;',
-                    'center' => 'margin-left:auto;margin-right:auto;',
-                    'right' => 'float:right;'
-                )
-            );
-
-        // @align for hr -----------------------------------------------
-        $r['hr@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS(
-                'align',
-                array(
-                    // we use both text-align and margin because these work
-                    // for different browsers (IE and Firefox, respectively)
-                    // and the melange makes for a pretty cross-compatible
-                    // solution
-                    'left' => 'margin-left:0;margin-right:auto;text-align:left;',
-                    'center' => 'margin-left:auto;margin-right:auto;text-align:center;',
-                    'right' => 'margin-left:auto;margin-right:0;text-align:right;'
-                )
-            );
-
-        // @align for h1, h2, h3, h4, h5, h6, p, div ----------------------
-        // {{{
-        $align_lookup = array();
-        $align_values = array('left', 'right', 'center', 'justify');
-        foreach ($align_values as $v) {
-            $align_lookup[$v] = "text-align:$v;";
-        }
-        // }}}
-        $r['h1@align'] =
-        $r['h2@align'] =
-        $r['h3@align'] =
-        $r['h4@align'] =
-        $r['h5@align'] =
-        $r['h6@align'] =
-        $r['p@align'] =
-        $r['div@align'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup);
-
-        // @bgcolor for table, tr, td, th ---------------------------------
-        $r['table@bgcolor'] =
-        $r['tr@bgcolor'] =
-        $r['td@bgcolor'] =
-        $r['th@bgcolor'] =
-            new HTMLPurifier_AttrTransform_BgColor();
-
-        // @border for img ------------------------------------------------
-        $r['img@border'] = new HTMLPurifier_AttrTransform_Border();
-
-        // @clear for br --------------------------------------------------
-        $r['br@clear'] =
-            new HTMLPurifier_AttrTransform_EnumToCSS(
-                'clear',
-                array(
-                    'left' => 'clear:left;',
-                    'right' => 'clear:right;',
-                    'all' => 'clear:both;',
-                    'none' => 'clear:none;',
-                )
-            );
-
-        // @height for td, th ---------------------------------------------
-        $r['td@height'] =
-        $r['th@height'] =
-            new HTMLPurifier_AttrTransform_Length('height');
-
-        // @hspace for img ------------------------------------------------
-        $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace');
-
-        // @noshade for hr ------------------------------------------------
-        // this transformation is not precise but often good enough.
-        // different browsers use different styles to designate noshade
-        $r['hr@noshade'] =
-            new HTMLPurifier_AttrTransform_BoolToCSS(
-                'noshade',
-                'color:#808080;background-color:#808080;border:0;'
-            );
-
-        // @nowrap for td, th ---------------------------------------------
-        $r['td@nowrap'] =
-        $r['th@nowrap'] =
-            new HTMLPurifier_AttrTransform_BoolToCSS(
-                'nowrap',
-                'white-space:nowrap;'
-            );
-
-        // @size for hr  --------------------------------------------------
-        $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height');
-
-        // @type for li, ol, ul -------------------------------------------
-        // {{{
-        $ul_types = array(
-            'disc' => 'list-style-type:disc;',
-            'square' => 'list-style-type:square;',
-            'circle' => 'list-style-type:circle;'
-        );
-        $ol_types = array(
-            '1' => 'list-style-type:decimal;',
-            'i' => 'list-style-type:lower-roman;',
-            'I' => 'list-style-type:upper-roman;',
-            'a' => 'list-style-type:lower-alpha;',
-            'A' => 'list-style-type:upper-alpha;'
-        );
-        $li_types = $ul_types + $ol_types;
-        // }}}
-
-        $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types);
-        $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true);
-        $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true);
-
-        // @vspace for img ------------------------------------------------
-        $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace');
-
-        // @width for table, hr, td, th, col ------------------------------------------
-        $r['table@width'] =
-        $r['td@width'] =
-        $r['th@width'] =
-        $r['col@width'] =
-        $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width');
-
-        return $r;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php
deleted file mode 100644
index 01dbe9d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/XMLCommonAttributes.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModule_XMLCommonAttributes extends HTMLPurifier_HTMLModule
-{
-    /**
-     * @type string
-     */
-    public $name = 'XMLCommonAttributes';
-
-    /**
-     * @type array
-     */
-    public $attr_collections = array(
-        'Lang' => array(
-            'xml:lang' => 'LanguageCode',
-        )
-    );
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php
deleted file mode 100644
index 38c058f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModuleManager.php
+++ /dev/null
@@ -1,467 +0,0 @@
-<?php
-
-class HTMLPurifier_HTMLModuleManager
-{
-
-    /**
-     * @type HTMLPurifier_DoctypeRegistry
-     */
-    public $doctypes;
-
-    /**
-     * Instance of current doctype.
-     * @type string
-     */
-    public $doctype;
-
-    /**
-     * @type HTMLPurifier_AttrTypes
-     */
-    public $attrTypes;
-
-    /**
-     * Active instances of modules for the specified doctype are
-     * indexed, by name, in this array.
-     * @type HTMLPurifier_HTMLModule[]
-     */
-    public $modules = array();
-
-    /**
-     * Array of recognized HTMLPurifier_HTMLModule instances,
-     * indexed by module's class name. This array is usually lazy loaded, but a
-     * user can overload a module by pre-emptively registering it.
-     * @type HTMLPurifier_HTMLModule[]
-     */
-    public $registeredModules = array();
-
-    /**
-     * List of extra modules that were added by the user
-     * using addModule(). These get unconditionally merged into the current doctype, whatever
-     * it may be.
-     * @type HTMLPurifier_HTMLModule[]
-     */
-    public $userModules = array();
-
-    /**
-     * Associative array of element name to list of modules that have
-     * definitions for the element; this array is dynamically filled.
-     * @type array
-     */
-    public $elementLookup = array();
-
-    /**
-     * List of prefixes we should use for registering small names.
-     * @type array
-     */
-    public $prefixes = array('HTMLPurifier_HTMLModule_');
-
-    /**
-     * @type HTMLPurifier_ContentSets
-     */
-    public $contentSets;
-
-    /**
-     * @type HTMLPurifier_AttrCollections
-     */
-    public $attrCollections;
-
-    /**
-     * If set to true, unsafe elements and attributes will be allowed.
-     * @type bool
-     */
-    public $trusted = false;
-
-    public function __construct()
-    {
-        // editable internal objects
-        $this->attrTypes = new HTMLPurifier_AttrTypes();
-        $this->doctypes  = new HTMLPurifier_DoctypeRegistry();
-
-        // setup basic modules
-        $common = array(
-            'CommonAttributes', 'Text', 'Hypertext', 'List',
-            'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
-            'StyleAttribute',
-            // Unsafe:
-            'Scripting', 'Object', 'Forms',
-            // Sorta legacy, but present in strict:
-            'Name',
-        );
-        $transitional = array('Legacy', 'Target', 'Iframe');
-        $xml = array('XMLCommonAttributes');
-        $non_xml = array('NonXMLCommonAttributes');
-
-        // setup basic doctypes
-        $this->doctypes->register(
-            'HTML 4.01 Transitional',
-            false,
-            array_merge($common, $transitional, $non_xml),
-            array('Tidy_Transitional', 'Tidy_Proprietary'),
-            array(),
-            '-//W3C//DTD HTML 4.01 Transitional//EN',
-            'http://www.w3.org/TR/html4/loose.dtd'
-        );
-
-        $this->doctypes->register(
-            'HTML 4.01 Strict',
-            false,
-            array_merge($common, $non_xml),
-            array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
-            array(),
-            '-//W3C//DTD HTML 4.01//EN',
-            'http://www.w3.org/TR/html4/strict.dtd'
-        );
-
-        $this->doctypes->register(
-            'XHTML 1.0 Transitional',
-            true,
-            array_merge($common, $transitional, $xml, $non_xml),
-            array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'),
-            array(),
-            '-//W3C//DTD XHTML 1.0 Transitional//EN',
-            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
-        );
-
-        $this->doctypes->register(
-            'XHTML 1.0 Strict',
-            true,
-            array_merge($common, $xml, $non_xml),
-            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
-            array(),
-            '-//W3C//DTD XHTML 1.0 Strict//EN',
-            'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
-        );
-
-        $this->doctypes->register(
-            'XHTML 1.1',
-            true,
-            // Iframe is a real XHTML 1.1 module, despite being
-            // "transitional"!
-            array_merge($common, $xml, array('Ruby', 'Iframe')),
-            array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1
-            array(),
-            '-//W3C//DTD XHTML 1.1//EN',
-            'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
-        );
-
-    }
-
-    /**
-     * Registers a module to the recognized module list, useful for
-     * overloading pre-existing modules.
-     * @param $module Mixed: string module name, with or without
-     *                HTMLPurifier_HTMLModule prefix, or instance of
-     *                subclass of HTMLPurifier_HTMLModule.
-     * @param $overload Boolean whether or not to overload previous modules.
-     *                  If this is not set, and you do overload a module,
-     *                  HTML Purifier will complain with a warning.
-     * @note This function will not call autoload, you must instantiate
-     *       (and thus invoke) autoload outside the method.
-     * @note If a string is passed as a module name, different variants
-     *       will be tested in this order:
-     *          - Check for HTMLPurifier_HTMLModule_$name
-     *          - Check all prefixes with $name in order they were added
-     *          - Check for literal object name
-     *          - Throw fatal error
-     *       If your object name collides with an internal class, specify
-     *       your module manually. All modules must have been included
-     *       externally: registerModule will not perform inclusions for you!
-     */
-    public function registerModule($module, $overload = false)
-    {
-        if (is_string($module)) {
-            // attempt to load the module
-            $original_module = $module;
-            $ok = false;
-            foreach ($this->prefixes as $prefix) {
-                $module = $prefix . $original_module;
-                if (class_exists($module)) {
-                    $ok = true;
-                    break;
-                }
-            }
-            if (!$ok) {
-                $module = $original_module;
-                if (!class_exists($module)) {
-                    trigger_error(
-                        $original_module . ' module does not exist',
-                        E_USER_ERROR
-                    );
-                    return;
-                }
-            }
-            $module = new $module();
-        }
-        if (empty($module->name)) {
-            trigger_error('Module instance of ' . get_class($module) . ' must have name');
-            return;
-        }
-        if (!$overload && isset($this->registeredModules[$module->name])) {
-            trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING);
-        }
-        $this->registeredModules[$module->name] = $module;
-    }
-
-    /**
-     * Adds a module to the current doctype by first registering it,
-     * and then tacking it on to the active doctype
-     */
-    public function addModule($module)
-    {
-        $this->registerModule($module);
-        if (is_object($module)) {
-            $module = $module->name;
-        }
-        $this->userModules[] = $module;
-    }
-
-    /**
-     * Adds a class prefix that registerModule() will use to resolve a
-     * string name to a concrete class
-     */
-    public function addPrefix($prefix)
-    {
-        $this->prefixes[] = $prefix;
-    }
-
-    /**
-     * Performs processing on modules, after being called you may
-     * use getElement() and getElements()
-     * @param HTMLPurifier_Config $config
-     */
-    public function setup($config)
-    {
-        $this->trusted = $config->get('HTML.Trusted');
-
-        // generate
-        $this->doctype = $this->doctypes->make($config);
-        $modules = $this->doctype->modules;
-
-        // take out the default modules that aren't allowed
-        $lookup = $config->get('HTML.AllowedModules');
-        $special_cases = $config->get('HTML.CoreModules');
-
-        if (is_array($lookup)) {
-            foreach ($modules as $k => $m) {
-                if (isset($special_cases[$m])) {
-                    continue;
-                }
-                if (!isset($lookup[$m])) {
-                    unset($modules[$k]);
-                }
-            }
-        }
-
-        // custom modules
-        if ($config->get('HTML.Proprietary')) {
-            $modules[] = 'Proprietary';
-        }
-        if ($config->get('HTML.SafeObject')) {
-            $modules[] = 'SafeObject';
-        }
-        if ($config->get('HTML.SafeEmbed')) {
-            $modules[] = 'SafeEmbed';
-        }
-        if ($config->get('HTML.SafeScripting') !== array()) {
-            $modules[] = 'SafeScripting';
-        }
-        if ($config->get('HTML.Nofollow')) {
-            $modules[] = 'Nofollow';
-        }
-        if ($config->get('HTML.TargetBlank')) {
-            $modules[] = 'TargetBlank';
-        }
-        // NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER HTML.TargetBlank
-        // so that its post-attr-transform gets run afterwards.
-        if ($config->get('HTML.TargetNoreferrer')) {
-            $modules[] = 'TargetNoreferrer';
-        }
-        if ($config->get('HTML.TargetNoopener')) {
-            $modules[] = 'TargetNoopener';
-        }
-
-        // merge in custom modules
-        $modules = array_merge($modules, $this->userModules);
-
-        foreach ($modules as $module) {
-            $this->processModule($module);
-            $this->modules[$module]->setup($config);
-        }
-
-        foreach ($this->doctype->tidyModules as $module) {
-            $this->processModule($module);
-            $this->modules[$module]->setup($config);
-        }
-
-        // prepare any injectors
-        foreach ($this->modules as $module) {
-            $n = array();
-            foreach ($module->info_injector as $injector) {
-                if (!is_object($injector)) {
-                    $class = "HTMLPurifier_Injector_$injector";
-                    $injector = new $class;
-                }
-                $n[$injector->name] = $injector;
-            }
-            $module->info_injector = $n;
-        }
-
-        // setup lookup table based on all valid modules
-        foreach ($this->modules as $module) {
-            foreach ($module->info as $name => $def) {
-                if (!isset($this->elementLookup[$name])) {
-                    $this->elementLookup[$name] = array();
-                }
-                $this->elementLookup[$name][] = $module->name;
-            }
-        }
-
-        // note the different choice
-        $this->contentSets = new HTMLPurifier_ContentSets(
-            // content set assembly deals with all possible modules,
-            // not just ones deemed to be "safe"
-            $this->modules
-        );
-        $this->attrCollections = new HTMLPurifier_AttrCollections(
-            $this->attrTypes,
-            // there is no way to directly disable a global attribute,
-            // but using AllowedAttributes or simply not including
-            // the module in your custom doctype should be sufficient
-            $this->modules
-        );
-    }
-
-    /**
-     * Takes a module and adds it to the active module collection,
-     * registering it if necessary.
-     */
-    public function processModule($module)
-    {
-        if (!isset($this->registeredModules[$module]) || is_object($module)) {
-            $this->registerModule($module);
-        }
-        $this->modules[$module] = $this->registeredModules[$module];
-    }
-
-    /**
-     * Retrieves merged element definitions.
-     * @return Array of HTMLPurifier_ElementDef
-     */
-    public function getElements()
-    {
-        $elements = array();
-        foreach ($this->modules as $module) {
-            if (!$this->trusted && !$module->safe) {
-                continue;
-            }
-            foreach ($module->info as $name => $v) {
-                if (isset($elements[$name])) {
-                    continue;
-                }
-                $elements[$name] = $this->getElement($name);
-            }
-        }
-
-        // remove dud elements, this happens when an element that
-        // appeared to be safe actually wasn't
-        foreach ($elements as $n => $v) {
-            if ($v === false) {
-                unset($elements[$n]);
-            }
-        }
-
-        return $elements;
-
-    }
-
-    /**
-     * Retrieves a single merged element definition
-     * @param string $name Name of element
-     * @param bool $trusted Boolean trusted overriding parameter: set to true
-     *                 if you want the full version of an element
-     * @return HTMLPurifier_ElementDef Merged HTMLPurifier_ElementDef
-     * @note You may notice that modules are getting iterated over twice (once
-     *       in getElements() and once here). This
-     *       is because
-     */
-    public function getElement($name, $trusted = null)
-    {
-        if (!isset($this->elementLookup[$name])) {
-            return false;
-        }
-
-        // setup global state variables
-        $def = false;
-        if ($trusted === null) {
-            $trusted = $this->trusted;
-        }
-
-        // iterate through each module that has registered itself to this
-        // element
-        foreach ($this->elementLookup[$name] as $module_name) {
-            $module = $this->modules[$module_name];
-
-            // refuse to create/merge from a module that is deemed unsafe--
-            // pretend the module doesn't exist--when trusted mode is not on.
-            if (!$trusted && !$module->safe) {
-                continue;
-            }
-
-            // clone is used because, ideally speaking, the original
-            // definition should not be modified. Usually, this will
-            // make no difference, but for consistency's sake
-            $new_def = clone $module->info[$name];
-
-            if (!$def && $new_def->standalone) {
-                $def = $new_def;
-            } elseif ($def) {
-                // This will occur even if $new_def is standalone. In practice,
-                // this will usually result in a full replacement.
-                $def->mergeIn($new_def);
-            } else {
-                // :TODO:
-                // non-standalone definitions that don't have a standalone
-                // to merge into could be deferred to the end
-                // HOWEVER, it is perfectly valid for a non-standalone
-                // definition to lack a standalone definition, even
-                // after all processing: this allows us to safely
-                // specify extra attributes for elements that may not be
-                // enabled all in one place.  In particular, this might
-                // be the case for trusted elements.  WARNING: care must
-                // be taken that the /extra/ definitions are all safe.
-                continue;
-            }
-
-            // attribute value expansions
-            $this->attrCollections->performInclusions($def->attr);
-            $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
-
-            // descendants_are_inline, for ChildDef_Chameleon
-            if (is_string($def->content_model) &&
-                strpos($def->content_model, 'Inline') !== false) {
-                if ($name != 'del' && $name != 'ins') {
-                    // this is for you, ins/del
-                    $def->descendants_are_inline = true;
-                }
-            }
-
-            $this->contentSets->generateChildDef($def, $module);
-        }
-
-        // This can occur if there is a blank definition, but no base to
-        // mix it in with
-        if (!$def) {
-            return false;
-        }
-
-        // add information on required attributes
-        foreach ($def->attr as $attr_name => $attr_def) {
-            if ($attr_def->required) {
-                $def->required_attr[] = $attr_name;
-            }
-        }
-        return $def;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php
deleted file mode 100644
index 65c902c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/IDAccumulator.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/**
- * Component of HTMLPurifier_AttrContext that accumulates IDs to prevent dupes
- * @note In Slashdot-speak, dupe means duplicate.
- * @note The default constructor does not accept $config or $context objects:
- *       use must use the static build() factory method to perform initialization.
- */
-class HTMLPurifier_IDAccumulator
-{
-
-    /**
-     * Lookup table of IDs we've accumulated.
-     * @public
-     */
-    public $ids = array();
-
-    /**
-     * Builds an IDAccumulator, also initializing the default blacklist
-     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
-     * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
-     * @return HTMLPurifier_IDAccumulator Fully initialized HTMLPurifier_IDAccumulator
-     */
-    public static function build($config, $context)
-    {
-        $id_accumulator = new HTMLPurifier_IDAccumulator();
-        $id_accumulator->load($config->get('Attr.IDBlacklist'));
-        return $id_accumulator;
-    }
-
-    /**
-     * Add an ID to the lookup table.
-     * @param string $id ID to be added.
-     * @return bool status, true if success, false if there's a dupe
-     */
-    public function add($id)
-    {
-        if (isset($this->ids[$id])) {
-            return false;
-        }
-        return $this->ids[$id] = true;
-    }
-
-    /**
-     * Load a list of IDs into the lookup table
-     * @param $array_of_ids Array of IDs to load
-     * @note This function doesn't care about duplicates
-     */
-    public function load($array_of_ids)
-    {
-        foreach ($array_of_ids as $id) {
-            $this->ids[$id] = true;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php
deleted file mode 100644
index 116b470..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector.php
+++ /dev/null
@@ -1,283 +0,0 @@
-<?php
-
-/**
- * Injects tokens into the document while parsing for well-formedness.
- * This enables "formatter-like" functionality such as auto-paragraphing,
- * smiley-ification and linkification to take place.
- *
- * A note on how handlers create changes; this is done by assigning a new
- * value to the $token reference. These values can take a variety of forms and
- * are best described HTMLPurifier_Strategy_MakeWellFormed->processToken()
- * documentation.
- *
- * @todo Allow injectors to request a re-run on their output. This
- *       would help if an operation is recursive.
- */
-abstract class HTMLPurifier_Injector
-{
-
-    /**
-     * Advisory name of injector, this is for friendly error messages.
-     * @type string
-     */
-    public $name;
-
-    /**
-     * @type HTMLPurifier_HTMLDefinition
-     */
-    protected $htmlDefinition;
-
-    /**
-     * Reference to CurrentNesting variable in Context. This is an array
-     * list of tokens that we are currently "inside"
-     * @type array
-     */
-    protected $currentNesting;
-
-    /**
-     * Reference to current token.
-     * @type HTMLPurifier_Token
-     */
-    protected $currentToken;
-
-    /**
-     * Reference to InputZipper variable in Context.
-     * @type HTMLPurifier_Zipper
-     */
-    protected $inputZipper;
-
-    /**
-     * Array of elements and attributes this injector creates and therefore
-     * need to be allowed by the definition. Takes form of
-     * array('element' => array('attr', 'attr2'), 'element2')
-     * @type array
-     */
-    public $needed = array();
-
-    /**
-     * Number of elements to rewind backwards (relative).
-     * @type bool|int
-     */
-    protected $rewindOffset = false;
-
-    /**
-     * Rewind to a spot to re-perform processing. This is useful if you
-     * deleted a node, and now need to see if this change affected any
-     * earlier nodes. Rewinding does not affect other injectors, and can
-     * result in infinite loops if not used carefully.
-     * @param bool|int $offset
-     * @warning HTML Purifier will prevent you from fast-forwarding with this
-     *          function.
-     */
-    public function rewindOffset($offset)
-    {
-        $this->rewindOffset = $offset;
-    }
-
-    /**
-     * Retrieves rewind offset, and then unsets it.
-     * @return bool|int
-     */
-    public function getRewindOffset()
-    {
-        $r = $this->rewindOffset;
-        $this->rewindOffset = false;
-        return $r;
-    }
-
-    /**
-     * Prepares the injector by giving it the config and context objects:
-     * this allows references to important variables to be made within
-     * the injector. This function also checks if the HTML environment
-     * will work with the Injector (see checkNeeded()).
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool|string Boolean false if success, string of missing needed element/attribute if failure
-     */
-    public function prepare($config, $context)
-    {
-        $this->htmlDefinition = $config->getHTMLDefinition();
-        // Even though this might fail, some unit tests ignore this and
-        // still test checkNeeded, so be careful. Maybe get rid of that
-        // dependency.
-        $result = $this->checkNeeded($config);
-        if ($result !== false) {
-            return $result;
-        }
-        $this->currentNesting =& $context->get('CurrentNesting');
-        $this->currentToken   =& $context->get('CurrentToken');
-        $this->inputZipper    =& $context->get('InputZipper');
-        return false;
-    }
-
-    /**
-     * This function checks if the HTML environment
-     * will work with the Injector: if p tags are not allowed, the
-     * Auto-Paragraphing injector should not be enabled.
-     * @param HTMLPurifier_Config $config
-     * @return bool|string Boolean false if success, string of missing needed element/attribute if failure
-     */
-    public function checkNeeded($config)
-    {
-        $def = $config->getHTMLDefinition();
-        foreach ($this->needed as $element => $attributes) {
-            if (is_int($element)) {
-                $element = $attributes;
-            }
-            if (!isset($def->info[$element])) {
-                return $element;
-            }
-            if (!is_array($attributes)) {
-                continue;
-            }
-            foreach ($attributes as $name) {
-                if (!isset($def->info[$element]->attr[$name])) {
-                    return "$element.$name";
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Tests if the context node allows a certain element
-     * @param string $name Name of element to test for
-     * @return bool True if element is allowed, false if it is not
-     */
-    public function allowsElement($name)
-    {
-        if (!empty($this->currentNesting)) {
-            $parent_token = array_pop($this->currentNesting);
-            $this->currentNesting[] = $parent_token;
-            $parent = $this->htmlDefinition->info[$parent_token->name];
-        } else {
-            $parent = $this->htmlDefinition->info_parent_def;
-        }
-        if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) {
-            return false;
-        }
-        // check for exclusion
-        if (!empty($this->currentNesting)) {
-            for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) {
-                $node = $this->currentNesting[$i];
-                $def  = $this->htmlDefinition->info[$node->name];
-                if (isset($def->excludes[$name])) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Iterator function, which starts with the next token and continues until
-     * you reach the end of the input tokens.
-     * @warning Please prevent previous references from interfering with this
-     *          functions by setting $i = null beforehand!
-     * @param int $i Current integer index variable for inputTokens
-     * @param HTMLPurifier_Token $current Current token variable.
-     *          Do NOT use $token, as that variable is also a reference
-     * @return bool
-     */
-    protected function forward(&$i, &$current)
-    {
-        if ($i === null) {
-            $i = count($this->inputZipper->back) - 1;
-        } else {
-            $i--;
-        }
-        if ($i < 0) {
-            return false;
-        }
-        $current = $this->inputZipper->back[$i];
-        return true;
-    }
-
-    /**
-     * Similar to _forward, but accepts a third parameter $nesting (which
-     * should be initialized at 0) and stops when we hit the end tag
-     * for the node $this->inputIndex starts in.
-     * @param int $i Current integer index variable for inputTokens
-     * @param HTMLPurifier_Token $current Current token variable.
-     *          Do NOT use $token, as that variable is also a reference
-     * @param int $nesting
-     * @return bool
-     */
-    protected function forwardUntilEndToken(&$i, &$current, &$nesting)
-    {
-        $result = $this->forward($i, $current);
-        if (!$result) {
-            return false;
-        }
-        if ($nesting === null) {
-            $nesting = 0;
-        }
-        if ($current instanceof HTMLPurifier_Token_Start) {
-            $nesting++;
-        } elseif ($current instanceof HTMLPurifier_Token_End) {
-            if ($nesting <= 0) {
-                return false;
-            }
-            $nesting--;
-        }
-        return true;
-    }
-
-    /**
-     * Iterator function, starts with the previous token and continues until
-     * you reach the beginning of input tokens.
-     * @warning Please prevent previous references from interfering with this
-     *          functions by setting $i = null beforehand!
-     * @param int $i Current integer index variable for inputTokens
-     * @param HTMLPurifier_Token $current Current token variable.
-     *          Do NOT use $token, as that variable is also a reference
-     * @return bool
-     */
-    protected function backward(&$i, &$current)
-    {
-        if ($i === null) {
-            $i = count($this->inputZipper->front) - 1;
-        } else {
-            $i--;
-        }
-        if ($i < 0) {
-            return false;
-        }
-        $current = $this->inputZipper->front[$i];
-        return true;
-    }
-
-    /**
-     * Handler that is called when a text token is processed
-     */
-    public function handleText(&$token)
-    {
-    }
-
-    /**
-     * Handler that is called when a start or empty token is processed
-     */
-    public function handleElement(&$token)
-    {
-    }
-
-    /**
-     * Handler that is called when an end token is processed
-     */
-    public function handleEnd(&$token)
-    {
-        $this->notifyEnd($token);
-    }
-
-    /**
-     * Notifier that is called when an end token is processed
-     * @param HTMLPurifier_Token $token Current token variable.
-     * @note This differs from handlers in that the token is read-only
-     * @deprecated
-     */
-    public function notifyEnd($token)
-    {
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php
deleted file mode 100644
index 4afdd12..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/AutoParagraph.php
+++ /dev/null
@@ -1,356 +0,0 @@
-<?php
-
-/**
- * Injector that auto paragraphs text in the root node based on
- * double-spacing.
- * @todo Ensure all states are unit tested, including variations as well.
- * @todo Make a graph of the flow control for this Injector.
- */
-class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector
-{
-    /**
-     * @type string
-     */
-    public $name = 'AutoParagraph';
-
-    /**
-     * @type array
-     */
-    public $needed = array('p');
-
-    /**
-     * @return HTMLPurifier_Token_Start
-     */
-    private function _pStart()
-    {
-        $par = new HTMLPurifier_Token_Start('p');
-        $par->armor['MakeWellFormed_TagClosedError'] = true;
-        return $par;
-    }
-
-    /**
-     * @param HTMLPurifier_Token_Text $token
-     */
-    public function handleText(&$token)
-    {
-        $text = $token->data;
-        // Does the current parent allow <p> tags?
-        if ($this->allowsElement('p')) {
-            if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) {
-                // Note that we have differing behavior when dealing with text
-                // in the anonymous root node, or a node inside the document.
-                // If the text as a double-newline, the treatment is the same;
-                // if it doesn't, see the next if-block if you're in the document.
-
-                $i = $nesting = null;
-                if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) {
-                    // State 1.1: ...    ^ (whitespace, then document end)
-                    //               ----
-                    // This is a degenerate case
-                } else {
-                    if (!$token->is_whitespace || $this->_isInline($current)) {
-                        // State 1.2: PAR1
-                        //            ----
-
-                        // State 1.3: PAR1\n\nPAR2
-                        //            ------------
-
-                        // State 1.4: <div>PAR1\n\nPAR2 (see State 2)
-                        //                 ------------
-                        $token = array($this->_pStart());
-                        $this->_splitText($text, $token);
-                    } else {
-                        // State 1.5: \n<hr />
-                        //            --
-                    }
-                }
-            } else {
-                // State 2:   <div>PAR1... (similar to 1.4)
-                //                 ----
-
-                // We're in an element that allows paragraph tags, but we're not
-                // sure if we're going to need them.
-                if ($this->_pLookAhead()) {
-                    // State 2.1: <div>PAR1<b>PAR1\n\nPAR2
-                    //                 ----
-                    // Note: This will always be the first child, since any
-                    // previous inline element would have triggered this very
-                    // same routine, and found the double newline. One possible
-                    // exception would be a comment.
-                    $token = array($this->_pStart(), $token);
-                } else {
-                    // State 2.2.1: <div>PAR1<div>
-                    //                   ----
-
-                    // State 2.2.2: <div>PAR1<b>PAR1</b></div>
-                    //                   ----
-                }
-            }
-            // Is the current parent a <p> tag?
-        } elseif (!empty($this->currentNesting) &&
-            $this->currentNesting[count($this->currentNesting) - 1]->name == 'p') {
-            // State 3.1: ...<p>PAR1
-            //                  ----
-
-            // State 3.2: ...<p>PAR1\n\nPAR2
-            //                  ------------
-            $token = array();
-            $this->_splitText($text, $token);
-            // Abort!
-        } else {
-            // State 4.1: ...<b>PAR1
-            //                  ----
-
-            // State 4.2: ...<b>PAR1\n\nPAR2
-            //                  ------------
-        }
-    }
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleElement(&$token)
-    {
-        // We don't have to check if we're already in a <p> tag for block
-        // tokens, because the tag would have been autoclosed by MakeWellFormed.
-        if ($this->allowsElement('p')) {
-            if (!empty($this->currentNesting)) {
-                if ($this->_isInline($token)) {
-                    // State 1: <div>...<b>
-                    //                  ---
-                    // Check if this token is adjacent to the parent token
-                    // (seek backwards until token isn't whitespace)
-                    $i = null;
-                    $this->backward($i, $prev);
-
-                    if (!$prev instanceof HTMLPurifier_Token_Start) {
-                        // Token wasn't adjacent
-                        if ($prev instanceof HTMLPurifier_Token_Text &&
-                            substr($prev->data, -2) === "\n\n"
-                        ) {
-                            // State 1.1.4: <div><p>PAR1</p>\n\n<b>
-                            //                                  ---
-                            // Quite frankly, this should be handled by splitText
-                            $token = array($this->_pStart(), $token);
-                        } else {
-                            // State 1.1.1: <div><p>PAR1</p><b>
-                            //                              ---
-                            // State 1.1.2: <div><br /><b>
-                            //                         ---
-                            // State 1.1.3: <div>PAR<b>
-                            //                      ---
-                        }
-                    } else {
-                        // State 1.2.1: <div><b>
-                        //                   ---
-                        // Lookahead to see if <p> is needed.
-                        if ($this->_pLookAhead()) {
-                            // State 1.3.1: <div><b>PAR1\n\nPAR2
-                            //                   ---
-                            $token = array($this->_pStart(), $token);
-                        } else {
-                            // State 1.3.2: <div><b>PAR1</b></div>
-                            //                   ---
-
-                            // State 1.3.3: <div><b>PAR1</b><div></div>\n\n</div>
-                            //                   ---
-                        }
-                    }
-                } else {
-                    // State 2.3: ...<div>
-                    //               -----
-                }
-            } else {
-                if ($this->_isInline($token)) {
-                    // State 3.1: <b>
-                    //            ---
-                    // This is where the {p} tag is inserted, not reflected in
-                    // inputTokens yet, however.
-                    $token = array($this->_pStart(), $token);
-                } else {
-                    // State 3.2: <div>
-                    //            -----
-                }
-
-                $i = null;
-                if ($this->backward($i, $prev)) {
-                    if (!$prev instanceof HTMLPurifier_Token_Text) {
-                        // State 3.1.1: ...</p>{p}<b>
-                        //                        ---
-                        // State 3.2.1: ...</p><div>
-                        //                     -----
-                        if (!is_array($token)) {
-                            $token = array($token);
-                        }
-                        array_unshift($token, new HTMLPurifier_Token_Text("\n\n"));
-                    } else {
-                        // State 3.1.2: ...</p>\n\n{p}<b>
-                        //                            ---
-                        // State 3.2.2: ...</p>\n\n<div>
-                        //                         -----
-                        // Note: PAR<ELEM> cannot occur because PAR would have been
-                        // wrapped in <p> tags.
-                    }
-                }
-            }
-        } else {
-            // State 2.2: <ul><li>
-            //                ----
-            // State 2.4: <p><b>
-            //               ---
-        }
-    }
-
-    /**
-     * Splits up a text in paragraph tokens and appends them
-     * to the result stream that will replace the original
-     * @param string $data String text data that will be processed
-     *    into paragraphs
-     * @param HTMLPurifier_Token[] $result Reference to array of tokens that the
-     *    tags will be appended onto
-     */
-    private function _splitText($data, &$result)
-    {
-        $raw_paragraphs = explode("\n\n", $data);
-        $paragraphs = array(); // without empty paragraphs
-        $needs_start = false;
-        $needs_end = false;
-
-        $c = count($raw_paragraphs);
-        if ($c == 1) {
-            // There were no double-newlines, abort quickly. In theory this
-            // should never happen.
-            $result[] = new HTMLPurifier_Token_Text($data);
-            return;
-        }
-        for ($i = 0; $i < $c; $i++) {
-            $par = $raw_paragraphs[$i];
-            if (trim($par) !== '') {
-                $paragraphs[] = $par;
-            } else {
-                if ($i == 0) {
-                    // Double newline at the front
-                    if (empty($result)) {
-                        // The empty result indicates that the AutoParagraph
-                        // injector did not add any start paragraph tokens.
-                        // This means that we have been in a paragraph for
-                        // a while, and the newline means we should start a new one.
-                        $result[] = new HTMLPurifier_Token_End('p');
-                        $result[] = new HTMLPurifier_Token_Text("\n\n");
-                        // However, the start token should only be added if
-                        // there is more processing to be done (i.e. there are
-                        // real paragraphs in here). If there are none, the
-                        // next start paragraph tag will be handled by the
-                        // next call to the injector
-                        $needs_start = true;
-                    } else {
-                        // We just started a new paragraph!
-                        // Reinstate a double-newline for presentation's sake, since
-                        // it was in the source code.
-                        array_unshift($result, new HTMLPurifier_Token_Text("\n\n"));
-                    }
-                } elseif ($i + 1 == $c) {
-                    // Double newline at the end
-                    // There should be a trailing </p> when we're finally done.
-                    $needs_end = true;
-                }
-            }
-        }
-
-        // Check if this was just a giant blob of whitespace. Move this earlier,
-        // perhaps?
-        if (empty($paragraphs)) {
-            return;
-        }
-
-        // Add the start tag indicated by \n\n at the beginning of $data
-        if ($needs_start) {
-            $result[] = $this->_pStart();
-        }
-
-        // Append the paragraphs onto the result
-        foreach ($paragraphs as $par) {
-            $result[] = new HTMLPurifier_Token_Text($par);
-            $result[] = new HTMLPurifier_Token_End('p');
-            $result[] = new HTMLPurifier_Token_Text("\n\n");
-            $result[] = $this->_pStart();
-        }
-
-        // Remove trailing start token; Injector will handle this later if
-        // it was indeed needed. This prevents from needing to do a lookahead,
-        // at the cost of a lookbehind later.
-        array_pop($result);
-
-        // If there is no need for an end tag, remove all of it and let
-        // MakeWellFormed close it later.
-        if (!$needs_end) {
-            array_pop($result); // removes \n\n
-            array_pop($result); // removes </p>
-        }
-    }
-
-    /**
-     * Returns true if passed token is inline (and, ergo, allowed in
-     * paragraph tags)
-     * @param HTMLPurifier_Token $token
-     * @return bool
-     */
-    private function _isInline($token)
-    {
-        return isset($this->htmlDefinition->info['p']->child->elements[$token->name]);
-    }
-
-    /**
-     * Looks ahead in the token list and determines whether or not we need
-     * to insert a <p> tag.
-     * @return bool
-     */
-    private function _pLookAhead()
-    {
-        if ($this->currentToken instanceof HTMLPurifier_Token_Start) {
-            $nesting = 1;
-        } else {
-            $nesting = 0;
-        }
-        $ok = false;
-        $i = null;
-        while ($this->forwardUntilEndToken($i, $current, $nesting)) {
-            $result = $this->_checkNeedsP($current);
-            if ($result !== null) {
-                $ok = $result;
-                break;
-            }
-        }
-        return $ok;
-    }
-
-    /**
-     * Determines if a particular token requires an earlier inline token
-     * to get a paragraph. This should be used with _forwardUntilEndToken
-     * @param HTMLPurifier_Token $current
-     * @return bool
-     */
-    private function _checkNeedsP($current)
-    {
-        if ($current instanceof HTMLPurifier_Token_Start) {
-            if (!$this->_isInline($current)) {
-                // <div>PAR1<div>
-                //      ----
-                // Terminate early, since we hit a block element
-                return false;
-            }
-        } elseif ($current instanceof HTMLPurifier_Token_Text) {
-            if (strpos($current->data, "\n\n") !== false) {
-                // <div>PAR1<b>PAR1\n\nPAR2
-                //      ----
-                return true;
-            } else {
-                // <div>PAR1<b>PAR1...
-                //      ----
-            }
-        }
-        return null;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php
deleted file mode 100644
index c19b1bc..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/DisplayLinkURI.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * Injector that displays the URL of an anchor instead of linking to it, in addition to showing the text of the link.
- */
-class HTMLPurifier_Injector_DisplayLinkURI extends HTMLPurifier_Injector
-{
-    /**
-     * @type string
-     */
-    public $name = 'DisplayLinkURI';
-
-    /**
-     * @type array
-     */
-    public $needed = array('a');
-
-    /**
-     * @param $token
-     */
-    public function handleElement(&$token)
-    {
-    }
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleEnd(&$token)
-    {
-        if (isset($token->start->attr['href'])) {
-            $url = $token->start->attr['href'];
-            unset($token->start->attr['href']);
-            $token = array($token, new HTMLPurifier_Token_Text(" ($url)"));
-        } else {
-            // nothing to display
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php
deleted file mode 100644
index 3b6d70f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/Linkify.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * Injector that converts http, https and ftp text URLs to actual links.
- */
-class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
-{
-    /**
-     * @type string
-     */
-    public $name = 'Linkify';
-
-    /**
-     * @type array
-     */
-    public $needed = array('a' => array('href'));
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleText(&$token)
-    {
-        if (!$this->allowsElement('a')) {
-            return;
-        }
-
-        if (strpos($token->data, '://') === false) {
-            // our really quick heuristic failed, abort
-            // this may not work so well if we want to match things like
-            // "google.com", but then again, most people don't
-            return;
-        }
-
-        // there is/are URL(s). Let's split the string.
-        // We use this regex:
-        // https://gist.github.com/gruber/249502
-        // but with @cscott's backtracking fix and also
-        // the Unicode characters un-Unicodified.
-        $bits = preg_split(
-            '/\\b((?:[a-z][\\w\\-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\))+(?:\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\x{00ab}\x{00bb}\x{201c}\x{201d}\x{2018}\x{2019}]))/iu',
-            $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
-
-        if ($bits === false) {
-            return;
-        }
-
-        $token = array();
-
-        // $i = index
-        // $c = count
-        // $l = is link
-        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
-            if (!$l) {
-                if ($bits[$i] === '') {
-                    continue;
-                }
-                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
-            } else {
-                $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i]));
-                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
-                $token[] = new HTMLPurifier_Token_End('a');
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php
deleted file mode 100644
index cb9046f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/PurifierLinkify.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * Injector that converts configuration directive syntax %Namespace.Directive
- * to links
- */
-class HTMLPurifier_Injector_PurifierLinkify extends HTMLPurifier_Injector
-{
-    /**
-     * @type string
-     */
-    public $name = 'PurifierLinkify';
-
-    /**
-     * @type string
-     */
-    public $docURL;
-
-    /**
-     * @type array
-     */
-    public $needed = array('a' => array('href'));
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public function prepare($config, $context)
-    {
-        $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL');
-        return parent::prepare($config, $context);
-    }
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleText(&$token)
-    {
-        if (!$this->allowsElement('a')) {
-            return;
-        }
-        if (strpos($token->data, '%') === false) {
-            return;
-        }
-
-        $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
-        $token = array();
-
-        // $i = index
-        // $c = count
-        // $l = is link
-        for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) {
-            if (!$l) {
-                if ($bits[$i] === '') {
-                    continue;
-                }
-                $token[] = new HTMLPurifier_Token_Text($bits[$i]);
-            } else {
-                $token[] = new HTMLPurifier_Token_Start(
-                    'a',
-                    array('href' => str_replace('%s', $bits[$i], $this->docURL))
-                );
-                $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]);
-                $token[] = new HTMLPurifier_Token_End('a');
-            }
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php
deleted file mode 100644
index 0ebc477..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveEmpty.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
-{
-    /**
-     * @type HTMLPurifier_Context
-     */
-    private $context;
-
-    /**
-     * @type HTMLPurifier_Config
-     */
-    private $config;
-
-    /**
-     * @type HTMLPurifier_AttrValidator
-     */
-    private $attrValidator;
-
-    /**
-     * @type bool
-     */
-    private $removeNbsp;
-
-    /**
-     * @type bool
-     */
-    private $removeNbspExceptions;
-
-    /**
-     * Cached contents of %AutoFormat.RemoveEmpty.Predicate
-     * @type array
-     */
-    private $exclude;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return void
-     */
-    public function prepare($config, $context)
-    {
-        parent::prepare($config, $context);
-        $this->config = $config;
-        $this->context = $context;
-        $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
-        $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
-        $this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
-        foreach ($this->exclude as $key => $attrs) {
-            if (!is_array($attrs)) {
-                // HACK, see HTMLPurifier/Printer/ConfigForm.php
-                $this->exclude[$key] = explode(';', $attrs);
-            }
-        }
-        $this->attrValidator = new HTMLPurifier_AttrValidator();
-    }
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleElement(&$token)
-    {
-        if (!$token instanceof HTMLPurifier_Token_Start) {
-            return;
-        }
-        $next = false;
-        $deleted = 1; // the current tag
-        for ($i = count($this->inputZipper->back) - 1; $i >= 0; $i--, $deleted++) {
-            $next = $this->inputZipper->back[$i];
-            if ($next instanceof HTMLPurifier_Token_Text) {
-                if ($next->is_whitespace) {
-                    continue;
-                }
-                if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
-                    $plain = str_replace("\xC2\xA0", "", $next->data);
-                    $isWsOrNbsp = $plain === '' || ctype_space($plain);
-                    if ($isWsOrNbsp) {
-                        continue;
-                    }
-                }
-            }
-            break;
-        }
-        if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
-            $this->attrValidator->validateToken($token, $this->config, $this->context);
-            $token->armor['ValidateAttributes'] = true;
-            if (isset($this->exclude[$token->name])) {
-                $r = true;
-                foreach ($this->exclude[$token->name] as $elem) {
-                    if (!isset($token->attr[$elem])) $r = false;
-                }
-                if ($r) return;
-            }
-            if (isset($token->attr['id']) || isset($token->attr['name'])) {
-                return;
-            }
-            $token = $deleted + 1;
-            for ($b = 0, $c = count($this->inputZipper->front); $b < $c; $b++) {
-                $prev = $this->inputZipper->front[$b];
-                if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) {
-                    continue;
-                }
-                break;
-            }
-            // This is safe because we removed the token that triggered this.
-            $this->rewindOffset($b+$deleted);
-            return;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php
deleted file mode 100644
index 42d5144..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-/**
- * Injector that removes spans with no attributes
- */
-class HTMLPurifier_Injector_RemoveSpansWithoutAttributes extends HTMLPurifier_Injector
-{
-    /**
-     * @type string
-     */
-    public $name = 'RemoveSpansWithoutAttributes';
-
-    /**
-     * @type array
-     */
-    public $needed = array('span');
-
-    /**
-     * @type HTMLPurifier_AttrValidator
-     */
-    private $attrValidator;
-
-    /**
-     * Used by AttrValidator.
-     * @type HTMLPurifier_Config
-     */
-    private $config;
-
-    /**
-     * @type HTMLPurifier_Context
-     */
-    private $context;
-
-    /**
-     * @type SplObjectStorage
-     */
-    private $markForDeletion;
-
-    public function __construct()
-    {
-        $this->markForDeletion = new SplObjectStorage();
-    }
-
-    public function prepare($config, $context)
-    {
-        $this->attrValidator = new HTMLPurifier_AttrValidator();
-        $this->config = $config;
-        $this->context = $context;
-        return parent::prepare($config, $context);
-    }
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleElement(&$token)
-    {
-        if ($token->name !== 'span' || !$token instanceof HTMLPurifier_Token_Start) {
-            return;
-        }
-
-        // We need to validate the attributes now since this doesn't normally
-        // happen until after MakeWellFormed. If all the attributes are removed
-        // the span needs to be removed too.
-        $this->attrValidator->validateToken($token, $this->config, $this->context);
-        $token->armor['ValidateAttributes'] = true;
-
-        if (!empty($token->attr)) {
-            return;
-        }
-
-        $nesting = 0;
-        while ($this->forwardUntilEndToken($i, $current, $nesting)) {
-        }
-
-        if ($current instanceof HTMLPurifier_Token_End && $current->name === 'span') {
-            // Mark closing span tag for deletion
-            $this->markForDeletion->attach($current);
-            // Delete open span tag
-            $token = false;
-        }
-    }
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleEnd(&$token)
-    {
-        if ($this->markForDeletion->contains($token)) {
-            $this->markForDeletion->detach($token);
-            $token = false;
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php
deleted file mode 100644
index 317f786..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Injector/SafeObject.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-/**
- * Adds important param elements to inside of object in order to make
- * things safe.
- */
-class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
-{
-    /**
-     * @type string
-     */
-    public $name = 'SafeObject';
-
-    /**
-     * @type array
-     */
-    public $needed = array('object', 'param');
-
-    /**
-     * @type array
-     */
-    protected $objectStack = array();
-
-    /**
-     * @type array
-     */
-    protected $paramStack = array();
-
-    /**
-     * Keep this synchronized with AttrTransform/SafeParam.php.
-     * @type array
-     */
-    protected $addParam = array(
-        'allowScriptAccess' => 'never',
-        'allowNetworking' => 'internal',
-    );
-
-    /**
-     * These are all lower-case keys.
-     * @type array
-     */
-    protected $allowedParam = array(
-        'wmode' => true,
-        'movie' => true,
-        'flashvars' => true,
-        'src' => true,
-        'allowfullscreen' => true, // if omitted, assume to be 'false'
-    );
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return void
-     */
-    public function prepare($config, $context)
-    {
-        parent::prepare($config, $context);
-    }
-
-    /**
-     * @param HTMLPurifier_Token $token
-     */
-    public function handleElement(&$token)
-    {
-        if ($token->name == 'object') {
-            $this->objectStack[] = $token;
-            $this->paramStack[] = array();
-            $new = array($token);
-            foreach ($this->addParam as $name => $value) {
-                $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value));
-            }
-            $token = $new;
-        } elseif ($token->name == 'param') {
-            $nest = count($this->currentNesting) - 1;
-            if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') {
-                $i = count($this->objectStack) - 1;
-                if (!isset($token->attr['name'])) {
-                    $token = false;
-                    return;
-                }
-                $n = $token->attr['name'];
-                // We need this fix because YouTube doesn't supply a data
-                // attribute, which we need if a type is specified. This is
-                // *very* Flash specific.
-                if (!isset($this->objectStack[$i]->attr['data']) &&
-                    ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')
-                ) {
-                    $this->objectStack[$i]->attr['data'] = $token->attr['value'];
-                }
-                // Check if the parameter is the correct value but has not
-                // already been added
-                if (!isset($this->paramStack[$i][$n]) &&
-                    isset($this->addParam[$n]) &&
-                    $token->attr['name'] === $this->addParam[$n]) {
-                    // keep token, and add to param stack
-                    $this->paramStack[$i][$n] = true;
-                } elseif (isset($this->allowedParam[strtolower($n)])) {
-                    // keep token, don't do anything to it
-                    // (could possibly check for duplicates here)
-                    // Note: In principle, parameters should be case sensitive.
-                    // But it seems they are not really; so accept any case.
-                } else {
-                    $token = false;
-                }
-            } else {
-                // not directly inside an object, DENY!
-                $token = false;
-            }
-        }
-    }
-
-    public function handleEnd(&$token)
-    {
-        // This is the WRONG way of handling the object and param stacks;
-        // we should be inserting them directly on the relevant object tokens
-        // so that the global stack handling handles it.
-        if ($token->name == 'object') {
-            array_pop($this->objectStack);
-            array_pop($this->paramStack);
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language.php
deleted file mode 100644
index 65277dd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language.php
+++ /dev/null
@@ -1,204 +0,0 @@
-<?php
-
-/**
- * Represents a language and defines localizable string formatting and
- * other functions, as well as the localized messages for HTML Purifier.
- */
-class HTMLPurifier_Language
-{
-
-    /**
-     * ISO 639 language code of language. Prefers shortest possible version.
-     * @type string
-     */
-    public $code = 'en';
-
-    /**
-     * Fallback language code.
-     * @type bool|string
-     */
-    public $fallback = false;
-
-    /**
-     * Array of localizable messages.
-     * @type array
-     */
-    public $messages = array();
-
-    /**
-     * Array of localizable error codes.
-     * @type array
-     */
-    public $errorNames = array();
-
-    /**
-     * True if no message file was found for this language, so English
-     * is being used instead. Check this if you'd like to notify the
-     * user that they've used a non-supported language.
-     * @type bool
-     */
-    public $error = false;
-
-    /**
-     * Has the language object been loaded yet?
-     * @type bool
-     * @todo Make it private, fix usage in HTMLPurifier_LanguageTest
-     */
-    public $_loaded = false;
-
-    /**
-     * @type HTMLPurifier_Config
-     */
-    protected $config;
-
-    /**
-     * @type HTMLPurifier_Context
-     */
-    protected $context;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     */
-    public function __construct($config, $context)
-    {
-        $this->config  = $config;
-        $this->context = $context;
-    }
-
-    /**
-     * Loads language object with necessary info from factory cache
-     * @note This is a lazy loader
-     */
-    public function load()
-    {
-        if ($this->_loaded) {
-            return;
-        }
-        $factory = HTMLPurifier_LanguageFactory::instance();
-        $factory->loadLanguage($this->code);
-        foreach ($factory->keys as $key) {
-            $this->$key = $factory->cache[$this->code][$key];
-        }
-        $this->_loaded = true;
-    }
-
-    /**
-     * Retrieves a localised message.
-     * @param string $key string identifier of message
-     * @return string localised message
-     */
-    public function getMessage($key)
-    {
-        if (!$this->_loaded) {
-            $this->load();
-        }
-        if (!isset($this->messages[$key])) {
-            return "[$key]";
-        }
-        return $this->messages[$key];
-    }
-
-    /**
-     * Retrieves a localised error name.
-     * @param int $int error number, corresponding to PHP's error reporting
-     * @return string localised message
-     */
-    public function getErrorName($int)
-    {
-        if (!$this->_loaded) {
-            $this->load();
-        }
-        if (!isset($this->errorNames[$int])) {
-            return "[Error: $int]";
-        }
-        return $this->errorNames[$int];
-    }
-
-    /**
-     * Converts an array list into a string readable representation
-     * @param array $array
-     * @return string
-     */
-    public function listify($array)
-    {
-        $sep      = $this->getMessage('Item separator');
-        $sep_last = $this->getMessage('Item separator last');
-        $ret = '';
-        for ($i = 0, $c = count($array); $i < $c; $i++) {
-            if ($i == 0) {
-            } elseif ($i + 1 < $c) {
-                $ret .= $sep;
-            } else {
-                $ret .= $sep_last;
-            }
-            $ret .= $array[$i];
-        }
-        return $ret;
-    }
-
-    /**
-     * Formats a localised message with passed parameters
-     * @param string $key string identifier of message
-     * @param array $args Parameters to substitute in
-     * @return string localised message
-     * @todo Implement conditionals? Right now, some messages make
-     *     reference to line numbers, but those aren't always available
-     */
-    public function formatMessage($key, $args = array())
-    {
-        if (!$this->_loaded) {
-            $this->load();
-        }
-        if (!isset($this->messages[$key])) {
-            return "[$key]";
-        }
-        $raw = $this->messages[$key];
-        $subst = array();
-        $generator = false;
-        foreach ($args as $i => $value) {
-            if (is_object($value)) {
-                if ($value instanceof HTMLPurifier_Token) {
-                    // factor this out some time
-                    if (!$generator) {
-                        $generator = $this->context->get('Generator');
-                    }
-                    if (isset($value->name)) {
-                        $subst['$'.$i.'.Name'] = $value->name;
-                    }
-                    if (isset($value->data)) {
-                        $subst['$'.$i.'.Data'] = $value->data;
-                    }
-                    $subst['$'.$i.'.Compact'] =
-                    $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value);
-                    // a more complex algorithm for compact representation
-                    // could be introduced for all types of tokens. This
-                    // may need to be factored out into a dedicated class
-                    if (!empty($value->attr)) {
-                        $stripped_token = clone $value;
-                        $stripped_token->attr = array();
-                        $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token);
-                    }
-                    $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown';
-                }
-                continue;
-            } elseif (is_array($value)) {
-                $keys = array_keys($value);
-                if (array_keys($keys) === $keys) {
-                    // list
-                    $subst['$'.$i] = $this->listify($value);
-                } else {
-                    // associative array
-                    // no $i implementation yet, sorry
-                    $subst['$'.$i.'.Keys'] = $this->listify($keys);
-                    $subst['$'.$i.'.Values'] = $this->listify(array_values($value));
-                }
-                continue;
-            }
-            $subst['$' . $i] = $value;
-        }
-        return strtr($raw, $subst);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en.php
deleted file mode 100644
index c7f197e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Language/messages/en.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-$fallback = false;
-
-$messages = array(
-
-    'HTMLPurifier' => 'HTML Purifier',
-// for unit testing purposes
-    'LanguageFactoryTest: Pizza' => 'Pizza',
-    'LanguageTest: List' => '$1',
-    'LanguageTest: Hash' => '$1.Keys; $1.Values',
-    'Item separator' => ', ',
-    'Item separator last' => ' and ', // non-Harvard style
-
-    'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.',
-    'ErrorCollector: At line' => ' at line $line',
-    'ErrorCollector: Incidental errors' => 'Incidental errors',
-    'Lexer: Unclosed comment' => 'Unclosed comment',
-    'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be &lt;',
-    'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped',
-    'Lexer: Missing attribute key' => 'Attribute declaration has no key',
-    'Lexer: Missing end quote' => 'Attribute declaration has no end quote',
-    'Lexer: Extracted body' => 'Removed document metadata tags',
-    'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized',
-    'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1',
-    'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text',
-    'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed',
-    'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed',
-    'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed',
-    'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end',
-    'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed',
-    'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens',
-    'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed',
-    'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text',
-    'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact',
-    'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact',
-    'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed',
-    'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text',
-    'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized',
-    'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document',
-    'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed',
-    'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element',
-    'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model',
-    'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed',
-    'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys',
-    'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed',
-);
-
-$errorNames = array(
-    E_ERROR => 'Error',
-    E_WARNING => 'Warning',
-    E_NOTICE => 'Notice'
-);
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/LanguageFactory.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/LanguageFactory.php
deleted file mode 100644
index 4e35272..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/LanguageFactory.php
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-
-/**
- * Class responsible for generating HTMLPurifier_Language objects, managing
- * caching and fallbacks.
- * @note Thanks to MediaWiki for the general logic, although this version
- *       has been entirely rewritten
- * @todo Serialized cache for languages
- */
-class HTMLPurifier_LanguageFactory
-{
-
-    /**
-     * Cache of language code information used to load HTMLPurifier_Language objects.
-     * Structure is: $factory->cache[$language_code][$key] = $value
-     * @type array
-     */
-    public $cache;
-
-    /**
-     * Valid keys in the HTMLPurifier_Language object. Designates which
-     * variables to slurp out of a message file.
-     * @type array
-     */
-    public $keys = array('fallback', 'messages', 'errorNames');
-
-    /**
-     * Instance to validate language codes.
-     * @type HTMLPurifier_AttrDef_Lang
-     *
-     */
-    protected $validator;
-
-    /**
-     * Cached copy of dirname(__FILE__), directory of current file without
-     * trailing slash.
-     * @type string
-     */
-    protected $dir;
-
-    /**
-     * Keys whose contents are a hash map and can be merged.
-     * @type array
-     */
-    protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true);
-
-    /**
-     * Keys whose contents are a list and can be merged.
-     * @value array lookup
-     */
-    protected $mergeable_keys_list = array();
-
-    /**
-     * Retrieve sole instance of the factory.
-     * @param HTMLPurifier_LanguageFactory $prototype Optional prototype to overload sole instance with,
-     *                   or bool true to reset to default factory.
-     * @return HTMLPurifier_LanguageFactory
-     */
-    public static function instance($prototype = null)
-    {
-        static $instance = null;
-        if ($prototype !== null) {
-            $instance = $prototype;
-        } elseif ($instance === null || $prototype == true) {
-            $instance = new HTMLPurifier_LanguageFactory();
-            $instance->setup();
-        }
-        return $instance;
-    }
-
-    /**
-     * Sets up the singleton, much like a constructor
-     * @note Prevents people from getting this outside of the singleton
-     */
-    public function setup()
-    {
-        $this->validator = new HTMLPurifier_AttrDef_Lang();
-        $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier';
-    }
-
-    /**
-     * Creates a language object, handles class fallbacks
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @param bool|string $code Code to override configuration with. Private parameter.
-     * @return HTMLPurifier_Language
-     */
-    public function create($config, $context, $code = false)
-    {
-        // validate language code
-        if ($code === false) {
-            $code = $this->validator->validate(
-                $config->get('Core.Language'),
-                $config,
-                $context
-            );
-        } else {
-            $code = $this->validator->validate($code, $config, $context);
-        }
-        if ($code === false) {
-            $code = 'en'; // malformed code becomes English
-        }
-
-        $pcode = str_replace('-', '_', $code); // make valid PHP classname
-        static $depth = 0; // recursion protection
-
-        if ($code == 'en') {
-            $lang = new HTMLPurifier_Language($config, $context);
-        } else {
-            $class = 'HTMLPurifier_Language_' . $pcode;
-            $file  = $this->dir . '/Language/classes/' . $code . '.php';
-            if (file_exists($file) || class_exists($class, false)) {
-                $lang = new $class($config, $context);
-            } else {
-                // Go fallback
-                $raw_fallback = $this->getFallbackFor($code);
-                $fallback = $raw_fallback ? $raw_fallback : 'en';
-                $depth++;
-                $lang = $this->create($config, $context, $fallback);
-                if (!$raw_fallback) {
-                    $lang->error = true;
-                }
-                $depth--;
-            }
-        }
-        $lang->code = $code;
-        return $lang;
-    }
-
-    /**
-     * Returns the fallback language for language
-     * @note Loads the original language into cache
-     * @param string $code language code
-     * @return string|bool
-     */
-    public function getFallbackFor($code)
-    {
-        $this->loadLanguage($code);
-        return $this->cache[$code]['fallback'];
-    }
-
-    /**
-     * Loads language into the cache, handles message file and fallbacks
-     * @param string $code language code
-     */
-    public function loadLanguage($code)
-    {
-        static $languages_seen = array(); // recursion guard
-
-        // abort if we've already loaded it
-        if (isset($this->cache[$code])) {
-            return;
-        }
-
-        // generate filename
-        $filename = $this->dir . '/Language/messages/' . $code . '.php';
-
-        // default fallback : may be overwritten by the ensuing include
-        $fallback = ($code != 'en') ? 'en' : false;
-
-        // load primary localisation
-        if (!file_exists($filename)) {
-            // skip the include: will rely solely on fallback
-            $filename = $this->dir . '/Language/messages/en.php';
-            $cache = array();
-        } else {
-            include $filename;
-            $cache = compact($this->keys);
-        }
-
-        // load fallback localisation
-        if (!empty($fallback)) {
-
-            // infinite recursion guard
-            if (isset($languages_seen[$code])) {
-                trigger_error(
-                    'Circular fallback reference in language ' .
-                    $code,
-                    E_USER_ERROR
-                );
-                $fallback = 'en';
-            }
-            $language_seen[$code] = true;
-
-            // load the fallback recursively
-            $this->loadLanguage($fallback);
-            $fallback_cache = $this->cache[$fallback];
-
-            // merge fallback with current language
-            foreach ($this->keys as $key) {
-                if (isset($cache[$key]) && isset($fallback_cache[$key])) {
-                    if (isset($this->mergeable_keys_map[$key])) {
-                        $cache[$key] = $cache[$key] + $fallback_cache[$key];
-                    } elseif (isset($this->mergeable_keys_list[$key])) {
-                        $cache[$key] = array_merge($fallback_cache[$key], $cache[$key]);
-                    }
-                } else {
-                    $cache[$key] = $fallback_cache[$key];
-                }
-            }
-        }
-
-        // save to cache for later retrieval
-        $this->cache[$code] = $cache;
-        return;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Length.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Length.php
deleted file mode 100644
index b6ea123..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Length.php
+++ /dev/null
@@ -1,162 +0,0 @@
-<?php
-
-/**
- * Represents a measurable length, with a string numeric magnitude
- * and a unit. This object is immutable.
- */
-class HTMLPurifier_Length
-{
-
-    /**
-     * String numeric magnitude.
-     * @type string
-     */
-    protected $n;
-
-    /**
-     * String unit. False is permitted if $n = 0.
-     * @type string|bool
-     */
-    protected $unit;
-
-    /**
-     * Whether or not this length is valid. Null if not calculated yet.
-     * @type bool
-     */
-    protected $isValid;
-
-    /**
-     * Array Lookup array of units recognized by CSS 3
-     * @type array
-     */
-    protected static $allowedUnits = array(
-        'em' => true, 'ex' => true, 'px' => true, 'in' => true,
-        'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true,
-        'ch' => true, 'rem' => true, 'vw' => true, 'vh' => true,
-        'vmin' => true, 'vmax' => true
-    );
-
-    /**
-     * @param string $n Magnitude
-     * @param bool|string $u Unit
-     */
-    public function __construct($n = '0', $u = false)
-    {
-        $this->n = (string) $n;
-        $this->unit = $u !== false ? (string) $u : false;
-    }
-
-    /**
-     * @param string $s Unit string, like '2em' or '3.4in'
-     * @return HTMLPurifier_Length
-     * @warning Does not perform validation.
-     */
-    public static function make($s)
-    {
-        if ($s instanceof HTMLPurifier_Length) {
-            return $s;
-        }
-        $n_length = strspn($s, '1234567890.+-');
-        $n = substr($s, 0, $n_length);
-        $unit = substr($s, $n_length);
-        if ($unit === '') {
-            $unit = false;
-        }
-        return new HTMLPurifier_Length($n, $unit);
-    }
-
-    /**
-     * Validates the number and unit.
-     * @return bool
-     */
-    protected function validate()
-    {
-        // Special case:
-        if ($this->n === '+0' || $this->n === '-0') {
-            $this->n = '0';
-        }
-        if ($this->n === '0' && $this->unit === false) {
-            return true;
-        }
-        if ($this->unit === false || !ctype_lower($this->unit)) {
-            $this->unit = strtolower($this->unit);
-        }
-        if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) {
-            return false;
-        }
-        // Hack:
-        $def = new HTMLPurifier_AttrDef_CSS_Number();
-        $result = $def->validate($this->n, false, false);
-        if ($result === false) {
-            return false;
-        }
-        $this->n = $result;
-        return true;
-    }
-
-    /**
-     * Returns string representation of number.
-     * @return string
-     */
-    public function toString()
-    {
-        if (!$this->isValid()) {
-            return false;
-        }
-        return $this->n . $this->unit;
-    }
-
-    /**
-     * Retrieves string numeric magnitude.
-     * @return string
-     */
-    public function getN()
-    {
-        return $this->n;
-    }
-
-    /**
-     * Retrieves string unit.
-     * @return string
-     */
-    public function getUnit()
-    {
-        return $this->unit;
-    }
-
-    /**
-     * Returns true if this length unit is valid.
-     * @return bool
-     */
-    public function isValid()
-    {
-        if ($this->isValid === null) {
-            $this->isValid = $this->validate();
-        }
-        return $this->isValid;
-    }
-
-    /**
-     * Compares two lengths, and returns 1 if greater, -1 if less and 0 if equal.
-     * @param HTMLPurifier_Length $l
-     * @return int
-     * @warning If both values are too large or small, this calculation will
-     *          not work properly
-     */
-    public function compareTo($l)
-    {
-        if ($l === false) {
-            return false;
-        }
-        if ($l->unit !== $this->unit) {
-            $converter = new HTMLPurifier_UnitConverter();
-            $l = $converter->convert($l, $this->unit);
-            if ($l === false) {
-                return false;
-            }
-        }
-        return $this->n - $l->n;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php
deleted file mode 100644
index c21f364..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php
+++ /dev/null
@@ -1,387 +0,0 @@
-<?php
-
-/**
- * Forgivingly lexes HTML (SGML-style) markup into tokens.
- *
- * A lexer parses a string of SGML-style markup and converts them into
- * corresponding tokens.  It doesn't check for well-formedness, although its
- * internal mechanism may make this automatic (such as the case of
- * HTMLPurifier_Lexer_DOMLex).  There are several implementations to choose
- * from.
- *
- * A lexer is HTML-oriented: it might work with XML, but it's not
- * recommended, as we adhere to a subset of the specification for optimization
- * reasons. This might change in the future. Also, most tokenizers are not
- * expected to handle DTDs or PIs.
- *
- * This class should not be directly instantiated, but you may use create() to
- * retrieve a default copy of the lexer.  Being a supertype, this class
- * does not actually define any implementation, but offers commonly used
- * convenience functions for subclasses.
- *
- * @note The unit tests will instantiate this class for testing purposes, as
- *       many of the utility functions require a class to be instantiated.
- *       This means that, even though this class is not runnable, it will
- *       not be declared abstract.
- *
- * @par
- *
- * @note
- * We use tokens rather than create a DOM representation because DOM would:
- *
- * @par
- *  -# Require more processing and memory to create,
- *  -# Is not streamable, and
- *  -# Has the entire document structure (html and body not needed).
- *
- * @par
- * However, DOM is helpful in that it makes it easy to move around nodes
- * without a lot of lookaheads to see when a tag is closed. This is a
- * limitation of the token system and some workarounds would be nice.
- */
-class HTMLPurifier_Lexer
-{
-
-    /**
-     * Whether or not this lexer implements line-number/column-number tracking.
-     * If it does, set to true.
-     */
-    public $tracksLineNumbers = false;
-
-    /**
-     * @type HTMLPurifier_EntityParser
-     */
-    private $_entity_parser;
-
-    // -- STATIC ----------------------------------------------------------
-
-    /**
-     * Retrieves or sets the default Lexer as a Prototype Factory.
-     *
-     * By default HTMLPurifier_Lexer_DOMLex will be returned. There are
-     * a few exceptions involving special features that only DirectLex
-     * implements.
-     *
-     * @note The behavior of this class has changed, rather than accepting
-     *       a prototype object, it now accepts a configuration object.
-     *       To specify your own prototype, set %Core.LexerImpl to it.
-     *       This change in behavior de-singletonizes the lexer object.
-     *
-     * @param HTMLPurifier_Config $config
-     * @return HTMLPurifier_Lexer
-     * @throws HTMLPurifier_Exception
-     */
-    public static function create($config)
-    {
-        if (!($config instanceof HTMLPurifier_Config)) {
-            $lexer = $config;
-            trigger_error(
-                "Passing a prototype to
-                HTMLPurifier_Lexer::create() is deprecated, please instead
-                use %Core.LexerImpl",
-                E_USER_WARNING
-            );
-        } else {
-            $lexer = $config->get('Core.LexerImpl');
-        }
-
-        $needs_tracking =
-            $config->get('Core.MaintainLineNumbers') ||
-            $config->get('Core.CollectErrors');
-
-        $inst = null;
-        if (is_object($lexer)) {
-            $inst = $lexer;
-        } else {
-            if (is_null($lexer)) {
-                do {
-                    // auto-detection algorithm
-                    if ($needs_tracking) {
-                        $lexer = 'DirectLex';
-                        break;
-                    }
-
-                    if (class_exists('DOMDocument', false) &&
-                        method_exists('DOMDocument', 'loadHTML') &&
-                        !extension_loaded('domxml')
-                    ) {
-                        // check for DOM support, because while it's part of the
-                        // core, it can be disabled compile time. Also, the PECL
-                        // domxml extension overrides the default DOM, and is evil
-                        // and nasty and we shan't bother to support it
-                        $lexer = 'DOMLex';
-                    } else {
-                        $lexer = 'DirectLex';
-                    }
-                } while (0);
-            } // do..while so we can break
-
-            // instantiate recognized string names
-            switch ($lexer) {
-                case 'DOMLex':
-                    $inst = new HTMLPurifier_Lexer_DOMLex();
-                    break;
-                case 'DirectLex':
-                    $inst = new HTMLPurifier_Lexer_DirectLex();
-                    break;
-                case 'PH5P':
-                    $inst = new HTMLPurifier_Lexer_PH5P();
-                    break;
-                default:
-                    throw new HTMLPurifier_Exception(
-                        "Cannot instantiate unrecognized Lexer type " .
-                        htmlspecialchars($lexer)
-                    );
-            }
-        }
-
-        if (!$inst) {
-            throw new HTMLPurifier_Exception('No lexer was instantiated');
-        }
-
-        // once PHP DOM implements native line numbers, or we
-        // hack out something using XSLT, remove this stipulation
-        if ($needs_tracking && !$inst->tracksLineNumbers) {
-            throw new HTMLPurifier_Exception(
-                'Cannot use lexer that does not support line numbers with ' .
-                'Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'
-            );
-        }
-
-        return $inst;
-
-    }
-
-    // -- CONVENIENCE MEMBERS ---------------------------------------------
-
-    public function __construct()
-    {
-        $this->_entity_parser = new HTMLPurifier_EntityParser();
-    }
-
-    /**
-     * Most common entity to raw value conversion table for special entities.
-     * @type array
-     */
-    protected $_special_entity2str =
-        array(
-            '&quot;' => '"',
-            '&amp;' => '&',
-            '&lt;' => '<',
-            '&gt;' => '>',
-            '&#39;' => "'",
-            '&#039;' => "'",
-            '&#x27;' => "'"
-        );
-
-    public function parseText($string, $config) {
-        return $this->parseData($string, false, $config);
-    }
-
-    public function parseAttr($string, $config) {
-        return $this->parseData($string, true, $config);
-    }
-
-    /**
-     * Parses special entities into the proper characters.
-     *
-     * This string will translate escaped versions of the special characters
-     * into the correct ones.
-     *
-     * @param string $string String character data to be parsed.
-     * @return string Parsed character data.
-     */
-    public function parseData($string, $is_attr, $config)
-    {
-        // following functions require at least one character
-        if ($string === '') {
-            return '';
-        }
-
-        // subtracts amps that cannot possibly be escaped
-        $num_amp = substr_count($string, '&') - substr_count($string, '& ') -
-            ($string[strlen($string) - 1] === '&' ? 1 : 0);
-
-        if (!$num_amp) {
-            return $string;
-        } // abort if no entities
-        $num_esc_amp = substr_count($string, '&amp;');
-        $string = strtr($string, $this->_special_entity2str);
-
-        // code duplication for sake of optimization, see above
-        $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -
-            ($string[strlen($string) - 1] === '&' ? 1 : 0);
-
-        if ($num_amp_2 <= $num_esc_amp) {
-            return $string;
-        }
-
-        // hmm... now we have some uncommon entities. Use the callback.
-        if ($config->get('Core.LegacyEntityDecoder')) {
-            $string = $this->_entity_parser->substituteSpecialEntities($string);
-        } else {
-            if ($is_attr) {
-                $string = $this->_entity_parser->substituteAttrEntities($string);
-            } else {
-                $string = $this->_entity_parser->substituteTextEntities($string);
-            }
-        }
-        return $string;
-    }
-
-    /**
-     * Lexes an HTML string into tokens.
-     * @param $string String HTML.
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token[] array representation of HTML.
-     */
-    public function tokenizeHTML($string, $config, $context)
-    {
-        trigger_error('Call to abstract class', E_USER_ERROR);
-    }
-
-    /**
-     * Translates CDATA sections into regular sections (through escaping).
-     * @param string $string HTML string to process.
-     * @return string HTML with CDATA sections escaped.
-     */
-    protected static function escapeCDATA($string)
-    {
-        return preg_replace_callback(
-            '/<!\[CDATA\[(.+?)\]\]>/s',
-            array('HTMLPurifier_Lexer', 'CDATACallback'),
-            $string
-        );
-    }
-
-    /**
-     * Special CDATA case that is especially convoluted for <script>
-     * @param string $string HTML string to process.
-     * @return string HTML with CDATA sections escaped.
-     */
-    protected static function escapeCommentedCDATA($string)
-    {
-        return preg_replace_callback(
-            '#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s',
-            array('HTMLPurifier_Lexer', 'CDATACallback'),
-            $string
-        );
-    }
-
-    /**
-     * Special Internet Explorer conditional comments should be removed.
-     * @param string $string HTML string to process.
-     * @return string HTML with conditional comments removed.
-     */
-    protected static function removeIEConditional($string)
-    {
-        return preg_replace(
-            '#<!--\[if [^>]+\]>.*?<!\[endif\]-->#si', // probably should generalize for all strings
-            '',
-            $string
-        );
-    }
-
-    /**
-     * Callback function for escapeCDATA() that does the work.
-     *
-     * @warning Though this is public in order to let the callback happen,
-     *          calling it directly is not recommended.
-     * @param array $matches PCRE matches array, with index 0 the entire match
-     *                  and 1 the inside of the CDATA section.
-     * @return string Escaped internals of the CDATA section.
-     */
-    protected static function CDATACallback($matches)
-    {
-        // not exactly sure why the character set is needed, but whatever
-        return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
-    }
-
-    /**
-     * Takes a piece of HTML and normalizes it by converting entities, fixing
-     * encoding, extracting bits, and other good stuff.
-     * @param string $html HTML.
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     * @todo Consider making protected
-     */
-    public function normalize($html, $config, $context)
-    {
-        // normalize newlines to \n
-        if ($config->get('Core.NormalizeNewlines')) {
-            $html = str_replace("\r\n", "\n", (string)$html);
-            $html = str_replace("\r", "\n", (string)$html);
-        }
-
-        if ($config->get('HTML.Trusted')) {
-            // escape convoluted CDATA
-            $html = $this->escapeCommentedCDATA($html);
-        }
-
-        // escape CDATA
-        $html = $this->escapeCDATA($html);
-
-        $html = $this->removeIEConditional($html);
-
-        // extract body from document if applicable
-        if ($config->get('Core.ConvertDocumentToFragment')) {
-            $e = false;
-            if ($config->get('Core.CollectErrors')) {
-                $e =& $context->get('ErrorCollector');
-            }
-            $new_html = $this->extractBody($html);
-            if ($e && $new_html != $html) {
-                $e->send(E_WARNING, 'Lexer: Extracted body');
-            }
-            $html = $new_html;
-        }
-
-        // expand entities that aren't the big five
-        if ($config->get('Core.LegacyEntityDecoder')) {
-            $html = $this->_entity_parser->substituteNonSpecialEntities($html);
-        }
-
-        // clean into wellformed UTF-8 string for an SGML context: this has
-        // to be done after entity expansion because the entities sometimes
-        // represent non-SGML characters (horror, horror!)
-        $html = HTMLPurifier_Encoder::cleanUTF8($html);
-
-        // if processing instructions are to removed, remove them now
-        if ($config->get('Core.RemoveProcessingInstructions')) {
-            $html = preg_replace('#<\?.+?\?>#s', '', $html);
-        }
-
-        $hidden_elements = $config->get('Core.HiddenElements');
-        if ($config->get('Core.AggressivelyRemoveScript') &&
-            !($config->get('HTML.Trusted') || !$config->get('Core.RemoveScriptContents')
-            || empty($hidden_elements["script"]))) {
-            $html = preg_replace('#<script[^>]*>.*?</script>#i', '', $html);
-        }
-
-        return $html;
-    }
-
-    /**
-     * Takes a string of HTML (fragment or document) and returns the content
-     * @todo Consider making protected
-     */
-    public function extractBody($html)
-    {
-        $matches = array();
-        $result = preg_match('|(.*?)<body[^>]*>(.*)</body>|is', $html, $matches);
-        if ($result) {
-            // Make sure it's not in a comment
-            $comment_start = strrpos($matches[1], '<!--');
-            $comment_end   = strrpos($matches[1], '-->');
-            if ($comment_start === false ||
-                ($comment_end !== false && $comment_end > $comment_start)) {
-                return $matches[2];
-            }
-        }
-        return $html;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php
deleted file mode 100644
index ca5f25b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DOMLex.php
+++ /dev/null
@@ -1,338 +0,0 @@
-<?php
-
-/**
- * Parser that uses PHP 5's DOM extension (part of the core).
- *
- * In PHP 5, the DOM XML extension was revamped into DOM and added to the core.
- * It gives us a forgiving HTML parser, which we use to transform the HTML
- * into a DOM, and then into the tokens.  It is blazingly fast (for large
- * documents, it performs twenty times faster than
- * HTMLPurifier_Lexer_DirectLex,and is the default choice for PHP 5.
- *
- * @note Any empty elements will have empty tokens associated with them, even if
- * this is prohibited by the spec. This is cannot be fixed until the spec
- * comes into play.
- *
- * @note PHP's DOM extension does not actually parse any entities, we use
- *       our own function to do that.
- *
- * @warning DOM tends to drop whitespace, which may wreak havoc on indenting.
- *          If this is a huge problem, due to the fact that HTML is hand
- *          edited and you are unable to get a parser cache that caches the
- *          the output of HTML Purifier while keeping the original HTML lying
- *          around, you may want to run Tidy on the resulting output or use
- *          HTMLPurifier_DirectLex
- */
-
-class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
-{
-
-    /**
-     * @type HTMLPurifier_TokenFactory
-     */
-    private $factory;
-
-    public function __construct()
-    {
-        // setup the factory
-        parent::__construct();
-        $this->factory = new HTMLPurifier_TokenFactory();
-    }
-
-    /**
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token[]
-     */
-    public function tokenizeHTML($html, $config, $context)
-    {
-        $html = $this->normalize($html, $config, $context);
-
-        // attempt to armor stray angled brackets that cannot possibly
-        // form tags and thus are probably being used as emoticons
-        if ($config->get('Core.AggressivelyFixLt')) {
-            $char = '[^a-z!\/]';
-            $comment = "/<!--(.*?)(-->|\z)/is";
-            $html = preg_replace_callback($comment, array($this, 'callbackArmorCommentEntities'), $html);
-            do {
-                $old = $html;
-                $html = preg_replace("/<($char)/i", '&lt;\\1', $html);
-            } while ($html !== $old);
-            $html = preg_replace_callback($comment, array($this, 'callbackUndoCommentSubst'), $html); // fix comments
-        }
-
-        // preprocess html, essential for UTF-8
-        $html = $this->wrapHTML($html, $config, $context);
-
-        $doc = new DOMDocument();
-        $doc->encoding = 'UTF-8'; // theoretically, the above has this covered
-
-        $options = 0;
-        if ($config->get('Core.AllowParseManyTags') && defined('LIBXML_PARSEHUGE')) {
-            $options |= LIBXML_PARSEHUGE;
-        }
-
-        set_error_handler(array($this, 'muteErrorHandler'));
-        // loadHTML() fails on PHP 5.3 when second parameter is given
-        if ($options) {
-            $doc->loadHTML($html, $options);
-        } else {
-            $doc->loadHTML($html);
-        }
-        restore_error_handler();
-
-        $body = $doc->getElementsByTagName('html')->item(0)-> // <html>
-                      getElementsByTagName('body')->item(0);  // <body>
-
-        $div = $body->getElementsByTagName('div')->item(0); // <div>
-        $tokens = array();
-        $this->tokenizeDOM($div, $tokens, $config);
-        // If the div has a sibling, that means we tripped across
-        // a premature </div> tag.  So remove the div we parsed,
-        // and then tokenize the rest of body.  We can't tokenize
-        // the sibling directly as we'll lose the tags in that case.
-        if ($div->nextSibling) {
-            $body->removeChild($div);
-            $this->tokenizeDOM($body, $tokens, $config);
-        }
-        return $tokens;
-    }
-
-    /**
-     * Iterative function that tokenizes a node, putting it into an accumulator.
-     * To iterate is human, to recurse divine - L. Peter Deutsch
-     * @param DOMNode $node DOMNode to be tokenized.
-     * @param HTMLPurifier_Token[] $tokens   Array-list of already tokenized tokens.
-     * @return HTMLPurifier_Token of node appended to previously passed tokens.
-     */
-    protected function tokenizeDOM($node, &$tokens, $config)
-    {
-        $level = 0;
-        $nodes = array($level => new HTMLPurifier_Queue(array($node)));
-        $closingNodes = array();
-        do {
-            while (!$nodes[$level]->isEmpty()) {
-                $node = $nodes[$level]->shift(); // FIFO
-                $collect = $level > 0 ? true : false;
-                $needEndingTag = $this->createStartNode($node, $tokens, $collect, $config);
-                if ($needEndingTag) {
-                    $closingNodes[$level][] = $node;
-                }
-                if ($node->childNodes && $node->childNodes->length) {
-                    $level++;
-                    $nodes[$level] = new HTMLPurifier_Queue();
-                    foreach ($node->childNodes as $childNode) {
-                        $nodes[$level]->push($childNode);
-                    }
-                }
-            }
-            $level--;
-            if ($level && isset($closingNodes[$level])) {
-                while ($node = array_pop($closingNodes[$level])) {
-                    $this->createEndNode($node, $tokens);
-                }
-            }
-        } while ($level > 0);
-    }
-
-    /**
-     * Portably retrieve the tag name of a node; deals with older versions
-     * of libxml like 2.7.6
-     * @param DOMNode $node
-     */
-    protected function getTagName($node)
-    {
-        if (isset($node->tagName)) {
-            return $node->tagName;
-        } else if (isset($node->nodeName)) {
-            return $node->nodeName;
-        } else if (isset($node->localName)) {
-            return $node->localName;
-        }
-        return null;
-    }
-
-    /**
-     * Portably retrieve the data of a node; deals with older versions
-     * of libxml like 2.7.6
-     * @param DOMNode $node
-     */
-    protected function getData($node)
-    {
-        if (isset($node->data)) {
-            return $node->data;
-        } else if (isset($node->nodeValue)) {
-            return $node->nodeValue;
-        } else if (isset($node->textContent)) {
-            return $node->textContent;
-        }
-        return null;
-    }
-
-
-    /**
-     * @param DOMNode $node DOMNode to be tokenized.
-     * @param HTMLPurifier_Token[] $tokens   Array-list of already tokenized tokens.
-     * @param bool $collect  Says whether or start and close are collected, set to
-     *                    false at first recursion because it's the implicit DIV
-     *                    tag you're dealing with.
-     * @return bool if the token needs an endtoken
-     * @todo data and tagName properties don't seem to exist in DOMNode?
-     */
-    protected function createStartNode($node, &$tokens, $collect, $config)
-    {
-        // intercept non element nodes. WE MUST catch all of them,
-        // but we're not getting the character reference nodes because
-        // those should have been preprocessed
-        if ($node->nodeType === XML_TEXT_NODE) {
-            $data = $this->getData($node); // Handle variable data property
-            if ($data !== null) {
-              $tokens[] = $this->factory->createText($data);
-            }
-            return false;
-        } elseif ($node->nodeType === XML_CDATA_SECTION_NODE) {
-            // undo libxml's special treatment of <script> and <style> tags
-            $last = end($tokens);
-            $data = $node->data;
-            // (note $node->tagname is already normalized)
-            if ($last instanceof HTMLPurifier_Token_Start && ($last->name == 'script' || $last->name == 'style')) {
-                $new_data = trim($data);
-                if (substr($new_data, 0, 4) === '<!--') {
-                    $data = substr($new_data, 4);
-                    if (substr($data, -3) === '-->') {
-                        $data = substr($data, 0, -3);
-                    } else {
-                        // Highly suspicious! Not sure what to do...
-                    }
-                }
-            }
-            $tokens[] = $this->factory->createText($this->parseText($data, $config));
-            return false;
-        } elseif ($node->nodeType === XML_COMMENT_NODE) {
-            // this is code is only invoked for comments in script/style in versions
-            // of libxml pre-2.6.28 (regular comments, of course, are still
-            // handled regularly)
-            $tokens[] = $this->factory->createComment($node->data);
-            return false;
-        } elseif ($node->nodeType !== XML_ELEMENT_NODE) {
-            // not-well tested: there may be other nodes we have to grab
-            return false;
-        }
-        $attr = $node->hasAttributes() ? $this->transformAttrToAssoc($node->attributes) : array();
-        $tag_name = $this->getTagName($node); // Handle variable tagName property
-        if (empty($tag_name)) {
-            return (bool) $node->childNodes->length;
-        }
-        // We still have to make sure that the element actually IS empty
-        if (!$node->childNodes->length) {
-            if ($collect) {
-                $tokens[] = $this->factory->createEmpty($tag_name, $attr);
-            }
-            return false;
-        } else {
-            if ($collect) {
-                $tokens[] = $this->factory->createStart($tag_name, $attr);
-            }
-            return true;
-        }
-    }
-
-    /**
-     * @param DOMNode $node
-     * @param HTMLPurifier_Token[] $tokens
-     */
-    protected function createEndNode($node, &$tokens)
-    {
-        $tag_name = $this->getTagName($node); // Handle variable tagName property
-        $tokens[] = $this->factory->createEnd($tag_name);
-    }
-
-    /**
-     * Converts a DOMNamedNodeMap of DOMAttr objects into an assoc array.
-     *
-     * @param DOMNamedNodeMap $node_map DOMNamedNodeMap of DOMAttr objects.
-     * @return array Associative array of attributes.
-     */
-    protected function transformAttrToAssoc($node_map)
-    {
-        // NamedNodeMap is documented very well, so we're using undocumented
-        // features, namely, the fact that it implements Iterator and
-        // has a ->length attribute
-        if ($node_map->length === 0) {
-            return array();
-        }
-        $array = array();
-        foreach ($node_map as $attr) {
-            $array[$attr->name] = $attr->value;
-        }
-        return $array;
-    }
-
-    /**
-     * An error handler that mutes all errors
-     * @param int $errno
-     * @param string $errstr
-     */
-    public function muteErrorHandler($errno, $errstr)
-    {
-    }
-
-    /**
-     * Callback function for undoing escaping of stray angled brackets
-     * in comments
-     * @param array $matches
-     * @return string
-     */
-    public function callbackUndoCommentSubst($matches)
-    {
-        return '<!--' . strtr($matches[1], array('&amp;' => '&', '&lt;' => '<')) . $matches[2];
-    }
-
-    /**
-     * Callback function that entity-izes ampersands in comments so that
-     * callbackUndoCommentSubst doesn't clobber them
-     * @param array $matches
-     * @return string
-     */
-    public function callbackArmorCommentEntities($matches)
-    {
-        return '<!--' . str_replace('&', '&amp;', $matches[1]) . $matches[2];
-    }
-
-    /**
-     * Wraps an HTML fragment in the necessary HTML
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    protected function wrapHTML($html, $config, $context, $use_div = true)
-    {
-        $def = $config->getDefinition('HTML');
-        $ret = '';
-
-        if (!empty($def->doctype->dtdPublic) || !empty($def->doctype->dtdSystem)) {
-            $ret .= '<!DOCTYPE html ';
-            if (!empty($def->doctype->dtdPublic)) {
-                $ret .= 'PUBLIC "' . $def->doctype->dtdPublic . '" ';
-            }
-            if (!empty($def->doctype->dtdSystem)) {
-                $ret .= '"' . $def->doctype->dtdSystem . '" ';
-            }
-            $ret .= '>';
-        }
-
-        $ret .= '<html><head>';
-        $ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
-        // No protection if $html contains a stray </div>!
-        $ret .= '</head><body>';
-        if ($use_div) $ret .= '<div>';
-        $ret .= $html;
-        if ($use_div) $ret .= '</div>';
-        $ret .= '</body></html>';
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php
deleted file mode 100644
index 6f13089..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/DirectLex.php
+++ /dev/null
@@ -1,539 +0,0 @@
-<?php
-
-/**
- * Our in-house implementation of a parser.
- *
- * A pure PHP parser, DirectLex has absolutely no dependencies, making
- * it a reasonably good default for PHP4.  Written with efficiency in mind,
- * it can be four times faster than HTMLPurifier_Lexer_PEARSax3, although it
- * pales in comparison to HTMLPurifier_Lexer_DOMLex.
- *
- * @todo Reread XML spec and document differences.
- */
-class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
-{
-    /**
-     * @type bool
-     */
-    public $tracksLineNumbers = true;
-
-    /**
-     * Whitespace characters for str(c)spn.
-     * @type string
-     */
-    protected $_whitespace = "\x20\x09\x0D\x0A";
-
-    /**
-     * Callback function for script CDATA fudge
-     * @param array $matches, in form of array(opening tag, contents, closing tag)
-     * @return string
-     */
-    protected function scriptCallback($matches)
-    {
-        return $matches[1] . htmlspecialchars($matches[2], ENT_COMPAT, 'UTF-8') . $matches[3];
-    }
-
-    /**
-     * @param String $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array|HTMLPurifier_Token[]
-     */
-    public function tokenizeHTML($html, $config, $context)
-    {
-        // special normalization for script tags without any armor
-        // our "armor" heurstic is a < sign any number of whitespaces after
-        // the first script tag
-        if ($config->get('HTML.Trusted')) {
-            $html = preg_replace_callback(
-                '#(<script[^>]*>)(\s*[^<].+?)(</script>)#si',
-                array($this, 'scriptCallback'),
-                $html
-            );
-        }
-
-        $html = $this->normalize($html, $config, $context);
-
-        $cursor = 0; // our location in the text
-        $inside_tag = false; // whether or not we're parsing the inside of a tag
-        $array = array(); // result array
-
-        // This is also treated to mean maintain *column* numbers too
-        $maintain_line_numbers = $config->get('Core.MaintainLineNumbers');
-
-        if ($maintain_line_numbers === null) {
-            // automatically determine line numbering by checking
-            // if error collection is on
-            $maintain_line_numbers = $config->get('Core.CollectErrors');
-        }
-
-        if ($maintain_line_numbers) {
-            $current_line = 1;
-            $current_col = 0;
-            $length = strlen($html);
-        } else {
-            $current_line = false;
-            $current_col = false;
-            $length = false;
-        }
-        $context->register('CurrentLine', $current_line);
-        $context->register('CurrentCol', $current_col);
-        $nl = "\n";
-        // how often to manually recalculate. This will ALWAYS be right,
-        // but it's pretty wasteful. Set to 0 to turn off
-        $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval');
-
-        $e = false;
-        if ($config->get('Core.CollectErrors')) {
-            $e =& $context->get('ErrorCollector');
-        }
-
-        // for testing synchronization
-        $loops = 0;
-
-        while (++$loops) {
-            // $cursor is either at the start of a token, or inside of
-            // a tag (i.e. there was a < immediately before it), as indicated
-            // by $inside_tag
-
-            if ($maintain_line_numbers) {
-                // $rcursor, however, is always at the start of a token.
-                $rcursor = $cursor - (int)$inside_tag;
-
-                // Column number is cheap, so we calculate it every round.
-                // We're interested at the *end* of the newline string, so
-                // we need to add strlen($nl) == 1 to $nl_pos before subtracting it
-                // from our "rcursor" position.
-                $nl_pos = strrpos($html, $nl, $rcursor - $length);
-                $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1);
-
-                // recalculate lines
-                if ($synchronize_interval && // synchronization is on
-                    $cursor > 0 && // cursor is further than zero
-                    $loops % $synchronize_interval === 0) { // time to synchronize!
-                    $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor);
-                }
-            }
-
-            $position_next_lt = strpos($html, '<', $cursor);
-            $position_next_gt = strpos($html, '>', $cursor);
-
-            // triggers on "<b>asdf</b>" but not "asdf <b></b>"
-            // special case to set up context
-            if ($position_next_lt === $cursor) {
-                $inside_tag = true;
-                $cursor++;
-            }
-
-            if (!$inside_tag && $position_next_lt !== false) {
-                // We are not inside tag and there still is another tag to parse
-                $token = new
-                HTMLPurifier_Token_Text(
-                    $this->parseText(
-                        substr(
-                            $html,
-                            $cursor,
-                            $position_next_lt - $cursor
-                        ), $config
-                    )
-                );
-                if ($maintain_line_numbers) {
-                    $token->rawPosition($current_line, $current_col);
-                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor);
-                }
-                $array[] = $token;
-                $cursor = $position_next_lt + 1;
-                $inside_tag = true;
-                continue;
-            } elseif (!$inside_tag) {
-                // We are not inside tag but there are no more tags
-                // If we're already at the end, break
-                if ($cursor === strlen($html)) {
-                    break;
-                }
-                // Create Text of rest of string
-                $token = new
-                HTMLPurifier_Token_Text(
-                    $this->parseText(
-                        substr(
-                            $html,
-                            $cursor
-                        ), $config
-                    )
-                );
-                if ($maintain_line_numbers) {
-                    $token->rawPosition($current_line, $current_col);
-                }
-                $array[] = $token;
-                break;
-            } elseif ($inside_tag && $position_next_gt !== false) {
-                // We are in tag and it is well formed
-                // Grab the internals of the tag
-                $strlen_segment = $position_next_gt - $cursor;
-
-                if ($strlen_segment < 1) {
-                    // there's nothing to process!
-                    $token = new HTMLPurifier_Token_Text('<');
-                    $cursor++;
-                    continue;
-                }
-
-                $segment = substr($html, $cursor, $strlen_segment);
-
-                if ($segment === false) {
-                    // somehow, we attempted to access beyond the end of
-                    // the string, defense-in-depth, reported by Nate Abele
-                    break;
-                }
-
-                // Check if it's a comment
-                if (substr($segment, 0, 3) === '!--') {
-                    // re-determine segment length, looking for -->
-                    $position_comment_end = strpos($html, '-->', $cursor);
-                    if ($position_comment_end === false) {
-                        // uh oh, we have a comment that extends to
-                        // infinity. Can't be helped: set comment
-                        // end position to end of string
-                        if ($e) {
-                            $e->send(E_WARNING, 'Lexer: Unclosed comment');
-                        }
-                        $position_comment_end = strlen($html);
-                        $end = true;
-                    } else {
-                        $end = false;
-                    }
-                    $strlen_segment = $position_comment_end - $cursor;
-                    $segment = substr($html, $cursor, $strlen_segment);
-                    $token = new
-                    HTMLPurifier_Token_Comment(
-                        substr(
-                            $segment,
-                            3,
-                            $strlen_segment - 3
-                        )
-                    );
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment);
-                    }
-                    $array[] = $token;
-                    $cursor = $end ? $position_comment_end : $position_comment_end + 3;
-                    $inside_tag = false;
-                    continue;
-                }
-
-                // Check if it's an end tag
-                $is_end_tag = (strpos($segment, '/') === 0);
-                if ($is_end_tag) {
-                    $type = substr($segment, 1);
-                    $token = new HTMLPurifier_Token_End($type);
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                    }
-                    $array[] = $token;
-                    $inside_tag = false;
-                    $cursor = $position_next_gt + 1;
-                    continue;
-                }
-
-                // Check leading character is alnum, if not, we may
-                // have accidently grabbed an emoticon. Translate into
-                // text and go our merry way
-                if (!ctype_alpha($segment[0])) {
-                    // XML:  $segment[0] !== '_' && $segment[0] !== ':'
-                    if ($e) {
-                        $e->send(E_NOTICE, 'Lexer: Unescaped lt');
-                    }
-                    $token = new HTMLPurifier_Token_Text('<');
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                    }
-                    $array[] = $token;
-                    $inside_tag = false;
-                    continue;
-                }
-
-                // Check if it is explicitly self closing, if so, remove
-                // trailing slash. Remember, we could have a tag like <br>, so
-                // any later token processing scripts must convert improperly
-                // classified EmptyTags from StartTags.
-                $is_self_closing = (strrpos($segment, '/') === $strlen_segment - 1);
-                if ($is_self_closing) {
-                    $strlen_segment--;
-                    $segment = substr($segment, 0, $strlen_segment);
-                }
-
-                // Check if there are any attributes
-                $position_first_space = strcspn($segment, $this->_whitespace);
-
-                if ($position_first_space >= $strlen_segment) {
-                    if ($is_self_closing) {
-                        $token = new HTMLPurifier_Token_Empty($segment);
-                    } else {
-                        $token = new HTMLPurifier_Token_Start($segment);
-                    }
-                    if ($maintain_line_numbers) {
-                        $token->rawPosition($current_line, $current_col);
-                        $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                    }
-                    $array[] = $token;
-                    $inside_tag = false;
-                    $cursor = $position_next_gt + 1;
-                    continue;
-                }
-
-                // Grab out all the data
-                $type = substr($segment, 0, $position_first_space);
-                $attribute_string =
-                    trim(
-                        substr(
-                            $segment,
-                            $position_first_space
-                        )
-                    );
-                if ($attribute_string) {
-                    $attr = $this->parseAttributeString(
-                        $attribute_string,
-                        $config,
-                        $context
-                    );
-                } else {
-                    $attr = array();
-                }
-
-                if ($is_self_closing) {
-                    $token = new HTMLPurifier_Token_Empty($type, $attr);
-                } else {
-                    $token = new HTMLPurifier_Token_Start($type, $attr);
-                }
-                if ($maintain_line_numbers) {
-                    $token->rawPosition($current_line, $current_col);
-                    $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor);
-                }
-                $array[] = $token;
-                $cursor = $position_next_gt + 1;
-                $inside_tag = false;
-                continue;
-            } else {
-                // inside tag, but there's no ending > sign
-                if ($e) {
-                    $e->send(E_WARNING, 'Lexer: Missing gt');
-                }
-                $token = new
-                HTMLPurifier_Token_Text(
-                    '<' .
-                    $this->parseText(
-                        substr($html, $cursor), $config
-                    )
-                );
-                if ($maintain_line_numbers) {
-                    $token->rawPosition($current_line, $current_col);
-                }
-                // no cursor scroll? Hmm...
-                $array[] = $token;
-                break;
-            }
-            break;
-        }
-
-        $context->destroy('CurrentLine');
-        $context->destroy('CurrentCol');
-        return $array;
-    }
-
-    /**
-     * PHP 5.0.x compatible substr_count that implements offset and length
-     * @param string $haystack
-     * @param string $needle
-     * @param int $offset
-     * @param int $length
-     * @return int
-     */
-    protected function substrCount($haystack, $needle, $offset, $length)
-    {
-        static $oldVersion;
-        if ($oldVersion === null) {
-            $oldVersion = version_compare(PHP_VERSION, '5.1', '<');
-        }
-        if ($oldVersion) {
-            $haystack = substr($haystack, $offset, $length);
-            return substr_count($haystack, $needle);
-        } else {
-            return substr_count($haystack, $needle, $offset, $length);
-        }
-    }
-
-    /**
-     * Takes the inside of an HTML tag and makes an assoc array of attributes.
-     *
-     * @param string $string Inside of tag excluding name.
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array Assoc array of attributes.
-     */
-    public function parseAttributeString($string, $config, $context)
-    {
-        $string = (string)$string; // quick typecast
-
-        if ($string == '') {
-            return array();
-        } // no attributes
-
-        $e = false;
-        if ($config->get('Core.CollectErrors')) {
-            $e =& $context->get('ErrorCollector');
-        }
-
-        // let's see if we can abort as quickly as possible
-        // one equal sign, no spaces => one attribute
-        $num_equal = substr_count($string, '=');
-        $has_space = strpos($string, ' ');
-        if ($num_equal === 0 && !$has_space) {
-            // bool attribute
-            return array($string => $string);
-        } elseif ($num_equal === 1 && !$has_space) {
-            // only one attribute
-            list($key, $quoted_value) = explode('=', $string);
-            $quoted_value = trim($quoted_value);
-            if (!$key) {
-                if ($e) {
-                    $e->send(E_ERROR, 'Lexer: Missing attribute key');
-                }
-                return array();
-            }
-            if (!$quoted_value) {
-                return array($key => '');
-            }
-            $first_char = @$quoted_value[0];
-            $last_char = @$quoted_value[strlen($quoted_value) - 1];
-
-            $same_quote = ($first_char == $last_char);
-            $open_quote = ($first_char == '"' || $first_char == "'");
-
-            if ($same_quote && $open_quote) {
-                // well behaved
-                $value = substr($quoted_value, 1, strlen($quoted_value) - 2);
-            } else {
-                // not well behaved
-                if ($open_quote) {
-                    if ($e) {
-                        $e->send(E_ERROR, 'Lexer: Missing end quote');
-                    }
-                    $value = substr($quoted_value, 1);
-                } else {
-                    $value = $quoted_value;
-                }
-            }
-            if ($value === false) {
-                $value = '';
-            }
-            return array($key => $this->parseAttr($value, $config));
-        }
-
-        // setup loop environment
-        $array = array(); // return assoc array of attributes
-        $cursor = 0; // current position in string (moves forward)
-        $size = strlen($string); // size of the string (stays the same)
-
-        // if we have unquoted attributes, the parser expects a terminating
-        // space, so let's guarantee that there's always a terminating space.
-        $string .= ' ';
-
-        $old_cursor = -1;
-        while ($cursor < $size) {
-            if ($old_cursor >= $cursor) {
-                throw new Exception("Infinite loop detected");
-            }
-            $old_cursor = $cursor;
-
-            $cursor += ($value = strspn($string, $this->_whitespace, $cursor));
-            // grab the key
-
-            $key_begin = $cursor; //we're currently at the start of the key
-
-            // scroll past all characters that are the key (not whitespace or =)
-            $cursor += strcspn($string, $this->_whitespace . '=', $cursor);
-
-            $key_end = $cursor; // now at the end of the key
-
-            $key = substr($string, $key_begin, $key_end - $key_begin);
-
-            if (!$key) {
-                if ($e) {
-                    $e->send(E_ERROR, 'Lexer: Missing attribute key');
-                }
-                $cursor += 1 + strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop
-                continue; // empty key
-            }
-
-            // scroll past all whitespace
-            $cursor += strspn($string, $this->_whitespace, $cursor);
-
-            if ($cursor >= $size) {
-                $array[$key] = $key;
-                break;
-            }
-
-            // if the next character is an equal sign, we've got a regular
-            // pair, otherwise, it's a bool attribute
-            $first_char = @$string[$cursor];
-
-            if ($first_char == '=') {
-                // key="value"
-
-                $cursor++;
-                $cursor += strspn($string, $this->_whitespace, $cursor);
-
-                if ($cursor === false) {
-                    $array[$key] = '';
-                    break;
-                }
-
-                // we might be in front of a quote right now
-
-                $char = @$string[$cursor];
-
-                if ($char == '"' || $char == "'") {
-                    // it's quoted, end bound is $char
-                    $cursor++;
-                    $value_begin = $cursor;
-                    $cursor = strpos($string, $char, $cursor);
-                    $value_end = $cursor;
-                } else {
-                    // it's not quoted, end bound is whitespace
-                    $value_begin = $cursor;
-                    $cursor += strcspn($string, $this->_whitespace, $cursor);
-                    $value_end = $cursor;
-                }
-
-                // we reached a premature end
-                if ($cursor === false) {
-                    $cursor = $size;
-                    $value_end = $cursor;
-                }
-
-                $value = substr($string, $value_begin, $value_end - $value_begin);
-                if ($value === false) {
-                    $value = '';
-                }
-                $array[$key] = $this->parseAttr($value, $config);
-                $cursor++;
-            } else {
-                // boolattr
-                if ($key !== '') {
-                    $array[$key] = $key;
-                } else {
-                    // purely theoretical
-                    if ($e) {
-                        $e->send(E_ERROR, 'Lexer: Missing attribute key');
-                    }
-                }
-            }
-        }
-        return $array;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php
deleted file mode 100644
index 1564f28..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php
+++ /dev/null
@@ -1,4788 +0,0 @@
-<?php
-
-/**
- * Experimental HTML5-based parser using Jeroen van der Meer's PH5P library.
- * Occupies space in the HTML5 pseudo-namespace, which may cause conflicts.
- *
- * @note
- *    Recent changes to PHP's DOM extension have resulted in some fatal
- *    error conditions with the original version of PH5P. Pending changes,
- *    this lexer will punt to DirectLex if DOM throws an exception.
- */
-
-class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
-{
-    /**
-     * @param string $html
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token[]
-     */
-    public function tokenizeHTML($html, $config, $context)
-    {
-        $new_html = $this->normalize($html, $config, $context);
-        $new_html = $this->wrapHTML($new_html, $config, $context, false /* no div */);
-        try {
-            $parser = new HTML5($new_html);
-            $doc = $parser->save();
-        } catch (DOMException $e) {
-            // Uh oh, it failed. Punt to DirectLex.
-            $lexer = new HTMLPurifier_Lexer_DirectLex();
-            $context->register('PH5PError', $e); // save the error, so we can detect it
-            return $lexer->tokenizeHTML($html, $config, $context); // use original HTML
-        }
-        $tokens = array();
-        $this->tokenizeDOM(
-            $doc->getElementsByTagName('html')->item(0)-> // <html>
-                  getElementsByTagName('body')->item(0) //   <body>
-            ,
-            $tokens, $config
-        );
-        return $tokens;
-    }
-}
-
-/*
-
-Copyright 2007 Jeroen van der Meer <http://jero.net/>
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-
-class HTML5
-{
-    private $data;
-    private $char;
-    private $EOF;
-    private $state;
-    private $tree;
-    private $token;
-    private $content_model;
-    private $escape = false;
-    private $entities = array(
-        'AElig;',
-        'AElig',
-        'AMP;',
-        'AMP',
-        'Aacute;',
-        'Aacute',
-        'Acirc;',
-        'Acirc',
-        'Agrave;',
-        'Agrave',
-        'Alpha;',
-        'Aring;',
-        'Aring',
-        'Atilde;',
-        'Atilde',
-        'Auml;',
-        'Auml',
-        'Beta;',
-        'COPY;',
-        'COPY',
-        'Ccedil;',
-        'Ccedil',
-        'Chi;',
-        'Dagger;',
-        'Delta;',
-        'ETH;',
-        'ETH',
-        'Eacute;',
-        'Eacute',
-        'Ecirc;',
-        'Ecirc',
-        'Egrave;',
-        'Egrave',
-        'Epsilon;',
-        'Eta;',
-        'Euml;',
-        'Euml',
-        'GT;',
-        'GT',
-        'Gamma;',
-        'Iacute;',
-        'Iacute',
-        'Icirc;',
-        'Icirc',
-        'Igrave;',
-        'Igrave',
-        'Iota;',
-        'Iuml;',
-        'Iuml',
-        'Kappa;',
-        'LT;',
-        'LT',
-        'Lambda;',
-        'Mu;',
-        'Ntilde;',
-        'Ntilde',
-        'Nu;',
-        'OElig;',
-        'Oacute;',
-        'Oacute',
-        'Ocirc;',
-        'Ocirc',
-        'Ograve;',
-        'Ograve',
-        'Omega;',
-        'Omicron;',
-        'Oslash;',
-        'Oslash',
-        'Otilde;',
-        'Otilde',
-        'Ouml;',
-        'Ouml',
-        'Phi;',
-        'Pi;',
-        'Prime;',
-        'Psi;',
-        'QUOT;',
-        'QUOT',
-        'REG;',
-        'REG',
-        'Rho;',
-        'Scaron;',
-        'Sigma;',
-        'THORN;',
-        'THORN',
-        'TRADE;',
-        'Tau;',
-        'Theta;',
-        'Uacute;',
-        'Uacute',
-        'Ucirc;',
-        'Ucirc',
-        'Ugrave;',
-        'Ugrave',
-        'Upsilon;',
-        'Uuml;',
-        'Uuml',
-        'Xi;',
-        'Yacute;',
-        'Yacute',
-        'Yuml;',
-        'Zeta;',
-        'aacute;',
-        'aacute',
-        'acirc;',
-        'acirc',
-        'acute;',
-        'acute',
-        'aelig;',
-        'aelig',
-        'agrave;',
-        'agrave',
-        'alefsym;',
-        'alpha;',
-        'amp;',
-        'amp',
-        'and;',
-        'ang;',
-        'apos;',
-        'aring;',
-        'aring',
-        'asymp;',
-        'atilde;',
-        'atilde',
-        'auml;',
-        'auml',
-        'bdquo;',
-        'beta;',
-        'brvbar;',
-        'brvbar',
-        'bull;',
-        'cap;',
-        'ccedil;',
-        'ccedil',
-        'cedil;',
-        'cedil',
-        'cent;',
-        'cent',
-        'chi;',
-        'circ;',
-        'clubs;',
-        'cong;',
-        'copy;',
-        'copy',
-        'crarr;',
-        'cup;',
-        'curren;',
-        'curren',
-        'dArr;',
-        'dagger;',
-        'darr;',
-        'deg;',
-        'deg',
-        'delta;',
-        'diams;',
-        'divide;',
-        'divide',
-        'eacute;',
-        'eacute',
-        'ecirc;',
-        'ecirc',
-        'egrave;',
-        'egrave',
-        'empty;',
-        'emsp;',
-        'ensp;',
-        'epsilon;',
-        'equiv;',
-        'eta;',
-        'eth;',
-        'eth',
-        'euml;',
-        'euml',
-        'euro;',
-        'exist;',
-        'fnof;',
-        'forall;',
-        'frac12;',
-        'frac12',
-        'frac14;',
-        'frac14',
-        'frac34;',
-        'frac34',
-        'frasl;',
-        'gamma;',
-        'ge;',
-        'gt;',
-        'gt',
-        'hArr;',
-        'harr;',
-        'hearts;',
-        'hellip;',
-        'iacute;',
-        'iacute',
-        'icirc;',
-        'icirc',
-        'iexcl;',
-        'iexcl',
-        'igrave;',
-        'igrave',
-        'image;',
-        'infin;',
-        'int;',
-        'iota;',
-        'iquest;',
-        'iquest',
-        'isin;',
-        'iuml;',
-        'iuml',
-        'kappa;',
-        'lArr;',
-        'lambda;',
-        'lang;',
-        'laquo;',
-        'laquo',
-        'larr;',
-        'lceil;',
-        'ldquo;',
-        'le;',
-        'lfloor;',
-        'lowast;',
-        'loz;',
-        'lrm;',
-        'lsaquo;',
-        'lsquo;',
-        'lt;',
-        'lt',
-        'macr;',
-        'macr',
-        'mdash;',
-        'micro;',
-        'micro',
-        'middot;',
-        'middot',
-        'minus;',
-        'mu;',
-        'nabla;',
-        'nbsp;',
-        'nbsp',
-        'ndash;',
-        'ne;',
-        'ni;',
-        'not;',
-        'not',
-        'notin;',
-        'nsub;',
-        'ntilde;',
-        'ntilde',
-        'nu;',
-        'oacute;',
-        'oacute',
-        'ocirc;',
-        'ocirc',
-        'oelig;',
-        'ograve;',
-        'ograve',
-        'oline;',
-        'omega;',
-        'omicron;',
-        'oplus;',
-        'or;',
-        'ordf;',
-        'ordf',
-        'ordm;',
-        'ordm',
-        'oslash;',
-        'oslash',
-        'otilde;',
-        'otilde',
-        'otimes;',
-        'ouml;',
-        'ouml',
-        'para;',
-        'para',
-        'part;',
-        'permil;',
-        'perp;',
-        'phi;',
-        'pi;',
-        'piv;',
-        'plusmn;',
-        'plusmn',
-        'pound;',
-        'pound',
-        'prime;',
-        'prod;',
-        'prop;',
-        'psi;',
-        'quot;',
-        'quot',
-        'rArr;',
-        'radic;',
-        'rang;',
-        'raquo;',
-        'raquo',
-        'rarr;',
-        'rceil;',
-        'rdquo;',
-        'real;',
-        'reg;',
-        'reg',
-        'rfloor;',
-        'rho;',
-        'rlm;',
-        'rsaquo;',
-        'rsquo;',
-        'sbquo;',
-        'scaron;',
-        'sdot;',
-        'sect;',
-        'sect',
-        'shy;',
-        'shy',
-        'sigma;',
-        'sigmaf;',
-        'sim;',
-        'spades;',
-        'sub;',
-        'sube;',
-        'sum;',
-        'sup1;',
-        'sup1',
-        'sup2;',
-        'sup2',
-        'sup3;',
-        'sup3',
-        'sup;',
-        'supe;',
-        'szlig;',
-        'szlig',
-        'tau;',
-        'there4;',
-        'theta;',
-        'thetasym;',
-        'thinsp;',
-        'thorn;',
-        'thorn',
-        'tilde;',
-        'times;',
-        'times',
-        'trade;',
-        'uArr;',
-        'uacute;',
-        'uacute',
-        'uarr;',
-        'ucirc;',
-        'ucirc',
-        'ugrave;',
-        'ugrave',
-        'uml;',
-        'uml',
-        'upsih;',
-        'upsilon;',
-        'uuml;',
-        'uuml',
-        'weierp;',
-        'xi;',
-        'yacute;',
-        'yacute',
-        'yen;',
-        'yen',
-        'yuml;',
-        'yuml',
-        'zeta;',
-        'zwj;',
-        'zwnj;'
-    );
-
-    const PCDATA = 0;
-    const RCDATA = 1;
-    const CDATA = 2;
-    const PLAINTEXT = 3;
-
-    const DOCTYPE = 0;
-    const STARTTAG = 1;
-    const ENDTAG = 2;
-    const COMMENT = 3;
-    const CHARACTR = 4;
-    const EOF = 5;
-
-    public function __construct($data)
-    {
-        $this->data = $data;
-        $this->char = -1;
-        $this->EOF = strlen($data);
-        $this->tree = new HTML5TreeConstructer;
-        $this->content_model = self::PCDATA;
-
-        $this->state = 'data';
-
-        while ($this->state !== null) {
-            $this->{$this->state . 'State'}();
-        }
-    }
-
-    public function save()
-    {
-        return $this->tree->save();
-    }
-
-    private function char()
-    {
-        return ($this->char < $this->EOF)
-            ? $this->data[$this->char]
-            : false;
-    }
-
-    private function character($s, $l = 0)
-    {
-        if ($s + $l < $this->EOF) {
-            if ($l === 0) {
-                return $this->data[$s];
-            } else {
-                return substr($this->data, $s, $l);
-            }
-        }
-    }
-
-    private function characters($char_class, $start)
-    {
-        return preg_replace('#^([' . $char_class . ']+).*#s', '\\1', substr($this->data, $start));
-    }
-
-    private function dataState()
-    {
-        // Consume the next input character
-        $this->char++;
-        $char = $this->char();
-
-        if ($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) {
-            /* U+0026 AMPERSAND (&)
-            When the content model flag is set to one of the PCDATA or RCDATA
-            states: switch to the entity data state. Otherwise: treat it as per
-            the "anything else"    entry below. */
-            $this->state = 'entityData';
-
-        } elseif ($char === '-') {
-            /* If the content model flag is set to either the RCDATA state or
-            the CDATA state, and the escape flag is false, and there are at
-            least three characters before this one in the input stream, and the
-            last four characters in the input stream, including this one, are
-            U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS,
-            and U+002D HYPHEN-MINUS ("<!--"), then set the escape flag to true. */
-            if (($this->content_model === self::RCDATA || $this->content_model ===
-                    self::CDATA) && $this->escape === false &&
-                $this->char >= 3 && $this->character($this->char - 4, 4) === '<!--'
-            ) {
-                $this->escape = true;
-            }
-
-            /* In any case, emit the input character as a character token. Stay
-            in the data state. */
-            $this->emitToken(
-                array(
-                    'type' => self::CHARACTR,
-                    'data' => $char
-                )
-            );
-
-            /* U+003C LESS-THAN SIGN (<) */
-        } elseif ($char === '<' && ($this->content_model === self::PCDATA ||
-                (($this->content_model === self::RCDATA ||
-                        $this->content_model === self::CDATA) && $this->escape === false))
-        ) {
-            /* When the content model flag is set to the PCDATA state: switch
-            to the tag open state.
-
-            When the content model flag is set to either the RCDATA state or
-            the CDATA state and the escape flag is false: switch to the tag
-            open state.
-
-            Otherwise: treat it as per the "anything else" entry below. */
-            $this->state = 'tagOpen';
-
-            /* U+003E GREATER-THAN SIGN (>) */
-        } elseif ($char === '>') {
-            /* If the content model flag is set to either the RCDATA state or
-            the CDATA state, and the escape flag is true, and the last three
-            characters in the input stream including this one are U+002D
-            HYPHEN-MINUS, U+002D HYPHEN-MINUS, U+003E GREATER-THAN SIGN ("-->"),
-            set the escape flag to false. */
-            if (($this->content_model === self::RCDATA ||
-                    $this->content_model === self::CDATA) && $this->escape === true &&
-                $this->character($this->char, 3) === '-->'
-            ) {
-                $this->escape = false;
-            }
-
-            /* In any case, emit the input character as a character token.
-            Stay in the data state. */
-            $this->emitToken(
-                array(
-                    'type' => self::CHARACTR,
-                    'data' => $char
-                )
-            );
-
-        } elseif ($this->char === $this->EOF) {
-            /* EOF
-            Emit an end-of-file token. */
-            $this->EOF();
-
-        } elseif ($this->content_model === self::PLAINTEXT) {
-            /* When the content model flag is set to the PLAINTEXT state
-            THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of
-            the text and emit it as a character token. */
-            $this->emitToken(
-                array(
-                    'type' => self::CHARACTR,
-                    'data' => substr($this->data, $this->char)
-                )
-            );
-
-            $this->EOF();
-
-        } else {
-            /* Anything else
-            THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that
-            otherwise would also be treated as a character token and emit it
-            as a single character token. Stay in the data state. */
-            $len = strcspn($this->data, '<&', $this->char);
-            $char = substr($this->data, $this->char, $len);
-            $this->char += $len - 1;
-
-            $this->emitToken(
-                array(
-                    'type' => self::CHARACTR,
-                    'data' => $char
-                )
-            );
-
-            $this->state = 'data';
-        }
-    }
-
-    private function entityDataState()
-    {
-        // Attempt to consume an entity.
-        $entity = $this->entity();
-
-        // If nothing is returned, emit a U+0026 AMPERSAND character token.
-        // Otherwise, emit the character token that was returned.
-        $char = (!$entity) ? '&' : $entity;
-        $this->emitToken(
-            array(
-                'type' => self::CHARACTR,
-                'data' => $char
-            )
-        );
-
-        // Finally, switch to the data state.
-        $this->state = 'data';
-    }
-
-    private function tagOpenState()
-    {
-        switch ($this->content_model) {
-            case self::RCDATA:
-            case self::CDATA:
-                /* If the next input character is a U+002F SOLIDUS (/) character,
-                consume it and switch to the close tag open state. If the next
-                input character is not a U+002F SOLIDUS (/) character, emit a
-                U+003C LESS-THAN SIGN character token and switch to the data
-                state to process the next input character. */
-                if ($this->character($this->char + 1) === '/') {
-                    $this->char++;
-                    $this->state = 'closeTagOpen';
-
-                } else {
-                    $this->emitToken(
-                        array(
-                            'type' => self::CHARACTR,
-                            'data' => '<'
-                        )
-                    );
-
-                    $this->state = 'data';
-                }
-                break;
-
-            case self::PCDATA:
-                // If the content model flag is set to the PCDATA state
-                // Consume the next input character:
-                $this->char++;
-                $char = $this->char();
-
-                if ($char === '!') {
-                    /* U+0021 EXCLAMATION MARK (!)
-                    Switch to the markup declaration open state. */
-                    $this->state = 'markupDeclarationOpen';
-
-                } elseif ($char === '/') {
-                    /* U+002F SOLIDUS (/)
-                    Switch to the close tag open state. */
-                    $this->state = 'closeTagOpen';
-
-                } elseif (preg_match('/^[A-Za-z]$/', $char)) {
-                    /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
-                    Create a new start tag token, set its tag name to the lowercase
-                    version of the input character (add 0x0020 to the character's code
-                    point), then switch to the tag name state. (Don't emit the token
-                    yet; further details will be filled in before it is emitted.) */
-                    $this->token = array(
-                        'name' => strtolower($char),
-                        'type' => self::STARTTAG,
-                        'attr' => array()
-                    );
-
-                    $this->state = 'tagName';
-
-                } elseif ($char === '>') {
-                    /* U+003E GREATER-THAN SIGN (>)
-                    Parse error. Emit a U+003C LESS-THAN SIGN character token and a
-                    U+003E GREATER-THAN SIGN character token. Switch to the data state. */
-                    $this->emitToken(
-                        array(
-                            'type' => self::CHARACTR,
-                            'data' => '<>'
-                        )
-                    );
-
-                    $this->state = 'data';
-
-                } elseif ($char === '?') {
-                    /* U+003F QUESTION MARK (?)
-                    Parse error. Switch to the bogus comment state. */
-                    $this->state = 'bogusComment';
-
-                } else {
-                    /* Anything else
-                    Parse error. Emit a U+003C LESS-THAN SIGN character token and
-                    reconsume the current input character in the data state. */
-                    $this->emitToken(
-                        array(
-                            'type' => self::CHARACTR,
-                            'data' => '<'
-                        )
-                    );
-
-                    $this->char--;
-                    $this->state = 'data';
-                }
-                break;
-        }
-    }
-
-    private function closeTagOpenState()
-    {
-        $next_node = strtolower($this->characters('A-Za-z', $this->char + 1));
-        $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName;
-
-        if (($this->content_model === self::RCDATA || $this->content_model === self::CDATA) &&
-            (!$the_same || ($the_same && (!preg_match(
-                            '/[\t\n\x0b\x0c >\/]/',
-                            $this->character($this->char + 1 + strlen($next_node))
-                        ) || $this->EOF === $this->char)))
-        ) {
-            /* If the content model flag is set to the RCDATA or CDATA states then
-            examine the next few characters. If they do not match the tag name of
-            the last start tag token emitted (case insensitively), or if they do but
-            they are not immediately followed by one of the following characters:
-                * U+0009 CHARACTER TABULATION
-                * U+000A LINE FEED (LF)
-                * U+000B LINE TABULATION
-                * U+000C FORM FEED (FF)
-                * U+0020 SPACE
-                * U+003E GREATER-THAN SIGN (>)
-                * U+002F SOLIDUS (/)
-                * EOF
-            ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character
-            token, a U+002F SOLIDUS character token, and switch to the data state
-            to process the next input character. */
-            $this->emitToken(
-                array(
-                    'type' => self::CHARACTR,
-                    'data' => '</'
-                )
-            );
-
-            $this->state = 'data';
-
-        } else {
-            /* Otherwise, if the content model flag is set to the PCDATA state,
-            or if the next few characters do match that tag name, consume the
-            next input character: */
-            $this->char++;
-            $char = $this->char();
-
-            if (preg_match('/^[A-Za-z]$/', $char)) {
-                /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z
-                Create a new end tag token, set its tag name to the lowercase version
-                of the input character (add 0x0020 to the character's code point), then
-                switch to the tag name state. (Don't emit the token yet; further details
-                will be filled in before it is emitted.) */
-                $this->token = array(
-                    'name' => strtolower($char),
-                    'type' => self::ENDTAG
-                );
-
-                $this->state = 'tagName';
-
-            } elseif ($char === '>') {
-                /* U+003E GREATER-THAN SIGN (>)
-                Parse error. Switch to the data state. */
-                $this->state = 'data';
-
-            } elseif ($this->char === $this->EOF) {
-                /* EOF
-                Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F
-                SOLIDUS character token. Reconsume the EOF character in the data state. */
-                $this->emitToken(
-                    array(
-                        'type' => self::CHARACTR,
-                        'data' => '</'
-                    )
-                );
-
-                $this->char--;
-                $this->state = 'data';
-
-            } else {
-                /* Parse error. Switch to the bogus comment state. */
-                $this->state = 'bogusComment';
-            }
-        }
-    }
-
-    private function tagNameState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif ($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } elseif ($char === '/') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Switch to the before
-            attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current tag token's tag name.
-            Stay in the tag name state. */
-            $this->token['name'] .= strtolower($char);
-            $this->state = 'tagName';
-        }
-    }
-
-    private function beforeAttributeNameState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif ($char === '/') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Stay in the before
-            attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Start a new attribute in the current tag token. Set that attribute's
-            name to the current input character, and its value to the empty string.
-            Switch to the attribute name state. */
-            $this->token['attr'][] = array(
-                'name' => strtolower($char),
-                'value' => null
-            );
-
-            $this->state = 'attributeName';
-        }
-    }
-
-    private function attributeNameState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the before attribute name state. */
-            $this->state = 'afterAttributeName';
-
-        } elseif ($char === '=') {
-            /* U+003D EQUALS SIGN (=)
-            Switch to the before attribute value state. */
-            $this->state = 'beforeAttributeValue';
-
-        } elseif ($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif ($char === '/' && $this->character($this->char + 1) !== '>') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Switch to the before
-            attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's name.
-            Stay in the attribute name state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['name'] .= strtolower($char);
-
-            $this->state = 'attributeName';
-        }
-    }
-
-    private function afterAttributeNameState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the after attribute name state. */
-            $this->state = 'afterAttributeName';
-
-        } elseif ($char === '=') {
-            /* U+003D EQUALS SIGN (=)
-            Switch to the before attribute value state. */
-            $this->state = 'beforeAttributeValue';
-
-        } elseif ($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif ($char === '/' && $this->character($this->char + 1) !== '>') {
-            /* U+002F SOLIDUS (/)
-            Parse error unless this is a permitted slash. Switch to the
-            before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the EOF
-            character in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Start a new attribute in the current tag token. Set that attribute's
-            name to the current input character, and its value to the empty string.
-            Switch to the attribute name state. */
-            $this->token['attr'][] = array(
-                'name' => strtolower($char),
-                'value' => null
-            );
-
-            $this->state = 'attributeName';
-        }
-    }
-
-    private function beforeAttributeValueState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Stay in the before attribute value state. */
-            $this->state = 'beforeAttributeValue';
-
-        } elseif ($char === '"') {
-            /* U+0022 QUOTATION MARK (")
-            Switch to the attribute value (double-quoted) state. */
-            $this->state = 'attributeValueDoubleQuoted';
-
-        } elseif ($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the attribute value (unquoted) state and reconsume
-            this input character. */
-            $this->char--;
-            $this->state = 'attributeValueUnquoted';
-
-        } elseif ($char === '\'') {
-            /* U+0027 APOSTROPHE (')
-            Switch to the attribute value (single-quoted) state. */
-            $this->state = 'attributeValueSingleQuoted';
-
-        } elseif ($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Switch to the attribute value (unquoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueUnquoted';
-        }
-    }
-
-    private function attributeValueDoubleQuotedState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if ($char === '"') {
-            /* U+0022 QUOTATION MARK (")
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the entity in attribute value state. */
-            $this->entityInAttributeValueState('double');
-
-        } elseif ($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the character
-            in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Stay in the attribute value (double-quoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueDoubleQuoted';
-        }
-    }
-
-    private function attributeValueSingleQuotedState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if ($char === '\'') {
-            /* U+0022 QUOTATION MARK (')
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the entity in attribute value state. */
-            $this->entityInAttributeValueState('single');
-
-        } elseif ($this->char === $this->EOF) {
-            /* EOF
-            Parse error. Emit the current tag token. Reconsume the character
-            in the data state. */
-            $this->emitToken($this->token);
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Stay in the attribute value (single-quoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueSingleQuoted';
-        }
-    }
-
-    private function attributeValueUnquotedState()
-    {
-        // Consume the next input character:
-        $this->char++;
-        $char = $this->character($this->char);
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            /* U+0009 CHARACTER TABULATION
-            U+000A LINE FEED (LF)
-            U+000B LINE TABULATION
-            U+000C FORM FEED (FF)
-            U+0020 SPACE
-            Switch to the before attribute name state. */
-            $this->state = 'beforeAttributeName';
-
-        } elseif ($char === '&') {
-            /* U+0026 AMPERSAND (&)
-            Switch to the entity in attribute value state. */
-            $this->entityInAttributeValueState();
-
-        } elseif ($char === '>') {
-            /* U+003E GREATER-THAN SIGN (>)
-            Emit the current tag token. Switch to the data state. */
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } else {
-            /* Anything else
-            Append the current input character to the current attribute's value.
-            Stay in the attribute value (unquoted) state. */
-            $last = count($this->token['attr']) - 1;
-            $this->token['attr'][$last]['value'] .= $char;
-
-            $this->state = 'attributeValueUnquoted';
-        }
-    }
-
-    private function entityInAttributeValueState()
-    {
-        // Attempt to consume an entity.
-        $entity = $this->entity();
-
-        // If nothing is returned, append a U+0026 AMPERSAND character to the
-        // current attribute's value. Otherwise, emit the character token that
-        // was returned.
-        $char = (!$entity)
-            ? '&'
-            : $entity;
-
-        $last = count($this->token['attr']) - 1;
-        $this->token['attr'][$last]['value'] .= $char;
-    }
-
-    private function bogusCommentState()
-    {
-        /* Consume every character up to the first U+003E GREATER-THAN SIGN
-        character (>) or the end of the file (EOF), whichever comes first. Emit
-        a comment token whose data is the concatenation of all the characters
-        starting from and including the character that caused the state machine
-        to switch into the bogus comment state, up to and including the last
-        consumed character before the U+003E character, if any, or up to the
-        end of the file otherwise. (If the comment was started by the end of
-        the file (EOF), the token is empty.) */
-        $data = $this->characters('^>', $this->char);
-        $this->emitToken(
-            array(
-                'data' => $data,
-                'type' => self::COMMENT
-            )
-        );
-
-        $this->char += strlen($data);
-
-        /* Switch to the data state. */
-        $this->state = 'data';
-
-        /* If the end of the file was reached, reconsume the EOF character. */
-        if ($this->char === $this->EOF) {
-            $this->char = $this->EOF - 1;
-        }
-    }
-
-    private function markupDeclarationOpenState()
-    {
-        /* If the next two characters are both U+002D HYPHEN-MINUS (-)
-        characters, consume those two characters, create a comment token whose
-        data is the empty string, and switch to the comment state. */
-        if ($this->character($this->char + 1, 2) === '--') {
-            $this->char += 2;
-            $this->state = 'comment';
-            $this->token = array(
-                'data' => null,
-                'type' => self::COMMENT
-            );
-
-            /* Otherwise if the next seven chacacters are a case-insensitive match
-            for the word "DOCTYPE", then consume those characters and switch to the
-            DOCTYPE state. */
-        } elseif (strtolower($this->character($this->char + 1, 7)) === 'doctype') {
-            $this->char += 7;
-            $this->state = 'doctype';
-
-            /* Otherwise, is is a parse error. Switch to the bogus comment state.
-            The next character that is consumed, if any, is the first character
-            that will be in the comment. */
-        } else {
-            $this->char++;
-            $this->state = 'bogusComment';
-        }
-    }
-
-    private function commentState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        /* U+002D HYPHEN-MINUS (-) */
-        if ($char === '-') {
-            /* Switch to the comment dash state  */
-            $this->state = 'commentDash';
-
-            /* EOF */
-        } elseif ($this->char === $this->EOF) {
-            /* Parse error. Emit the comment token. Reconsume the EOF character
-            in the data state. */
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-            /* Anything else */
-        } else {
-            /* Append the input character to the comment token's data. Stay in
-            the comment state. */
-            $this->token['data'] .= $char;
-        }
-    }
-
-    private function commentDashState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        /* U+002D HYPHEN-MINUS (-) */
-        if ($char === '-') {
-            /* Switch to the comment end state  */
-            $this->state = 'commentEnd';
-
-            /* EOF */
-        } elseif ($this->char === $this->EOF) {
-            /* Parse error. Emit the comment token. Reconsume the EOF character
-            in the data state. */
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-            /* Anything else */
-        } else {
-            /* Append a U+002D HYPHEN-MINUS (-) character and the input
-            character to the comment token's data. Switch to the comment state. */
-            $this->token['data'] .= '-' . $char;
-            $this->state = 'comment';
-        }
-    }
-
-    private function commentEndState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if ($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif ($char === '-') {
-            $this->token['data'] .= '-';
-
-        } elseif ($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token['data'] .= '--' . $char;
-            $this->state = 'comment';
-        }
-    }
-
-    private function doctypeState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            $this->state = 'beforeDoctypeName';
-
-        } else {
-            $this->char--;
-            $this->state = 'beforeDoctypeName';
-        }
-    }
-
-    private function beforeDoctypeNameState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            // Stay in the before DOCTYPE name state.
-
-        } elseif (preg_match('/^[a-z]$/', $char)) {
-            $this->token = array(
-                'name' => strtoupper($char),
-                'type' => self::DOCTYPE,
-                'error' => true
-            );
-
-            $this->state = 'doctypeName';
-
-        } elseif ($char === '>') {
-            $this->emitToken(
-                array(
-                    'name' => null,
-                    'type' => self::DOCTYPE,
-                    'error' => true
-                )
-            );
-
-            $this->state = 'data';
-
-        } elseif ($this->char === $this->EOF) {
-            $this->emitToken(
-                array(
-                    'name' => null,
-                    'type' => self::DOCTYPE,
-                    'error' => true
-                )
-            );
-
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token = array(
-                'name' => $char,
-                'type' => self::DOCTYPE,
-                'error' => true
-            );
-
-            $this->state = 'doctypeName';
-        }
-    }
-
-    private function doctypeNameState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            $this->state = 'AfterDoctypeName';
-
-        } elseif ($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif (preg_match('/^[a-z]$/', $char)) {
-            $this->token['name'] .= strtoupper($char);
-
-        } elseif ($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token['name'] .= $char;
-        }
-
-        $this->token['error'] = ($this->token['name'] === 'HTML')
-            ? false
-            : true;
-    }
-
-    private function afterDoctypeNameState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) {
-            // Stay in the DOCTYPE name state.
-
-        } elseif ($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif ($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            $this->token['error'] = true;
-            $this->state = 'bogusDoctype';
-        }
-    }
-
-    private function bogusDoctypeState()
-    {
-        /* Consume the next input character: */
-        $this->char++;
-        $char = $this->char();
-
-        if ($char === '>') {
-            $this->emitToken($this->token);
-            $this->state = 'data';
-
-        } elseif ($this->char === $this->EOF) {
-            $this->emitToken($this->token);
-            $this->char--;
-            $this->state = 'data';
-
-        } else {
-            // Stay in the bogus DOCTYPE state.
-        }
-    }
-
-    private function entity()
-    {
-        $start = $this->char;
-
-        // This section defines how to consume an entity. This definition is
-        // used when parsing entities in text and in attributes.
-
-        // The behaviour depends on the identity of the next character (the
-        // one immediately after the U+0026 AMPERSAND character):
-
-        switch ($this->character($this->char + 1)) {
-            // U+0023 NUMBER SIGN (#)
-            case '#':
-
-                // The behaviour further depends on the character after the
-                // U+0023 NUMBER SIGN:
-                switch ($this->character($this->char + 1)) {
-                    // U+0078 LATIN SMALL LETTER X
-                    // U+0058 LATIN CAPITAL LETTER X
-                    case 'x':
-                    case 'X':
-                        // Follow the steps below, but using the range of
-                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
-                        // NINE, U+0061 LATIN SMALL LETTER A through to U+0066
-                        // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER
-                        // A, through to U+0046 LATIN CAPITAL LETTER F (in other
-                        // words, 0-9, A-F, a-f).
-                        $char = 1;
-                        $char_class = '0-9A-Fa-f';
-                        break;
-
-                    // Anything else
-                    default:
-                        // Follow the steps below, but using the range of
-                        // characters U+0030 DIGIT ZERO through to U+0039 DIGIT
-                        // NINE (i.e. just 0-9).
-                        $char = 0;
-                        $char_class = '0-9';
-                        break;
-                }
-
-                // Consume as many characters as match the range of characters
-                // given above.
-                $this->char++;
-                $e_name = $this->characters($char_class, $this->char + $char + 1);
-                $entity = $this->character($start, $this->char);
-                $cond = strlen($e_name) > 0;
-
-                // The rest of the parsing happens below.
-                break;
-
-            // Anything else
-            default:
-                // Consume the maximum number of characters possible, with the
-                // consumed characters case-sensitively matching one of the
-                // identifiers in the first column of the entities table.
-
-                $e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
-                $len = strlen($e_name);
-
-                for ($c = 1; $c <= $len; $c++) {
-                    $id = substr($e_name, 0, $c);
-                    $this->char++;
-
-                    if (in_array($id, $this->entities)) {
-                        if ($e_name[$c - 1] !== ';') {
-                            if ($c < $len && $e_name[$c] == ';') {
-                                $this->char++; // consume extra semicolon
-                            }
-                        }
-                        $entity = $id;
-                        break;
-                    }
-                }
-
-                $cond = isset($entity);
-                // The rest of the parsing happens below.
-                break;
-        }
-
-        if (!$cond) {
-            // If no match can be made, then this is a parse error. No
-            // characters are consumed, and nothing is returned.
-            $this->char = $start;
-            return false;
-        }
-
-        // Return a character token for the character corresponding to the
-        // entity name (as given by the second column of the entities table).
-        return html_entity_decode('&' . rtrim($entity, ';') . ';', ENT_QUOTES, 'UTF-8');
-    }
-
-    private function emitToken($token)
-    {
-        $emit = $this->tree->emitToken($token);
-
-        if (is_int($emit)) {
-            $this->content_model = $emit;
-
-        } elseif ($token['type'] === self::ENDTAG) {
-            $this->content_model = self::PCDATA;
-        }
-    }
-
-    private function EOF()
-    {
-        $this->state = null;
-        $this->tree->emitToken(
-            array(
-                'type' => self::EOF
-            )
-        );
-    }
-}
-
-class HTML5TreeConstructer
-{
-    public $stack = array();
-
-    private $phase;
-    private $mode;
-    private $dom;
-    private $foster_parent = null;
-    private $a_formatting = array();
-
-    private $head_pointer = null;
-    private $form_pointer = null;
-
-    private $scoping = array('button', 'caption', 'html', 'marquee', 'object', 'table', 'td', 'th');
-    private $formatting = array(
-        'a',
-        'b',
-        'big',
-        'em',
-        'font',
-        'i',
-        'nobr',
-        's',
-        'small',
-        'strike',
-        'strong',
-        'tt',
-        'u'
-    );
-    private $special = array(
-        'address',
-        'area',
-        'base',
-        'basefont',
-        'bgsound',
-        'blockquote',
-        'body',
-        'br',
-        'center',
-        'col',
-        'colgroup',
-        'dd',
-        'dir',
-        'div',
-        'dl',
-        'dt',
-        'embed',
-        'fieldset',
-        'form',
-        'frame',
-        'frameset',
-        'h1',
-        'h2',
-        'h3',
-        'h4',
-        'h5',
-        'h6',
-        'head',
-        'hr',
-        'iframe',
-        'image',
-        'img',
-        'input',
-        'isindex',
-        'li',
-        'link',
-        'listing',
-        'menu',
-        'meta',
-        'noembed',
-        'noframes',
-        'noscript',
-        'ol',
-        'optgroup',
-        'option',
-        'p',
-        'param',
-        'plaintext',
-        'pre',
-        'script',
-        'select',
-        'spacer',
-        'style',
-        'tbody',
-        'textarea',
-        'tfoot',
-        'thead',
-        'title',
-        'tr',
-        'ul',
-        'wbr'
-    );
-
-    // The different phases.
-    const INIT_PHASE = 0;
-    const ROOT_PHASE = 1;
-    const MAIN_PHASE = 2;
-    const END_PHASE = 3;
-
-    // The different insertion modes for the main phase.
-    const BEFOR_HEAD = 0;
-    const IN_HEAD = 1;
-    const AFTER_HEAD = 2;
-    const IN_BODY = 3;
-    const IN_TABLE = 4;
-    const IN_CAPTION = 5;
-    const IN_CGROUP = 6;
-    const IN_TBODY = 7;
-    const IN_ROW = 8;
-    const IN_CELL = 9;
-    const IN_SELECT = 10;
-    const AFTER_BODY = 11;
-    const IN_FRAME = 12;
-    const AFTR_FRAME = 13;
-
-    // The different types of elements.
-    const SPECIAL = 0;
-    const SCOPING = 1;
-    const FORMATTING = 2;
-    const PHRASING = 3;
-
-    const MARKER = 0;
-
-    public function __construct()
-    {
-        $this->phase = self::INIT_PHASE;
-        $this->mode = self::BEFOR_HEAD;
-        $this->dom = new DOMDocument;
-
-        $this->dom->encoding = 'UTF-8';
-        $this->dom->preserveWhiteSpace = true;
-        $this->dom->substituteEntities = true;
-        $this->dom->strictErrorChecking = false;
-    }
-
-    // Process tag tokens
-    public function emitToken($token)
-    {
-        switch ($this->phase) {
-            case self::INIT_PHASE:
-                return $this->initPhase($token);
-                break;
-            case self::ROOT_PHASE:
-                return $this->rootElementPhase($token);
-                break;
-            case self::MAIN_PHASE:
-                return $this->mainPhase($token);
-                break;
-            case self::END_PHASE :
-                return $this->trailingEndPhase($token);
-                break;
-        }
-    }
-
-    private function initPhase($token)
-    {
-        /* Initially, the tree construction stage must handle each token
-        emitted from the tokenisation stage as follows: */
-
-        /* A DOCTYPE token that is marked as being in error
-        A comment token
-        A start tag token
-        An end tag token
-        A character token that is not one of one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-            or U+0020 SPACE
-        An end-of-file token */
-        if ((isset($token['error']) && $token['error']) ||
-            $token['type'] === HTML5::COMMENT ||
-            $token['type'] === HTML5::STARTTAG ||
-            $token['type'] === HTML5::ENDTAG ||
-            $token['type'] === HTML5::EOF ||
-            ($token['type'] === HTML5::CHARACTR && isset($token['data']) &&
-                !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))
-        ) {
-            /* This specification does not define how to handle this case. In
-            particular, user agents may ignore the entirety of this specification
-            altogether for such documents, and instead invoke special parse modes
-            with a greater emphasis on backwards compatibility. */
-
-            $this->phase = self::ROOT_PHASE;
-            return $this->rootElementPhase($token);
-
-            /* A DOCTYPE token marked as being correct */
-        } elseif (isset($token['error']) && !$token['error']) {
-            /* Append a DocumentType node to the Document  node, with the name
-            attribute set to the name given in the DOCTYPE token (which will be
-            "HTML"), and the other attributes specific to DocumentType objects
-            set to null, empty lists, or the empty string as appropriate. */
-            $doctype = new DOMDocumentType(null, null, 'HTML');
-
-            /* Then, switch to the root element phase of the tree construction
-            stage. */
-            $this->phase = self::ROOT_PHASE;
-
-            /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-            or U+0020 SPACE */
-        } elseif (isset($token['data']) && preg_match(
-                '/^[\t\n\x0b\x0c ]+$/',
-                $token['data']
-            )
-        ) {
-            /* Append that character  to the Document node. */
-            $text = $this->dom->createTextNode($token['data']);
-            $this->dom->appendChild($text);
-        }
-    }
-
-    private function rootElementPhase($token)
-    {
-        /* After the initial phase, as each token is emitted from the tokenisation
-        stage, it must be processed as described in this section. */
-
-        /* A DOCTYPE token */
-        if ($token['type'] === HTML5::DOCTYPE) {
-            // Parse error. Ignore the token.
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the Document object with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            $this->dom->appendChild($comment);
-
-            /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-            or U+0020 SPACE */
-        } elseif ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Append that character  to the Document node. */
-            $text = $this->dom->createTextNode($token['data']);
-            $this->dom->appendChild($text);
-
-            /* A character token that is not one of U+0009 CHARACTER TABULATION,
-                U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED
-                (FF), or U+0020 SPACE
-            A start tag token
-            An end tag token
-            An end-of-file token */
-        } elseif (($token['type'] === HTML5::CHARACTR &&
-                !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
-            $token['type'] === HTML5::STARTTAG ||
-            $token['type'] === HTML5::ENDTAG ||
-            $token['type'] === HTML5::EOF
-        ) {
-            /* Create an HTMLElement node with the tag name html, in the HTML
-            namespace. Append it to the Document object. Switch to the main
-            phase and reprocess the current token. */
-            $html = $this->dom->createElement('html');
-            $this->dom->appendChild($html);
-            $this->stack[] = $html;
-
-            $this->phase = self::MAIN_PHASE;
-            return $this->mainPhase($token);
-        }
-    }
-
-    private function mainPhase($token)
-    {
-        /* Tokens in the main phase must be handled as follows: */
-
-        /* A DOCTYPE token */
-        if ($token['type'] === HTML5::DOCTYPE) {
-            // Parse error. Ignore the token.
-
-            /* A start tag token with the tag name "html" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') {
-            /* If this start tag token was not the first start tag token, then
-            it is a parse error. */
-
-            /* For each attribute on the token, check to see if the attribute
-            is already present on the top element of the stack of open elements.
-            If it is not, add the attribute and its corresponding value to that
-            element. */
-            foreach ($token['attr'] as $attr) {
-                if (!$this->stack[0]->hasAttribute($attr['name'])) {
-                    $this->stack[0]->setAttribute($attr['name'], $attr['value']);
-                }
-            }
-
-            /* An end-of-file token */
-        } elseif ($token['type'] === HTML5::EOF) {
-            /* Generate implied end tags. */
-            $this->generateImpliedEndTags();
-
-            /* Anything else. */
-        } else {
-            /* Depends on the insertion mode: */
-            switch ($this->mode) {
-                case self::BEFOR_HEAD:
-                    return $this->beforeHead($token);
-                    break;
-                case self::IN_HEAD:
-                    return $this->inHead($token);
-                    break;
-                case self::AFTER_HEAD:
-                    return $this->afterHead($token);
-                    break;
-                case self::IN_BODY:
-                    return $this->inBody($token);
-                    break;
-                case self::IN_TABLE:
-                    return $this->inTable($token);
-                    break;
-                case self::IN_CAPTION:
-                    return $this->inCaption($token);
-                    break;
-                case self::IN_CGROUP:
-                    return $this->inColumnGroup($token);
-                    break;
-                case self::IN_TBODY:
-                    return $this->inTableBody($token);
-                    break;
-                case self::IN_ROW:
-                    return $this->inRow($token);
-                    break;
-                case self::IN_CELL:
-                    return $this->inCell($token);
-                    break;
-                case self::IN_SELECT:
-                    return $this->inSelect($token);
-                    break;
-                case self::AFTER_BODY:
-                    return $this->afterBody($token);
-                    break;
-                case self::IN_FRAME:
-                    return $this->inFrameset($token);
-                    break;
-                case self::AFTR_FRAME:
-                    return $this->afterFrameset($token);
-                    break;
-                case self::END_PHASE:
-                    return $this->trailingEndPhase($token);
-                    break;
-            }
-        }
-    }
-
-    private function beforeHead($token)
-    {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data attribute
-            set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-            /* A start tag token with the tag name "head" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') {
-            /* Create an element for the token, append the new element to the
-            current node and push it onto the stack of open elements. */
-            $element = $this->insertElement($token);
-
-            /* Set the head element pointer to this new element node. */
-            $this->head_pointer = $element;
-
-            /* Change the insertion mode to "in head". */
-            $this->mode = self::IN_HEAD;
-
-            /* A start tag token whose tag name is one of: "base", "link", "meta",
-            "script", "style", "title". Or an end tag with the tag name "html".
-            Or a character token that is not one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-            or U+0020 SPACE. Or any other start tag token */
-        } elseif ($token['type'] === HTML5::STARTTAG ||
-            ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') ||
-            ($token['type'] === HTML5::CHARACTR && !preg_match(
-                    '/^[\t\n\x0b\x0c ]$/',
-                    $token['data']
-                ))
-        ) {
-            /* Act as if a start tag token with the tag name "head" and no
-            attributes had been seen, then reprocess the current token. */
-            $this->beforeHead(
-                array(
-                    'name' => 'head',
-                    'type' => HTML5::STARTTAG,
-                    'attr' => array()
-                )
-            );
-
-            return $this->inHead($token);
-
-            /* Any other end tag */
-        } elseif ($token['type'] === HTML5::ENDTAG) {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function inHead($token)
-    {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE.
-
-        THIS DIFFERS FROM THE SPEC: If the current node is either a title, style
-        or script element, append the character to the current node regardless
-        of its content. */
-        if (($token['type'] === HTML5::CHARACTR &&
-                preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || (
-                $token['type'] === HTML5::CHARACTR && in_array(
-                    end($this->stack)->nodeName,
-                    array('title', 'style', 'script')
-                ))
-        ) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data attribute
-            set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            in_array($token['name'], array('title', 'style', 'script'))
-        ) {
-            array_pop($this->stack);
-            return HTML5::PCDATA;
-
-            /* A start tag with the tag name "title" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') {
-            /* Create an element for the token and append the new element to the
-            node pointed to by the head element pointer, or, if that is null
-            (innerHTML case), to the current node. */
-            if ($this->head_pointer !== null) {
-                $element = $this->insertElement($token, false);
-                $this->head_pointer->appendChild($element);
-
-            } else {
-                $element = $this->insertElement($token);
-            }
-
-            /* Switch the tokeniser's content model flag  to the RCDATA state. */
-            return HTML5::RCDATA;
-
-            /* A start tag with the tag name "style" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') {
-            /* Create an element for the token and append the new element to the
-            node pointed to by the head element pointer, or, if that is null
-            (innerHTML case), to the current node. */
-            if ($this->head_pointer !== null) {
-                $element = $this->insertElement($token, false);
-                $this->head_pointer->appendChild($element);
-
-            } else {
-                $this->insertElement($token);
-            }
-
-            /* Switch the tokeniser's content model flag  to the CDATA state. */
-            return HTML5::CDATA;
-
-            /* A start tag with the tag name "script" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') {
-            /* Create an element for the token. */
-            $element = $this->insertElement($token, false);
-            $this->head_pointer->appendChild($element);
-
-            /* Switch the tokeniser's content model flag  to the CDATA state. */
-            return HTML5::CDATA;
-
-            /* A start tag with the tag name "base", "link", or "meta" */
-        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
-                $token['name'],
-                array('base', 'link', 'meta')
-            )
-        ) {
-            /* Create an element for the token and append the new element to the
-            node pointed to by the head element pointer, or, if that is null
-            (innerHTML case), to the current node. */
-            if ($this->head_pointer !== null) {
-                $element = $this->insertElement($token, false);
-                $this->head_pointer->appendChild($element);
-                array_pop($this->stack);
-
-            } else {
-                $this->insertElement($token);
-            }
-
-            /* An end tag with the tag name "head" */
-        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') {
-            /* If the current node is a head element, pop the current node off
-            the stack of open elements. */
-            if ($this->head_pointer->isSameNode(end($this->stack))) {
-                array_pop($this->stack);
-
-                /* Otherwise, this is a parse error. */
-            } else {
-                // k
-            }
-
-            /* Change the insertion mode to "after head". */
-            $this->mode = self::AFTER_HEAD;
-
-            /* A start tag with the tag name "head" or an end tag except "html". */
-        } elseif (($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') ||
-            ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')
-        ) {
-            // Parse error. Ignore the token.
-
-            /* Anything else */
-        } else {
-            /* If the current node is a head element, act as if an end tag
-            token with the tag name "head" had been seen. */
-            if ($this->head_pointer->isSameNode(end($this->stack))) {
-                $this->inHead(
-                    array(
-                        'name' => 'head',
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-
-                /* Otherwise, change the insertion mode to "after head". */
-            } else {
-                $this->mode = self::AFTER_HEAD;
-            }
-
-            /* Then, reprocess the current token. */
-            return $this->afterHead($token);
-        }
-    }
-
-    private function afterHead($token)
-    {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data attribute
-            set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-            /* A start tag token with the tag name "body" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') {
-            /* Insert a body element for the token. */
-            $this->insertElement($token);
-
-            /* Change the insertion mode to "in body". */
-            $this->mode = self::IN_BODY;
-
-            /* A start tag token with the tag name "frameset" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') {
-            /* Insert a frameset element for the token. */
-            $this->insertElement($token);
-
-            /* Change the insertion mode to "in frameset". */
-            $this->mode = self::IN_FRAME;
-
-            /* A start tag token whose tag name is one of: "base", "link", "meta",
-            "script", "style", "title" */
-        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
-                $token['name'],
-                array('base', 'link', 'meta', 'script', 'style', 'title')
-            )
-        ) {
-            /* Parse error. Switch the insertion mode back to "in head" and
-            reprocess the token. */
-            $this->mode = self::IN_HEAD;
-            return $this->inHead($token);
-
-            /* Anything else */
-        } else {
-            /* Act as if a start tag token with the tag name "body" and no
-            attributes had been seen, and then reprocess the current token. */
-            $this->afterHead(
-                array(
-                    'name' => 'body',
-                    'type' => HTML5::STARTTAG,
-                    'attr' => array()
-                )
-            );
-
-            return $this->inBody($token);
-        }
-    }
-
-    private function inBody($token)
-    {
-        /* Handle the token as follows: */
-
-        switch ($token['type']) {
-            /* A character token */
-            case HTML5::CHARACTR:
-                /* Reconstruct the active formatting elements, if any. */
-                $this->reconstructActiveFormattingElements();
-
-                /* Append the token's character to the current node. */
-                $this->insertText($token['data']);
-                break;
-
-            /* A comment token */
-            case HTML5::COMMENT:
-                /* Append a Comment node to the current node with the data
-                attribute set to the data given in the comment token. */
-                $this->insertComment($token['data']);
-                break;
-
-            case HTML5::STARTTAG:
-                switch ($token['name']) {
-                    /* A start tag token whose tag name is one of: "script",
-                    "style" */
-                    case 'script':
-                    case 'style':
-                        /* Process the token as if the insertion mode had been "in
-                        head". */
-                        return $this->inHead($token);
-                        break;
-
-                    /* A start tag token whose tag name is one of: "base", "link",
-                    "meta", "title" */
-                    case 'base':
-                    case 'link':
-                    case 'meta':
-                    case 'title':
-                        /* Parse error. Process the token as if the insertion mode
-                        had    been "in head". */
-                        return $this->inHead($token);
-                        break;
-
-                    /* A start tag token with the tag name "body" */
-                    case 'body':
-                        /* Parse error. If the second element on the stack of open
-                        elements is not a body element, or, if the stack of open
-                        elements has only one node on it, then ignore the token.
-                        (innerHTML case) */
-                        if (count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') {
-                            // Ignore
-
-                            /* Otherwise, for each attribute on the token, check to see
-                            if the attribute is already present on the body element (the
-                            second element)    on the stack of open elements. If it is not,
-                            add the attribute and its corresponding value to that
-                            element. */
-                        } else {
-                            foreach ($token['attr'] as $attr) {
-                                if (!$this->stack[1]->hasAttribute($attr['name'])) {
-                                    $this->stack[1]->setAttribute($attr['name'], $attr['value']);
-                                }
-                            }
-                        }
-                        break;
-
-                    /* A start tag whose tag name is one of: "address",
-                    "blockquote", "center", "dir", "div", "dl", "fieldset",
-                    "listing", "menu", "ol", "p", "ul" */
-                    case 'address':
-                    case 'blockquote':
-                    case 'center':
-                    case 'dir':
-                    case 'div':
-                    case 'dl':
-                    case 'fieldset':
-                    case 'listing':
-                    case 'menu':
-                    case 'ol':
-                    case 'p':
-                    case 'ul':
-                        /* If the stack of open elements has a p element in scope,
-                        then act as if an end tag with the tag name p had been
-                        seen. */
-                        if ($this->elementInScope('p')) {
-                            $this->emitToken(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-                        break;
-
-                    /* A start tag whose tag name is "form" */
-                    case 'form':
-                        /* If the form element pointer is not null, ignore the
-                        token with a parse error. */
-                        if ($this->form_pointer !== null) {
-                            // Ignore.
-
-                            /* Otherwise: */
-                        } else {
-                            /* If the stack of open elements has a p element in
-                            scope, then act as if an end tag with the tag name p
-                            had been seen. */
-                            if ($this->elementInScope('p')) {
-                                $this->emitToken(
-                                    array(
-                                        'name' => 'p',
-                                        'type' => HTML5::ENDTAG
-                                    )
-                                );
-                            }
-
-                            /* Insert an HTML element for the token, and set the
-                            form element pointer to point to the element created. */
-                            $element = $this->insertElement($token);
-                            $this->form_pointer = $element;
-                        }
-                        break;
-
-                    /* A start tag whose tag name is "li", "dd" or "dt" */
-                    case 'li':
-                    case 'dd':
-                    case 'dt':
-                        /* If the stack of open elements has a p  element in scope,
-                        then act as if an end tag with the tag name p had been
-                        seen. */
-                        if ($this->elementInScope('p')) {
-                            $this->emitToken(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-
-                        $stack_length = count($this->stack) - 1;
-
-                        for ($n = $stack_length; 0 <= $n; $n--) {
-                            /* 1. Initialise node to be the current node (the
-                            bottommost node of the stack). */
-                            $stop = false;
-                            $node = $this->stack[$n];
-                            $cat = $this->getElementCategory($node->tagName);
-
-                            /* 2. If node is an li, dd or dt element, then pop all
-                            the    nodes from the current node up to node, including
-                            node, then stop this algorithm. */
-                            if ($token['name'] === $node->tagName || ($token['name'] !== 'li'
-                                    && ($node->tagName === 'dd' || $node->tagName === 'dt'))
-                            ) {
-                                for ($x = $stack_length; $x >= $n; $x--) {
-                                    array_pop($this->stack);
-                                }
-
-                                break;
-                            }
-
-                            /* 3. If node is not in the formatting category, and is
-                            not    in the phrasing category, and is not an address or
-                            div element, then stop this algorithm. */
-                            if ($cat !== self::FORMATTING && $cat !== self::PHRASING &&
-                                $node->tagName !== 'address' && $node->tagName !== 'div'
-                            ) {
-                                break;
-                            }
-                        }
-
-                        /* Finally, insert an HTML element with the same tag
-                        name as the    token's. */
-                        $this->insertElement($token);
-                        break;
-
-                    /* A start tag token whose tag name is "plaintext" */
-                    case 'plaintext':
-                        /* If the stack of open elements has a p  element in scope,
-                        then act as if an end tag with the tag name p had been
-                        seen. */
-                        if ($this->elementInScope('p')) {
-                            $this->emitToken(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        return HTML5::PLAINTEXT;
-                        break;
-
-                    /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4",
-                    "h5", "h6" */
-                    case 'h1':
-                    case 'h2':
-                    case 'h3':
-                    case 'h4':
-                    case 'h5':
-                    case 'h6':
-                        /* If the stack of open elements has a p  element in scope,
-                        then act as if an end tag with the tag name p had been seen. */
-                        if ($this->elementInScope('p')) {
-                            $this->emitToken(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-
-                        /* If the stack of open elements has in scope an element whose
-                        tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
-                        this is a parse error; pop elements from the stack until an
-                        element with one of those tag names has been popped from the
-                        stack. */
-                        while ($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
-                            array_pop($this->stack);
-                        }
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-                        break;
-
-                    /* A start tag whose tag name is "a" */
-                    case 'a':
-                        /* If the list of active formatting elements contains
-                        an element whose tag name is "a" between the end of the
-                        list and the last marker on the list (or the start of
-                        the list if there is no marker on the list), then this
-                        is a parse error; act as if an end tag with the tag name
-                        "a" had been seen, then remove that element from the list
-                        of active formatting elements and the stack of open
-                        elements if the end tag didn't already remove it (it
-                        might not have if the element is not in table scope). */
-                        $leng = count($this->a_formatting);
-
-                        for ($n = $leng - 1; $n >= 0; $n--) {
-                            if ($this->a_formatting[$n] === self::MARKER) {
-                                break;
-
-                            } elseif ($this->a_formatting[$n]->nodeName === 'a') {
-                                $this->emitToken(
-                                    array(
-                                        'name' => 'a',
-                                        'type' => HTML5::ENDTAG
-                                    )
-                                );
-                                break;
-                            }
-                        }
-
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an HTML element for the token. */
-                        $el = $this->insertElement($token);
-
-                        /* Add that element to the list of active formatting
-                        elements. */
-                        $this->a_formatting[] = $el;
-                        break;
-
-                    /* A start tag whose tag name is one of: "b", "big", "em", "font",
-                    "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
-                    case 'b':
-                    case 'big':
-                    case 'em':
-                    case 'font':
-                    case 'i':
-                    case 'nobr':
-                    case 's':
-                    case 'small':
-                    case 'strike':
-                    case 'strong':
-                    case 'tt':
-                    case 'u':
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an HTML element for the token. */
-                        $el = $this->insertElement($token);
-
-                        /* Add that element to the list of active formatting
-                        elements. */
-                        $this->a_formatting[] = $el;
-                        break;
-
-                    /* A start tag token whose tag name is "button" */
-                    case 'button':
-                        /* If the stack of open elements has a button element in scope,
-                        then this is a parse error; act as if an end tag with the tag
-                        name "button" had been seen, then reprocess the token. (We don't
-                        do that. Unnecessary.) */
-                        if ($this->elementInScope('button')) {
-                            $this->inBody(
-                                array(
-                                    'name' => 'button',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        /* Insert a marker at the end of the list of active
-                        formatting elements. */
-                        $this->a_formatting[] = self::MARKER;
-                        break;
-
-                    /* A start tag token whose tag name is one of: "marquee", "object" */
-                    case 'marquee':
-                    case 'object':
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        /* Insert a marker at the end of the list of active
-                        formatting elements. */
-                        $this->a_formatting[] = self::MARKER;
-                        break;
-
-                    /* A start tag token whose tag name is "xmp" */
-                    case 'xmp':
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        /* Switch the content model flag to the CDATA state. */
-                        return HTML5::CDATA;
-                        break;
-
-                    /* A start tag whose tag name is "table" */
-                    case 'table':
-                        /* If the stack of open elements has a p element in scope,
-                        then act as if an end tag with the tag name p had been seen. */
-                        if ($this->elementInScope('p')) {
-                            $this->emitToken(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        /* Change the insertion mode to "in table". */
-                        $this->mode = self::IN_TABLE;
-                        break;
-
-                    /* A start tag whose tag name is one of: "area", "basefont",
-                    "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */
-                    case 'area':
-                    case 'basefont':
-                    case 'bgsound':
-                    case 'br':
-                    case 'embed':
-                    case 'img':
-                    case 'param':
-                    case 'spacer':
-                    case 'wbr':
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        /* Immediately pop the current node off the stack of open elements. */
-                        array_pop($this->stack);
-                        break;
-
-                    /* A start tag whose tag name is "hr" */
-                    case 'hr':
-                        /* If the stack of open elements has a p element in scope,
-                        then act as if an end tag with the tag name p had been seen. */
-                        if ($this->elementInScope('p')) {
-                            $this->emitToken(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        /* Immediately pop the current node off the stack of open elements. */
-                        array_pop($this->stack);
-                        break;
-
-                    /* A start tag whose tag name is "image" */
-                    case 'image':
-                        /* Parse error. Change the token's tag name to "img" and
-                        reprocess it. (Don't ask.) */
-                        $token['name'] = 'img';
-                        return $this->inBody($token);
-                        break;
-
-                    /* A start tag whose tag name is "input" */
-                    case 'input':
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an input element for the token. */
-                        $element = $this->insertElement($token, false);
-
-                        /* If the form element pointer is not null, then associate the
-                        input element with the form element pointed to by the form
-                        element pointer. */
-                        $this->form_pointer !== null
-                            ? $this->form_pointer->appendChild($element)
-                            : end($this->stack)->appendChild($element);
-
-                        /* Pop that input element off the stack of open elements. */
-                        array_pop($this->stack);
-                        break;
-
-                    /* A start tag whose tag name is "isindex" */
-                    case 'isindex':
-                        /* Parse error. */
-                        // w/e
-
-                        /* If the form element pointer is not null,
-                        then ignore the token. */
-                        if ($this->form_pointer === null) {
-                            /* Act as if a start tag token with the tag name "form" had
-                            been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'body',
-                                    'type' => HTML5::STARTTAG,
-                                    'attr' => array()
-                                )
-                            );
-
-                            /* Act as if a start tag token with the tag name "hr" had
-                            been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'hr',
-                                    'type' => HTML5::STARTTAG,
-                                    'attr' => array()
-                                )
-                            );
-
-                            /* Act as if a start tag token with the tag name "p" had
-                            been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::STARTTAG,
-                                    'attr' => array()
-                                )
-                            );
-
-                            /* Act as if a start tag token with the tag name "label"
-                            had been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'label',
-                                    'type' => HTML5::STARTTAG,
-                                    'attr' => array()
-                                )
-                            );
-
-                            /* Act as if a stream of character tokens had been seen. */
-                            $this->insertText(
-                                'This is a searchable index. ' .
-                                'Insert your search keywords here: '
-                            );
-
-                            /* Act as if a start tag token with the tag name "input"
-                            had been seen, with all the attributes from the "isindex"
-                            token, except with the "name" attribute set to the value
-                            "isindex" (ignoring any explicit "name" attribute). */
-                            $attr = $token['attr'];
-                            $attr[] = array('name' => 'name', 'value' => 'isindex');
-
-                            $this->inBody(
-                                array(
-                                    'name' => 'input',
-                                    'type' => HTML5::STARTTAG,
-                                    'attr' => $attr
-                                )
-                            );
-
-                            /* Act as if a stream of character tokens had been seen
-                            (see below for what they should say). */
-                            $this->insertText(
-                                'This is a searchable index. ' .
-                                'Insert your search keywords here: '
-                            );
-
-                            /* Act as if an end tag token with the tag name "label"
-                            had been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'label',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-
-                            /* Act as if an end tag token with the tag name "p" had
-                            been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'p',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-
-                            /* Act as if a start tag token with the tag name "hr" had
-                            been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'hr',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-
-                            /* Act as if an end tag token with the tag name "form" had
-                            been seen. */
-                            $this->inBody(
-                                array(
-                                    'name' => 'form',
-                                    'type' => HTML5::ENDTAG
-                                )
-                            );
-                        }
-                        break;
-
-                    /* A start tag whose tag name is "textarea" */
-                    case 'textarea':
-                        $this->insertElement($token);
-
-                        /* Switch the tokeniser's content model flag to the
-                        RCDATA state. */
-                        return HTML5::RCDATA;
-                        break;
-
-                    /* A start tag whose tag name is one of: "iframe", "noembed",
-                    "noframes" */
-                    case 'iframe':
-                    case 'noembed':
-                    case 'noframes':
-                        $this->insertElement($token);
-
-                        /* Switch the tokeniser's content model flag to the CDATA state. */
-                        return HTML5::CDATA;
-                        break;
-
-                    /* A start tag whose tag name is "select" */
-                    case 'select':
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        /* Insert an HTML element for the token. */
-                        $this->insertElement($token);
-
-                        /* Change the insertion mode to "in select". */
-                        $this->mode = self::IN_SELECT;
-                        break;
-
-                    /* A start or end tag whose tag name is one of: "caption", "col",
-                    "colgroup", "frame", "frameset", "head", "option", "optgroup",
-                    "tbody", "td", "tfoot", "th", "thead", "tr". */
-                    case 'caption':
-                    case 'col':
-                    case 'colgroup':
-                    case 'frame':
-                    case 'frameset':
-                    case 'head':
-                    case 'option':
-                    case 'optgroup':
-                    case 'tbody':
-                    case 'td':
-                    case 'tfoot':
-                    case 'th':
-                    case 'thead':
-                    case 'tr':
-                        // Parse error. Ignore the token.
-                        break;
-
-                    /* A start or end tag whose tag name is one of: "event-source",
-                    "section", "nav", "article", "aside", "header", "footer",
-                    "datagrid", "command" */
-                    case 'event-source':
-                    case 'section':
-                    case 'nav':
-                    case 'article':
-                    case 'aside':
-                    case 'header':
-                    case 'footer':
-                    case 'datagrid':
-                    case 'command':
-                        // Work in progress!
-                        break;
-
-                    /* A start tag token not covered by the previous entries */
-                    default:
-                        /* Reconstruct the active formatting elements, if any. */
-                        $this->reconstructActiveFormattingElements();
-
-                        $this->insertElement($token, true, true);
-                        break;
-                }
-                break;
-
-            case HTML5::ENDTAG:
-                switch ($token['name']) {
-                    /* An end tag with the tag name "body" */
-                    case 'body':
-                        /* If the second element in the stack of open elements is
-                        not a body element, this is a parse error. Ignore the token.
-                        (innerHTML case) */
-                        if (count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') {
-                            // Ignore.
-
-                            /* If the current node is not the body element, then this
-                            is a parse error. */
-                        } elseif (end($this->stack)->nodeName !== 'body') {
-                            // Parse error.
-                        }
-
-                        /* Change the insertion mode to "after body". */
-                        $this->mode = self::AFTER_BODY;
-                        break;
-
-                    /* An end tag with the tag name "html" */
-                    case 'html':
-                        /* Act as if an end tag with tag name "body" had been seen,
-                        then, if that token wasn't ignored, reprocess the current
-                        token. */
-                        $this->inBody(
-                            array(
-                                'name' => 'body',
-                                'type' => HTML5::ENDTAG
-                            )
-                        );
-
-                        return $this->afterBody($token);
-                        break;
-
-                    /* An end tag whose tag name is one of: "address", "blockquote",
-                    "center", "dir", "div", "dl", "fieldset", "listing", "menu",
-                    "ol", "pre", "ul" */
-                    case 'address':
-                    case 'blockquote':
-                    case 'center':
-                    case 'dir':
-                    case 'div':
-                    case 'dl':
-                    case 'fieldset':
-                    case 'listing':
-                    case 'menu':
-                    case 'ol':
-                    case 'pre':
-                    case 'ul':
-                        /* If the stack of open elements has an element in scope
-                        with the same tag name as that of the token, then generate
-                        implied end tags. */
-                        if ($this->elementInScope($token['name'])) {
-                            $this->generateImpliedEndTags();
-
-                            /* Now, if the current node is not an element with
-                            the same tag name as that of the token, then this
-                            is a parse error. */
-                            // w/e
-
-                            /* If the stack of open elements has an element in
-                            scope with the same tag name as that of the token,
-                            then pop elements from this stack until an element
-                            with that tag name has been popped from the stack. */
-                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                                if ($this->stack[$n]->nodeName === $token['name']) {
-                                    $n = -1;
-                                }
-
-                                array_pop($this->stack);
-                            }
-                        }
-                        break;
-
-                    /* An end tag whose tag name is "form" */
-                    case 'form':
-                        /* If the stack of open elements has an element in scope
-                        with the same tag name as that of the token, then generate
-                        implied    end tags. */
-                        if ($this->elementInScope($token['name'])) {
-                            $this->generateImpliedEndTags();
-
-                        }
-
-                        if (end($this->stack)->nodeName !== $token['name']) {
-                            /* Now, if the current node is not an element with the
-                            same tag name as that of the token, then this is a parse
-                            error. */
-                            // w/e
-
-                        } else {
-                            /* Otherwise, if the current node is an element with
-                            the same tag name as that of the token pop that element
-                            from the stack. */
-                            array_pop($this->stack);
-                        }
-
-                        /* In any case, set the form element pointer to null. */
-                        $this->form_pointer = null;
-                        break;
-
-                    /* An end tag whose tag name is "p" */
-                    case 'p':
-                        /* If the stack of open elements has a p element in scope,
-                        then generate implied end tags, except for p elements. */
-                        if ($this->elementInScope('p')) {
-                            $this->generateImpliedEndTags(array('p'));
-
-                            /* If the current node is not a p element, then this is
-                            a parse error. */
-                            // k
-
-                            /* If the stack of open elements has a p element in
-                            scope, then pop elements from this stack until the stack
-                            no longer has a p element in scope. */
-                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                                if ($this->elementInScope('p')) {
-                                    array_pop($this->stack);
-
-                                } else {
-                                    break;
-                                }
-                            }
-                        }
-                        break;
-
-                    /* An end tag whose tag name is "dd", "dt", or "li" */
-                    case 'dd':
-                    case 'dt':
-                    case 'li':
-                        /* If the stack of open elements has an element in scope
-                        whose tag name matches the tag name of the token, then
-                        generate implied end tags, except for elements with the
-                        same tag name as the token. */
-                        if ($this->elementInScope($token['name'])) {
-                            $this->generateImpliedEndTags(array($token['name']));
-
-                            /* If the current node is not an element with the same
-                            tag name as the token, then this is a parse error. */
-                            // w/e
-
-                            /* If the stack of open elements has an element in scope
-                            whose tag name matches the tag name of the token, then
-                            pop elements from this stack until an element with that
-                            tag name has been popped from the stack. */
-                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                                if ($this->stack[$n]->nodeName === $token['name']) {
-                                    $n = -1;
-                                }
-
-                                array_pop($this->stack);
-                            }
-                        }
-                        break;
-
-                    /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4",
-                    "h5", "h6" */
-                    case 'h1':
-                    case 'h2':
-                    case 'h3':
-                    case 'h4':
-                    case 'h5':
-                    case 'h6':
-                        $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
-
-                        /* If the stack of open elements has in scope an element whose
-                        tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then
-                        generate implied end tags. */
-                        if ($this->elementInScope($elements)) {
-                            $this->generateImpliedEndTags();
-
-                            /* Now, if the current node is not an element with the same
-                            tag name as that of the token, then this is a parse error. */
-                            // w/e
-
-                            /* If the stack of open elements has in scope an element
-                            whose tag name is one of "h1", "h2", "h3", "h4", "h5", or
-                            "h6", then pop elements from the stack until an element
-                            with one of those tag names has been popped from the stack. */
-                            while ($this->elementInScope($elements)) {
-                                array_pop($this->stack);
-                            }
-                        }
-                        break;
-
-                    /* An end tag whose tag name is one of: "a", "b", "big", "em",
-                    "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */
-                    case 'a':
-                    case 'b':
-                    case 'big':
-                    case 'em':
-                    case 'font':
-                    case 'i':
-                    case 'nobr':
-                    case 's':
-                    case 'small':
-                    case 'strike':
-                    case 'strong':
-                    case 'tt':
-                    case 'u':
-                        /* 1. Let the formatting element be the last element in
-                        the list of active formatting elements that:
-                            * is between the end of the list and the last scope
-                            marker in the list, if any, or the start of the list
-                            otherwise, and
-                            * has the same tag name as the token.
-                        */
-                        while (true) {
-                            for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) {
-                                if ($this->a_formatting[$a] === self::MARKER) {
-                                    break;
-
-                                } elseif ($this->a_formatting[$a]->tagName === $token['name']) {
-                                    $formatting_element = $this->a_formatting[$a];
-                                    $in_stack = in_array($formatting_element, $this->stack, true);
-                                    $fe_af_pos = $a;
-                                    break;
-                                }
-                            }
-
-                            /* If there is no such node, or, if that node is
-                            also in the stack of open elements but the element
-                            is not in scope, then this is a parse error. Abort
-                            these steps. The token is ignored. */
-                            if (!isset($formatting_element) || ($in_stack &&
-                                    !$this->elementInScope($token['name']))
-                            ) {
-                                break;
-
-                                /* Otherwise, if there is such a node, but that node
-                                is not in the stack of open elements, then this is a
-                                parse error; remove the element from the list, and
-                                abort these steps. */
-                            } elseif (isset($formatting_element) && !$in_stack) {
-                                unset($this->a_formatting[$fe_af_pos]);
-                                $this->a_formatting = array_merge($this->a_formatting);
-                                break;
-                            }
-
-                            /* 2. Let the furthest block be the topmost node in the
-                            stack of open elements that is lower in the stack
-                            than the formatting element, and is not an element in
-                            the phrasing or formatting categories. There might
-                            not be one. */
-                            $fe_s_pos = array_search($formatting_element, $this->stack, true);
-                            $length = count($this->stack);
-
-                            for ($s = $fe_s_pos + 1; $s < $length; $s++) {
-                                $category = $this->getElementCategory($this->stack[$s]->nodeName);
-
-                                if ($category !== self::PHRASING && $category !== self::FORMATTING) {
-                                    $furthest_block = $this->stack[$s];
-                                }
-                            }
-
-                            /* 3. If there is no furthest block, then the UA must
-                            skip the subsequent steps and instead just pop all
-                            the nodes from the bottom of the stack of open
-                            elements, from the current node up to the formatting
-                            element, and remove the formatting element from the
-                            list of active formatting elements. */
-                            if (!isset($furthest_block)) {
-                                for ($n = $length - 1; $n >= $fe_s_pos; $n--) {
-                                    array_pop($this->stack);
-                                }
-
-                                unset($this->a_formatting[$fe_af_pos]);
-                                $this->a_formatting = array_merge($this->a_formatting);
-                                break;
-                            }
-
-                            /* 4. Let the common ancestor be the element
-                            immediately above the formatting element in the stack
-                            of open elements. */
-                            $common_ancestor = $this->stack[$fe_s_pos - 1];
-
-                            /* 5. If the furthest block has a parent node, then
-                            remove the furthest block from its parent node. */
-                            if ($furthest_block->parentNode !== null) {
-                                $furthest_block->parentNode->removeChild($furthest_block);
-                            }
-
-                            /* 6. Let a bookmark note the position of the
-                            formatting element in the list of active formatting
-                            elements relative to the elements on either side
-                            of it in the list. */
-                            $bookmark = $fe_af_pos;
-
-                            /* 7. Let node and last node  be the furthest block.
-                            Follow these steps: */
-                            $node = $furthest_block;
-                            $last_node = $furthest_block;
-
-                            while (true) {
-                                for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) {
-                                    /* 7.1 Let node be the element immediately
-                                    prior to node in the stack of open elements. */
-                                    $node = $this->stack[$n];
-
-                                    /* 7.2 If node is not in the list of active
-                                    formatting elements, then remove node from
-                                    the stack of open elements and then go back
-                                    to step 1. */
-                                    if (!in_array($node, $this->a_formatting, true)) {
-                                        unset($this->stack[$n]);
-                                        $this->stack = array_merge($this->stack);
-
-                                    } else {
-                                        break;
-                                    }
-                                }
-
-                                /* 7.3 Otherwise, if node is the formatting
-                                element, then go to the next step in the overall
-                                algorithm. */
-                                if ($node === $formatting_element) {
-                                    break;
-
-                                    /* 7.4 Otherwise, if last node is the furthest
-                                    block, then move the aforementioned bookmark to
-                                    be immediately after the node in the list of
-                                    active formatting elements. */
-                                } elseif ($last_node === $furthest_block) {
-                                    $bookmark = array_search($node, $this->a_formatting, true) + 1;
-                                }
-
-                                /* 7.5 If node has any children, perform a
-                                shallow clone of node, replace the entry for
-                                node in the list of active formatting elements
-                                with an entry for the clone, replace the entry
-                                for node in the stack of open elements with an
-                                entry for the clone, and let node be the clone. */
-                                if ($node->hasChildNodes()) {
-                                    $clone = $node->cloneNode();
-                                    $s_pos = array_search($node, $this->stack, true);
-                                    $a_pos = array_search($node, $this->a_formatting, true);
-
-                                    $this->stack[$s_pos] = $clone;
-                                    $this->a_formatting[$a_pos] = $clone;
-                                    $node = $clone;
-                                }
-
-                                /* 7.6 Insert last node into node, first removing
-                                it from its previous parent node if any. */
-                                if ($last_node->parentNode !== null) {
-                                    $last_node->parentNode->removeChild($last_node);
-                                }
-
-                                $node->appendChild($last_node);
-
-                                /* 7.7 Let last node be node. */
-                                $last_node = $node;
-                            }
-
-                            /* 8. Insert whatever last node ended up being in
-                            the previous step into the common ancestor node,
-                            first removing it from its previous parent node if
-                            any. */
-                            if ($last_node->parentNode !== null) {
-                                $last_node->parentNode->removeChild($last_node);
-                            }
-
-                            $common_ancestor->appendChild($last_node);
-
-                            /* 9. Perform a shallow clone of the formatting
-                            element. */
-                            $clone = $formatting_element->cloneNode();
-
-                            /* 10. Take all of the child nodes of the furthest
-                            block and append them to the clone created in the
-                            last step. */
-                            while ($furthest_block->hasChildNodes()) {
-                                $child = $furthest_block->firstChild;
-                                $furthest_block->removeChild($child);
-                                $clone->appendChild($child);
-                            }
-
-                            /* 11. Append that clone to the furthest block. */
-                            $furthest_block->appendChild($clone);
-
-                            /* 12. Remove the formatting element from the list
-                            of active formatting elements, and insert the clone
-                            into the list of active formatting elements at the
-                            position of the aforementioned bookmark. */
-                            $fe_af_pos = array_search($formatting_element, $this->a_formatting, true);
-                            unset($this->a_formatting[$fe_af_pos]);
-                            $this->a_formatting = array_merge($this->a_formatting);
-
-                            $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1);
-                            $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting));
-                            $this->a_formatting = array_merge($af_part1, array($clone), $af_part2);
-
-                            /* 13. Remove the formatting element from the stack
-                            of open elements, and insert the clone into the stack
-                            of open elements immediately after (i.e. in a more
-                            deeply nested position than) the position of the
-                            furthest block in that stack. */
-                            $fe_s_pos = array_search($formatting_element, $this->stack, true);
-                            $fb_s_pos = array_search($furthest_block, $this->stack, true);
-                            unset($this->stack[$fe_s_pos]);
-
-                            $s_part1 = array_slice($this->stack, 0, $fb_s_pos);
-                            $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack));
-                            $this->stack = array_merge($s_part1, array($clone), $s_part2);
-
-                            /* 14. Jump back to step 1 in this series of steps. */
-                            unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block);
-                        }
-                        break;
-
-                    /* An end tag token whose tag name is one of: "button",
-                    "marquee", "object" */
-                    case 'button':
-                    case 'marquee':
-                    case 'object':
-                        /* If the stack of open elements has an element in scope whose
-                        tag name matches the tag name of the token, then generate implied
-                        tags. */
-                        if ($this->elementInScope($token['name'])) {
-                            $this->generateImpliedEndTags();
-
-                            /* Now, if the current node is not an element with the same
-                            tag name as the token, then this is a parse error. */
-                            // k
-
-                            /* Now, if the stack of open elements has an element in scope
-                            whose tag name matches the tag name of the token, then pop
-                            elements from the stack until that element has been popped from
-                            the stack, and clear the list of active formatting elements up
-                            to the last marker. */
-                            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                                if ($this->stack[$n]->nodeName === $token['name']) {
-                                    $n = -1;
-                                }
-
-                                array_pop($this->stack);
-                            }
-
-                            $marker = end(array_keys($this->a_formatting, self::MARKER, true));
-
-                            for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) {
-                                array_pop($this->a_formatting);
-                            }
-                        }
-                        break;
-
-                    /* Or an end tag whose tag name is one of: "area", "basefont",
-                    "bgsound", "br", "embed", "hr", "iframe", "image", "img",
-                    "input", "isindex", "noembed", "noframes", "param", "select",
-                    "spacer", "table", "textarea", "wbr" */
-                    case 'area':
-                    case 'basefont':
-                    case 'bgsound':
-                    case 'br':
-                    case 'embed':
-                    case 'hr':
-                    case 'iframe':
-                    case 'image':
-                    case 'img':
-                    case 'input':
-                    case 'isindex':
-                    case 'noembed':
-                    case 'noframes':
-                    case 'param':
-                    case 'select':
-                    case 'spacer':
-                    case 'table':
-                    case 'textarea':
-                    case 'wbr':
-                        // Parse error. Ignore the token.
-                        break;
-
-                    /* An end tag token not covered by the previous entries */
-                    default:
-                        for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                            /* Initialise node to be the current node (the bottommost
-                            node of the stack). */
-                            $node = end($this->stack);
-
-                            /* If node has the same tag name as the end tag token,
-                            then: */
-                            if ($token['name'] === $node->nodeName) {
-                                /* Generate implied end tags. */
-                                $this->generateImpliedEndTags();
-
-                                /* If the tag name of the end tag token does not
-                                match the tag name of the current node, this is a
-                                parse error. */
-                                // k
-
-                                /* Pop all the nodes from the current node up to
-                                node, including node, then stop this algorithm. */
-                                for ($x = count($this->stack) - $n; $x >= $n; $x--) {
-                                    array_pop($this->stack);
-                                }
-
-                            } else {
-                                $category = $this->getElementCategory($node);
-
-                                if ($category !== self::SPECIAL && $category !== self::SCOPING) {
-                                    /* Otherwise, if node is in neither the formatting
-                                    category nor the phrasing category, then this is a
-                                    parse error. Stop this algorithm. The end tag token
-                                    is ignored. */
-                                    return false;
-                                }
-                            }
-                        }
-                        break;
-                }
-                break;
-        }
-    }
-
-    private function inTable($token)
-    {
-        $clear = array('html', 'table');
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Append the character to the current node. */
-            $text = $this->dom->createTextNode($token['data']);
-            end($this->stack)->appendChild($text);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            end($this->stack)->appendChild($comment);
-
-            /* A start tag whose tag name is "caption" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            $token['name'] === 'caption'
-        ) {
-            /* Clear the stack back to a table context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert a marker at the end of the list of active
-            formatting elements. */
-            $this->a_formatting[] = self::MARKER;
-
-            /* Insert an HTML element for the token, then switch the
-            insertion mode to "in caption". */
-            $this->insertElement($token);
-            $this->mode = self::IN_CAPTION;
-
-            /* A start tag whose tag name is "colgroup" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            $token['name'] === 'colgroup'
-        ) {
-            /* Clear the stack back to a table context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert an HTML element for the token, then switch the
-            insertion mode to "in column group". */
-            $this->insertElement($token);
-            $this->mode = self::IN_CGROUP;
-
-            /* A start tag whose tag name is "col" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            $token['name'] === 'col'
-        ) {
-            $this->inTable(
-                array(
-                    'name' => 'colgroup',
-                    'type' => HTML5::STARTTAG,
-                    'attr' => array()
-                )
-            );
-
-            $this->inColumnGroup($token);
-
-            /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */
-        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
-                $token['name'],
-                array('tbody', 'tfoot', 'thead')
-            )
-        ) {
-            /* Clear the stack back to a table context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert an HTML element for the token, then switch the insertion
-            mode to "in table body". */
-            $this->insertElement($token);
-            $this->mode = self::IN_TBODY;
-
-            /* A start tag whose tag name is one of: "td", "th", "tr" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            in_array($token['name'], array('td', 'th', 'tr'))
-        ) {
-            /* Act as if a start tag token with the tag name "tbody" had been
-            seen, then reprocess the current token. */
-            $this->inTable(
-                array(
-                    'name' => 'tbody',
-                    'type' => HTML5::STARTTAG,
-                    'attr' => array()
-                )
-            );
-
-            return $this->inTableBody($token);
-
-            /* A start tag whose tag name is "table" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            $token['name'] === 'table'
-        ) {
-            /* Parse error. Act as if an end tag token with the tag name "table"
-            had been seen, then, if that token wasn't ignored, reprocess the
-            current token. */
-            $this->inTable(
-                array(
-                    'name' => 'table',
-                    'type' => HTML5::ENDTAG
-                )
-            );
-
-            return $this->mainPhase($token);
-
-            /* An end tag whose tag name is "table" */
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            $token['name'] === 'table'
-        ) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if (!$this->elementInScope($token['name'], true)) {
-                return false;
-
-                /* Otherwise: */
-            } else {
-                /* Generate implied end tags. */
-                $this->generateImpliedEndTags();
-
-                /* Now, if the current node is not a table element, then this
-                is a parse error. */
-                // w/e
-
-                /* Pop elements from this stack until a table element has been
-                popped from the stack. */
-                while (true) {
-                    $current = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if ($current === 'table') {
-                        break;
-                    }
-                }
-
-                /* Reset the insertion mode appropriately. */
-                $this->resetInsertionMode();
-            }
-
-            /* An end tag whose tag name is one of: "body", "caption", "col",
-            "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
-                $token['name'],
-                array(
-                    'body',
-                    'caption',
-                    'col',
-                    'colgroup',
-                    'html',
-                    'tbody',
-                    'td',
-                    'tfoot',
-                    'th',
-                    'thead',
-                    'tr'
-                )
-            )
-        ) {
-            // Parse error. Ignore the token.
-
-            /* Anything else */
-        } else {
-            /* Parse error. Process the token as if the insertion mode was "in
-            body", with the following exception: */
-
-            /* If the current node is a table, tbody, tfoot, thead, or tr
-            element, then, whenever a node would be inserted into the current
-            node, it must instead be inserted into the foster parent element. */
-            if (in_array(
-                end($this->stack)->nodeName,
-                array('table', 'tbody', 'tfoot', 'thead', 'tr')
-            )
-            ) {
-                /* The foster parent element is the parent element of the last
-                table element in the stack of open elements, if there is a
-                table element and it has such a parent element. If there is no
-                table element in the stack of open elements (innerHTML case),
-                then the foster parent element is the first element in the
-                stack of open elements (the html  element). Otherwise, if there
-                is a table element in the stack of open elements, but the last
-                table element in the stack of open elements has no parent, or
-                its parent node is not an element, then the foster parent
-                element is the element before the last table element in the
-                stack of open elements. */
-                for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                    if ($this->stack[$n]->nodeName === 'table') {
-                        $table = $this->stack[$n];
-                        break;
-                    }
-                }
-
-                if (isset($table) && $table->parentNode !== null) {
-                    $this->foster_parent = $table->parentNode;
-
-                } elseif (!isset($table)) {
-                    $this->foster_parent = $this->stack[0];
-
-                } elseif (isset($table) && ($table->parentNode === null ||
-                        $table->parentNode->nodeType !== XML_ELEMENT_NODE)
-                ) {
-                    $this->foster_parent = $this->stack[$n - 1];
-                }
-            }
-
-            $this->inBody($token);
-        }
-    }
-
-    private function inCaption($token)
-    {
-        /* An end tag whose tag name is "caption" */
-        if ($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if (!$this->elementInScope($token['name'], true)) {
-                // Ignore
-
-                /* Otherwise: */
-            } else {
-                /* Generate implied end tags. */
-                $this->generateImpliedEndTags();
-
-                /* Now, if the current node is not a caption element, then this
-                is a parse error. */
-                // w/e
-
-                /* Pop elements from this stack until a caption element has
-                been popped from the stack. */
-                while (true) {
-                    $node = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if ($node === 'caption') {
-                        break;
-                    }
-                }
-
-                /* Clear the list of active formatting elements up to the last
-                marker. */
-                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
-                /* Switch the insertion mode to "in table". */
-                $this->mode = self::IN_TABLE;
-            }
-
-            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-            "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag
-            name is "table" */
-        } elseif (($token['type'] === HTML5::STARTTAG && in_array(
-                    $token['name'],
-                    array(
-                        'caption',
-                        'col',
-                        'colgroup',
-                        'tbody',
-                        'td',
-                        'tfoot',
-                        'th',
-                        'thead',
-                        'tr'
-                    )
-                )) || ($token['type'] === HTML5::ENDTAG &&
-                $token['name'] === 'table')
-        ) {
-            /* Parse error. Act as if an end tag with the tag name "caption"
-            had been seen, then, if that token wasn't ignored, reprocess the
-            current token. */
-            $this->inCaption(
-                array(
-                    'name' => 'caption',
-                    'type' => HTML5::ENDTAG
-                )
-            );
-
-            return $this->inTable($token);
-
-            /* An end tag whose tag name is one of: "body", "col", "colgroup",
-            "html", "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
-                $token['name'],
-                array(
-                    'body',
-                    'col',
-                    'colgroup',
-                    'html',
-                    'tbody',
-                    'tfoot',
-                    'th',
-                    'thead',
-                    'tr'
-                )
-            )
-        ) {
-            // Parse error. Ignore the token.
-
-            /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in body". */
-            $this->inBody($token);
-        }
-    }
-
-    private function inColumnGroup($token)
-    {
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Append the character to the current node. */
-            $text = $this->dom->createTextNode($token['data']);
-            end($this->stack)->appendChild($text);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            end($this->stack)->appendChild($comment);
-
-            /* A start tag whose tag name is "col" */
-        } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') {
-            /* Insert a col element for the token. Immediately pop the current
-            node off the stack of open elements. */
-            $this->insertElement($token);
-            array_pop($this->stack);
-
-            /* An end tag whose tag name is "colgroup" */
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            $token['name'] === 'colgroup'
-        ) {
-            /* If the current node is the root html element, then this is a
-            parse error, ignore the token. (innerHTML case) */
-            if (end($this->stack)->nodeName === 'html') {
-                // Ignore
-
-                /* Otherwise, pop the current node (which will be a colgroup
-                element) from the stack of open elements. Switch the insertion
-                mode to "in table". */
-            } else {
-                array_pop($this->stack);
-                $this->mode = self::IN_TABLE;
-            }
-
-            /* An end tag whose tag name is "col" */
-        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') {
-            /* Parse error. Ignore the token. */
-
-            /* Anything else */
-        } else {
-            /* Act as if an end tag with the tag name "colgroup" had been seen,
-            and then, if that token wasn't ignored, reprocess the current token. */
-            $this->inColumnGroup(
-                array(
-                    'name' => 'colgroup',
-                    'type' => HTML5::ENDTAG
-                )
-            );
-
-            return $this->inTable($token);
-        }
-    }
-
-    private function inTableBody($token)
-    {
-        $clear = array('tbody', 'tfoot', 'thead', 'html');
-
-        /* A start tag whose tag name is "tr" */
-        if ($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') {
-            /* Clear the stack back to a table body context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert a tr element for the token, then switch the insertion
-            mode to "in row". */
-            $this->insertElement($token);
-            $this->mode = self::IN_ROW;
-
-            /* A start tag whose tag name is one of: "th", "td" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            ($token['name'] === 'th' || $token['name'] === 'td')
-        ) {
-            /* Parse error. Act as if a start tag with the tag name "tr" had
-            been seen, then reprocess the current token. */
-            $this->inTableBody(
-                array(
-                    'name' => 'tr',
-                    'type' => HTML5::STARTTAG,
-                    'attr' => array()
-                )
-            );
-
-            return $this->inRow($token);
-
-            /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            in_array($token['name'], array('tbody', 'tfoot', 'thead'))
-        ) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. */
-            if (!$this->elementInScope($token['name'], true)) {
-                // Ignore
-
-                /* Otherwise: */
-            } else {
-                /* Clear the stack back to a table body context. */
-                $this->clearStackToTableContext($clear);
-
-                /* Pop the current node from the stack of open elements. Switch
-                the insertion mode to "in table". */
-                array_pop($this->stack);
-                $this->mode = self::IN_TABLE;
-            }
-
-            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-            "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */
-        } elseif (($token['type'] === HTML5::STARTTAG && in_array(
-                    $token['name'],
-                    array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead')
-                )) ||
-            ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table')
-        ) {
-            /* If the stack of open elements does not have a tbody, thead, or
-            tfoot element in table scope, this is a parse error. Ignore the
-            token. (innerHTML case) */
-            if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) {
-                // Ignore.
-
-                /* Otherwise: */
-            } else {
-                /* Clear the stack back to a table body context. */
-                $this->clearStackToTableContext($clear);
-
-                /* Act as if an end tag with the same tag name as the current
-                node ("tbody", "tfoot", or "thead") had been seen, then
-                reprocess the current token. */
-                $this->inTableBody(
-                    array(
-                        'name' => end($this->stack)->nodeName,
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-
-                return $this->mainPhase($token);
-            }
-
-            /* An end tag whose tag name is one of: "body", "caption", "col",
-            "colgroup", "html", "td", "th", "tr" */
-        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
-                $token['name'],
-                array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr')
-            )
-        ) {
-            /* Parse error. Ignore the token. */
-
-            /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in table". */
-            $this->inTable($token);
-        }
-    }
-
-    private function inRow($token)
-    {
-        $clear = array('tr', 'html');
-
-        /* A start tag whose tag name is one of: "th", "td" */
-        if ($token['type'] === HTML5::STARTTAG &&
-            ($token['name'] === 'th' || $token['name'] === 'td')
-        ) {
-            /* Clear the stack back to a table row context. */
-            $this->clearStackToTableContext($clear);
-
-            /* Insert an HTML element for the token, then switch the insertion
-            mode to "in cell". */
-            $this->insertElement($token);
-            $this->mode = self::IN_CELL;
-
-            /* Insert a marker at the end of the list of active formatting
-            elements. */
-            $this->a_formatting[] = self::MARKER;
-
-            /* An end tag whose tag name is "tr" */
-        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if (!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-                /* Otherwise: */
-            } else {
-                /* Clear the stack back to a table row context. */
-                $this->clearStackToTableContext($clear);
-
-                /* Pop the current node (which will be a tr element) from the
-                stack of open elements. Switch the insertion mode to "in table
-                body". */
-                array_pop($this->stack);
-                $this->mode = self::IN_TBODY;
-            }
-
-            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-            "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */
-        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
-                $token['name'],
-                array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr')
-            )
-        ) {
-            /* Act as if an end tag with the tag name "tr" had been seen, then,
-            if that token wasn't ignored, reprocess the current token. */
-            $this->inRow(
-                array(
-                    'name' => 'tr',
-                    'type' => HTML5::ENDTAG
-                )
-            );
-
-            return $this->inCell($token);
-
-            /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            in_array($token['name'], array('tbody', 'tfoot', 'thead'))
-        ) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. */
-            if (!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-                /* Otherwise: */
-            } else {
-                /* Otherwise, act as if an end tag with the tag name "tr" had
-                been seen, then reprocess the current token. */
-                $this->inRow(
-                    array(
-                        'name' => 'tr',
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-
-                return $this->inCell($token);
-            }
-
-            /* An end tag whose tag name is one of: "body", "caption", "col",
-            "colgroup", "html", "td", "th" */
-        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
-                $token['name'],
-                array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr')
-            )
-        ) {
-            /* Parse error. Ignore the token. */
-
-            /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in table". */
-            $this->inTable($token);
-        }
-    }
-
-    private function inCell($token)
-    {
-        /* An end tag whose tag name is one of: "td", "th" */
-        if ($token['type'] === HTML5::ENDTAG &&
-            ($token['name'] === 'td' || $token['name'] === 'th')
-        ) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as that of the token, then this is a
-            parse error and the token must be ignored. */
-            if (!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-                /* Otherwise: */
-            } else {
-                /* Generate implied end tags, except for elements with the same
-                tag name as the token. */
-                $this->generateImpliedEndTags(array($token['name']));
-
-                /* Now, if the current node is not an element with the same tag
-                name as the token, then this is a parse error. */
-                // k
-
-                /* Pop elements from this stack until an element with the same
-                tag name as the token has been popped from the stack. */
-                while (true) {
-                    $node = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if ($node === $token['name']) {
-                        break;
-                    }
-                }
-
-                /* Clear the list of active formatting elements up to the last
-                marker. */
-                $this->clearTheActiveFormattingElementsUpToTheLastMarker();
-
-                /* Switch the insertion mode to "in row". (The current node
-                will be a tr element at this point.) */
-                $this->mode = self::IN_ROW;
-            }
-
-            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-            "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
-                $token['name'],
-                array(
-                    'caption',
-                    'col',
-                    'colgroup',
-                    'tbody',
-                    'td',
-                    'tfoot',
-                    'th',
-                    'thead',
-                    'tr'
-                )
-            )
-        ) {
-            /* If the stack of open elements does not have a td or th element
-            in table scope, then this is a parse error; ignore the token.
-            (innerHTML case) */
-            if (!$this->elementInScope(array('td', 'th'), true)) {
-                // Ignore.
-
-                /* Otherwise, close the cell (see below) and reprocess the current
-                token. */
-            } else {
-                $this->closeCell();
-                return $this->inRow($token);
-            }
-
-            /* A start tag whose tag name is one of: "caption", "col", "colgroup",
-            "tbody", "td", "tfoot", "th", "thead", "tr" */
-        } elseif ($token['type'] === HTML5::STARTTAG && in_array(
-                $token['name'],
-                array(
-                    'caption',
-                    'col',
-                    'colgroup',
-                    'tbody',
-                    'td',
-                    'tfoot',
-                    'th',
-                    'thead',
-                    'tr'
-                )
-            )
-        ) {
-            /* If the stack of open elements does not have a td or th element
-            in table scope, then this is a parse error; ignore the token.
-            (innerHTML case) */
-            if (!$this->elementInScope(array('td', 'th'), true)) {
-                // Ignore.
-
-                /* Otherwise, close the cell (see below) and reprocess the current
-                token. */
-            } else {
-                $this->closeCell();
-                return $this->inRow($token);
-            }
-
-            /* An end tag whose tag name is one of: "body", "caption", "col",
-            "colgroup", "html" */
-        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
-                $token['name'],
-                array('body', 'caption', 'col', 'colgroup', 'html')
-            )
-        ) {
-            /* Parse error. Ignore the token. */
-
-            /* An end tag whose tag name is one of: "table", "tbody", "tfoot",
-            "thead", "tr" */
-        } elseif ($token['type'] === HTML5::ENDTAG && in_array(
-                $token['name'],
-                array('table', 'tbody', 'tfoot', 'thead', 'tr')
-            )
-        ) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as that of the token (which can only
-            happen for "tbody", "tfoot" and "thead", or, in the innerHTML case),
-            then this is a parse error and the token must be ignored. */
-            if (!$this->elementInScope($token['name'], true)) {
-                // Ignore.
-
-                /* Otherwise, close the cell (see below) and reprocess the current
-                token. */
-            } else {
-                $this->closeCell();
-                return $this->inRow($token);
-            }
-
-            /* Anything else */
-        } else {
-            /* Process the token as if the insertion mode was "in body". */
-            $this->inBody($token);
-        }
-    }
-
-    private function inSelect($token)
-    {
-        /* Handle the token as follows: */
-
-        /* A character token */
-        if ($token['type'] === HTML5::CHARACTR) {
-            /* Append the token's character to the current node. */
-            $this->insertText($token['data']);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-            /* A start tag token whose tag name is "option" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            $token['name'] === 'option'
-        ) {
-            /* If the current node is an option element, act as if an end tag
-            with the tag name "option" had been seen. */
-            if (end($this->stack)->nodeName === 'option') {
-                $this->inSelect(
-                    array(
-                        'name' => 'option',
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-            }
-
-            /* Insert an HTML element for the token. */
-            $this->insertElement($token);
-
-            /* A start tag token whose tag name is "optgroup" */
-        } elseif ($token['type'] === HTML5::STARTTAG &&
-            $token['name'] === 'optgroup'
-        ) {
-            /* If the current node is an option element, act as if an end tag
-            with the tag name "option" had been seen. */
-            if (end($this->stack)->nodeName === 'option') {
-                $this->inSelect(
-                    array(
-                        'name' => 'option',
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-            }
-
-            /* If the current node is an optgroup element, act as if an end tag
-            with the tag name "optgroup" had been seen. */
-            if (end($this->stack)->nodeName === 'optgroup') {
-                $this->inSelect(
-                    array(
-                        'name' => 'optgroup',
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-            }
-
-            /* Insert an HTML element for the token. */
-            $this->insertElement($token);
-
-            /* An end tag token whose tag name is "optgroup" */
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            $token['name'] === 'optgroup'
-        ) {
-            /* First, if the current node is an option element, and the node
-            immediately before it in the stack of open elements is an optgroup
-            element, then act as if an end tag with the tag name "option" had
-            been seen. */
-            $elements_in_stack = count($this->stack);
-
-            if ($this->stack[$elements_in_stack - 1]->nodeName === 'option' &&
-                $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup'
-            ) {
-                $this->inSelect(
-                    array(
-                        'name' => 'option',
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-            }
-
-            /* If the current node is an optgroup element, then pop that node
-            from the stack of open elements. Otherwise, this is a parse error,
-            ignore the token. */
-            if ($this->stack[$elements_in_stack - 1] === 'optgroup') {
-                array_pop($this->stack);
-            }
-
-            /* An end tag token whose tag name is "option" */
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            $token['name'] === 'option'
-        ) {
-            /* If the current node is an option element, then pop that node
-            from the stack of open elements. Otherwise, this is a parse error,
-            ignore the token. */
-            if (end($this->stack)->nodeName === 'option') {
-                array_pop($this->stack);
-            }
-
-            /* An end tag whose tag name is "select" */
-        } elseif ($token['type'] === HTML5::ENDTAG &&
-            $token['name'] === 'select'
-        ) {
-            /* If the stack of open elements does not have an element in table
-            scope with the same tag name as the token, this is a parse error.
-            Ignore the token. (innerHTML case) */
-            if (!$this->elementInScope($token['name'], true)) {
-                // w/e
-
-                /* Otherwise: */
-            } else {
-                /* Pop elements from the stack of open elements until a select
-                element has been popped from the stack. */
-                while (true) {
-                    $current = end($this->stack)->nodeName;
-                    array_pop($this->stack);
-
-                    if ($current === 'select') {
-                        break;
-                    }
-                }
-
-                /* Reset the insertion mode appropriately. */
-                $this->resetInsertionMode();
-            }
-
-            /* A start tag whose tag name is "select" */
-        } elseif ($token['name'] === 'select' &&
-            $token['type'] === HTML5::STARTTAG
-        ) {
-            /* Parse error. Act as if the token had been an end tag with the
-            tag name "select" instead. */
-            $this->inSelect(
-                array(
-                    'name' => 'select',
-                    'type' => HTML5::ENDTAG
-                )
-            );
-
-            /* An end tag whose tag name is one of: "caption", "table", "tbody",
-            "tfoot", "thead", "tr", "td", "th" */
-        } elseif (in_array(
-                $token['name'],
-                array(
-                    'caption',
-                    'table',
-                    'tbody',
-                    'tfoot',
-                    'thead',
-                    'tr',
-                    'td',
-                    'th'
-                )
-            ) && $token['type'] === HTML5::ENDTAG
-        ) {
-            /* Parse error. */
-            // w/e
-
-            /* If the stack of open elements has an element in table scope with
-            the same tag name as that of the token, then act as if an end tag
-            with the tag name "select" had been seen, and reprocess the token.
-            Otherwise, ignore the token. */
-            if ($this->elementInScope($token['name'], true)) {
-                $this->inSelect(
-                    array(
-                        'name' => 'select',
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-
-                $this->mainPhase($token);
-            }
-
-            /* Anything else */
-        } else {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function afterBody($token)
-    {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        or U+0020 SPACE */
-        if ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Process the token as it would be processed if the insertion mode
-            was "in body". */
-            $this->inBody($token);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the first element in the stack of open
-            elements (the html element), with the data attribute set to the
-            data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            $this->stack[0]->appendChild($comment);
-
-            /* An end tag with the tag name "html" */
-        } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') {
-            /* If the parser was originally created in order to handle the
-            setting of an element's innerHTML attribute, this is a parse error;
-            ignore the token. (The element will be an html element in this
-            case.) (innerHTML case) */
-
-            /* Otherwise, switch to the trailing end phase. */
-            $this->phase = self::END_PHASE;
-
-            /* Anything else */
-        } else {
-            /* Parse error. Set the insertion mode to "in body" and reprocess
-            the token. */
-            $this->mode = self::IN_BODY;
-            return $this->inBody($token);
-        }
-    }
-
-    private function inFrameset($token)
-    {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
-        if ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-            /* A start tag with the tag name "frameset" */
-        } elseif ($token['name'] === 'frameset' &&
-            $token['type'] === HTML5::STARTTAG
-        ) {
-            $this->insertElement($token);
-
-            /* An end tag with the tag name "frameset" */
-        } elseif ($token['name'] === 'frameset' &&
-            $token['type'] === HTML5::ENDTAG
-        ) {
-            /* If the current node is the root html element, then this is a
-            parse error; ignore the token. (innerHTML case) */
-            if (end($this->stack)->nodeName === 'html') {
-                // Ignore
-
-            } else {
-                /* Otherwise, pop the current node from the stack of open
-                elements. */
-                array_pop($this->stack);
-
-                /* If the parser was not originally created in order to handle
-                the setting of an element's innerHTML attribute (innerHTML case),
-                and the current node is no longer a frameset element, then change
-                the insertion mode to "after frameset". */
-                $this->mode = self::AFTR_FRAME;
-            }
-
-            /* A start tag with the tag name "frame" */
-        } elseif ($token['name'] === 'frame' &&
-            $token['type'] === HTML5::STARTTAG
-        ) {
-            /* Insert an HTML element for the token. */
-            $this->insertElement($token);
-
-            /* Immediately pop the current node off the stack of open elements. */
-            array_pop($this->stack);
-
-            /* A start tag with the tag name "noframes" */
-        } elseif ($token['name'] === 'noframes' &&
-            $token['type'] === HTML5::STARTTAG
-        ) {
-            /* Process the token as if the insertion mode had been "in body". */
-            $this->inBody($token);
-
-            /* Anything else */
-        } else {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function afterFrameset($token)
-    {
-        /* Handle the token as follows: */
-
-        /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-        U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-        U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */
-        if ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Append the character to the current node. */
-            $this->insertText($token['data']);
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the current node with the data
-            attribute set to the data given in the comment token. */
-            $this->insertComment($token['data']);
-
-            /* An end tag with the tag name "html" */
-        } elseif ($token['name'] === 'html' &&
-            $token['type'] === HTML5::ENDTAG
-        ) {
-            /* Switch to the trailing end phase. */
-            $this->phase = self::END_PHASE;
-
-            /* A start tag with the tag name "noframes" */
-        } elseif ($token['name'] === 'noframes' &&
-            $token['type'] === HTML5::STARTTAG
-        ) {
-            /* Process the token as if the insertion mode had been "in body". */
-            $this->inBody($token);
-
-            /* Anything else */
-        } else {
-            /* Parse error. Ignore the token. */
-        }
-    }
-
-    private function trailingEndPhase($token)
-    {
-        /* After the main phase, as each token is emitted from the tokenisation
-        stage, it must be processed as described in this section. */
-
-        /* A DOCTYPE token */
-        if ($token['type'] === HTML5::DOCTYPE) {
-            // Parse error. Ignore the token.
-
-            /* A comment token */
-        } elseif ($token['type'] === HTML5::COMMENT) {
-            /* Append a Comment node to the Document object with the data
-            attribute set to the data given in the comment token. */
-            $comment = $this->dom->createComment($token['data']);
-            $this->dom->appendChild($comment);
-
-            /* A character token that is one of one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-            or U+0020 SPACE */
-        } elseif ($token['type'] === HTML5::CHARACTR &&
-            preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])
-        ) {
-            /* Process the token as it would be processed in the main phase. */
-            $this->mainPhase($token);
-
-            /* A character token that is not one of U+0009 CHARACTER TABULATION,
-            U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF),
-            or U+0020 SPACE. Or a start tag token. Or an end tag token. */
-        } elseif (($token['type'] === HTML5::CHARACTR &&
-                preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) ||
-            $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG
-        ) {
-            /* Parse error. Switch back to the main phase and reprocess the
-            token. */
-            $this->phase = self::MAIN_PHASE;
-            return $this->mainPhase($token);
-
-            /* An end-of-file token */
-        } elseif ($token['type'] === HTML5::EOF) {
-            /* OMG DONE!! */
-        }
-    }
-
-    private function insertElement($token, $append = true, $check = false)
-    {
-        // Proprietary workaround for libxml2's limitations with tag names
-        if ($check) {
-            // Slightly modified HTML5 tag-name modification,
-            // removing anything that's not an ASCII letter, digit, or hyphen
-            $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']);
-            // Remove leading hyphens and numbers
-            $token['name'] = ltrim($token['name'], '-0..9');
-            // In theory, this should ever be needed, but just in case
-            if ($token['name'] === '') {
-                $token['name'] = 'span';
-            } // arbitrary generic choice
-        }
-
-        $el = $this->dom->createElement($token['name']);
-
-        foreach ($token['attr'] as $attr) {
-            if (!$el->hasAttribute($attr['name'])) {
-                $el->setAttribute($attr['name'], (string)$attr['value']);
-            }
-        }
-
-        $this->appendToRealParent($el);
-        $this->stack[] = $el;
-
-        return $el;
-    }
-
-    private function insertText($data)
-    {
-        $text = $this->dom->createTextNode($data);
-        $this->appendToRealParent($text);
-    }
-
-    private function insertComment($data)
-    {
-        $comment = $this->dom->createComment($data);
-        $this->appendToRealParent($comment);
-    }
-
-    private function appendToRealParent($node)
-    {
-        if ($this->foster_parent === null) {
-            end($this->stack)->appendChild($node);
-
-        } elseif ($this->foster_parent !== null) {
-            /* If the foster parent element is the parent element of the
-            last table element in the stack of open elements, then the new
-            node must be inserted immediately before the last table element
-            in the stack of open elements in the foster parent element;
-            otherwise, the new node must be appended to the foster parent
-            element. */
-            for ($n = count($this->stack) - 1; $n >= 0; $n--) {
-                if ($this->stack[$n]->nodeName === 'table' &&
-                    $this->stack[$n]->parentNode !== null
-                ) {
-                    $table = $this->stack[$n];
-                    break;
-                }
-            }
-
-            if (isset($table) && $this->foster_parent->isSameNode($table->parentNode)) {
-                $this->foster_parent->insertBefore($node, $table);
-            } else {
-                $this->foster_parent->appendChild($node);
-            }
-
-            $this->foster_parent = null;
-        }
-    }
-
-    private function elementInScope($el, $table = false)
-    {
-        if (is_array($el)) {
-            foreach ($el as $element) {
-                if ($this->elementInScope($element, $table)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        $leng = count($this->stack);
-
-        for ($n = 0; $n < $leng; $n++) {
-            /* 1. Initialise node to be the current node (the bottommost node of
-            the stack). */
-            $node = $this->stack[$leng - 1 - $n];
-
-            if ($node->tagName === $el) {
-                /* 2. If node is the target node, terminate in a match state. */
-                return true;
-
-            } elseif ($node->tagName === 'table') {
-                /* 3. Otherwise, if node is a table element, terminate in a failure
-                state. */
-                return false;
-
-            } elseif ($table === true && in_array(
-                    $node->tagName,
-                    array(
-                        'caption',
-                        'td',
-                        'th',
-                        'button',
-                        'marquee',
-                        'object'
-                    )
-                )
-            ) {
-                /* 4. Otherwise, if the algorithm is the "has an element in scope"
-                variant (rather than the "has an element in table scope" variant),
-                and node is one of the following, terminate in a failure state. */
-                return false;
-
-            } elseif ($node === $node->ownerDocument->documentElement) {
-                /* 5. Otherwise, if node is an html element (root element), terminate
-                in a failure state. (This can only happen if the node is the topmost
-                node of the    stack of open elements, and prevents the next step from
-                being invoked if there are no more elements in the stack.) */
-                return false;
-            }
-
-            /* Otherwise, set node to the previous entry in the stack of open
-            elements and return to step 2. (This will never fail, since the loop
-            will always terminate in the previous step if the top of the stack
-            is reached.) */
-        }
-    }
-
-    private function reconstructActiveFormattingElements()
-    {
-        /* 1. If there are no entries in the list of active formatting elements,
-        then there is nothing to reconstruct; stop this algorithm. */
-        $formatting_elements = count($this->a_formatting);
-
-        if ($formatting_elements === 0) {
-            return false;
-        }
-
-        /* 3. Let entry be the last (most recently added) element in the list
-        of active formatting elements. */
-        $entry = end($this->a_formatting);
-
-        /* 2. If the last (most recently added) entry in the list of active
-        formatting elements is a marker, or if it is an element that is in the
-        stack of open elements, then there is nothing to reconstruct; stop this
-        algorithm. */
-        if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
-            return false;
-        }
-
-        for ($a = $formatting_elements - 1; $a >= 0; true) {
-            /* 4. If there are no entries before entry in the list of active
-            formatting elements, then jump to step 8. */
-            if ($a === 0) {
-                $step_seven = false;
-                break;
-            }
-
-            /* 5. Let entry be the entry one earlier than entry in the list of
-            active formatting elements. */
-            $a--;
-            $entry = $this->a_formatting[$a];
-
-            /* 6. If entry is neither a marker nor an element that is also in
-            thetack of open elements, go to step 4. */
-            if ($entry === self::MARKER || in_array($entry, $this->stack, true)) {
-                break;
-            }
-        }
-
-        while (true) {
-            /* 7. Let entry be the element one later than entry in the list of
-            active formatting elements. */
-            if (isset($step_seven) && $step_seven === true) {
-                $a++;
-                $entry = $this->a_formatting[$a];
-            }
-
-            /* 8. Perform a shallow clone of the element entry to obtain clone. */
-            $clone = $entry->cloneNode();
-
-            /* 9. Append clone to the current node and push it onto the stack
-            of open elements  so that it is the new current node. */
-            end($this->stack)->appendChild($clone);
-            $this->stack[] = $clone;
-
-            /* 10. Replace the entry for entry in the list with an entry for
-            clone. */
-            $this->a_formatting[$a] = $clone;
-
-            /* 11. If the entry for clone in the list of active formatting
-            elements is not the last entry in the list, return to step 7. */
-            if (end($this->a_formatting) !== $clone) {
-                $step_seven = true;
-            } else {
-                break;
-            }
-        }
-    }
-
-    private function clearTheActiveFormattingElementsUpToTheLastMarker()
-    {
-        /* When the steps below require the UA to clear the list of active
-        formatting elements up to the last marker, the UA must perform the
-        following steps: */
-
-        while (true) {
-            /* 1. Let entry be the last (most recently added) entry in the list
-            of active formatting elements. */
-            $entry = end($this->a_formatting);
-
-            /* 2. Remove entry from the list of active formatting elements. */
-            array_pop($this->a_formatting);
-
-            /* 3. If entry was a marker, then stop the algorithm at this point.
-            The list has been cleared up to the last marker. */
-            if ($entry === self::MARKER) {
-                break;
-            }
-        }
-    }
-
-    private function generateImpliedEndTags($exclude = array())
-    {
-        /* When the steps below require the UA to generate implied end tags,
-        then, if the current node is a dd element, a dt element, an li element,
-        a p element, a td element, a th  element, or a tr element, the UA must
-        act as if an end tag with the respective tag name had been seen and
-        then generate implied end tags again. */
-        $node = end($this->stack);
-        $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude);
-
-        while (in_array(end($this->stack)->nodeName, $elements)) {
-            array_pop($this->stack);
-        }
-    }
-
-    private function getElementCategory($node)
-    {
-        $name = $node->tagName;
-        if (in_array($name, $this->special)) {
-            return self::SPECIAL;
-        } elseif (in_array($name, $this->scoping)) {
-            return self::SCOPING;
-        } elseif (in_array($name, $this->formatting)) {
-            return self::FORMATTING;
-        } else {
-            return self::PHRASING;
-        }
-    }
-
-    private function clearStackToTableContext($elements)
-    {
-        /* When the steps above require the UA to clear the stack back to a
-        table context, it means that the UA must, while the current node is not
-        a table element or an html element, pop elements from the stack of open
-        elements. If this causes any elements to be popped from the stack, then
-        this is a parse error. */
-        while (true) {
-            $node = end($this->stack)->nodeName;
-
-            if (in_array($node, $elements)) {
-                break;
-            } else {
-                array_pop($this->stack);
-            }
-        }
-    }
-
-    private function resetInsertionMode()
-    {
-        /* 1. Let last be false. */
-        $last = false;
-        $leng = count($this->stack);
-
-        for ($n = $leng - 1; $n >= 0; $n--) {
-            /* 2. Let node be the last node in the stack of open elements. */
-            $node = $this->stack[$n];
-
-            /* 3. If node is the first node in the stack of open elements, then
-            set last to true. If the element whose innerHTML  attribute is being
-            set is neither a td  element nor a th element, then set node to the
-            element whose innerHTML  attribute is being set. (innerHTML  case) */
-            if ($this->stack[0]->isSameNode($node)) {
-                $last = true;
-            }
-
-            /* 4. If node is a select element, then switch the insertion mode to
-            "in select" and abort these steps. (innerHTML case) */
-            if ($node->nodeName === 'select') {
-                $this->mode = self::IN_SELECT;
-                break;
-
-                /* 5. If node is a td or th element, then switch the insertion mode
-                to "in cell" and abort these steps. */
-            } elseif ($node->nodeName === 'td' || $node->nodeName === 'th') {
-                $this->mode = self::IN_CELL;
-                break;
-
-                /* 6. If node is a tr element, then switch the insertion mode to
-                "in    row" and abort these steps. */
-            } elseif ($node->nodeName === 'tr') {
-                $this->mode = self::IN_ROW;
-                break;
-
-                /* 7. If node is a tbody, thead, or tfoot element, then switch the
-                insertion mode to "in table body" and abort these steps. */
-            } elseif (in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) {
-                $this->mode = self::IN_TBODY;
-                break;
-
-                /* 8. If node is a caption element, then switch the insertion mode
-                to "in caption" and abort these steps. */
-            } elseif ($node->nodeName === 'caption') {
-                $this->mode = self::IN_CAPTION;
-                break;
-
-                /* 9. If node is a colgroup element, then switch the insertion mode
-                to "in column group" and abort these steps. (innerHTML case) */
-            } elseif ($node->nodeName === 'colgroup') {
-                $this->mode = self::IN_CGROUP;
-                break;
-
-                /* 10. If node is a table element, then switch the insertion mode
-                to "in table" and abort these steps. */
-            } elseif ($node->nodeName === 'table') {
-                $this->mode = self::IN_TABLE;
-                break;
-
-                /* 11. If node is a head element, then switch the insertion mode
-                to "in body" ("in body"! not "in head"!) and abort these steps.
-                (innerHTML case) */
-            } elseif ($node->nodeName === 'head') {
-                $this->mode = self::IN_BODY;
-                break;
-
-                /* 12. If node is a body element, then switch the insertion mode to
-                "in body" and abort these steps. */
-            } elseif ($node->nodeName === 'body') {
-                $this->mode = self::IN_BODY;
-                break;
-
-                /* 13. If node is a frameset element, then switch the insertion
-                mode to "in frameset" and abort these steps. (innerHTML case) */
-            } elseif ($node->nodeName === 'frameset') {
-                $this->mode = self::IN_FRAME;
-                break;
-
-                /* 14. If node is an html element, then: if the head element
-                pointer is null, switch the insertion mode to "before head",
-                otherwise, switch the insertion mode to "after head". In either
-                case, abort these steps. (innerHTML case) */
-            } elseif ($node->nodeName === 'html') {
-                $this->mode = ($this->head_pointer === null)
-                    ? self::BEFOR_HEAD
-                    : self::AFTER_HEAD;
-
-                break;
-
-                /* 15. If last is true, then set the insertion mode to "in body"
-                and    abort these steps. (innerHTML case) */
-            } elseif ($last) {
-                $this->mode = self::IN_BODY;
-                break;
-            }
-        }
-    }
-
-    private function closeCell()
-    {
-        /* If the stack of open elements has a td or th element in table scope,
-        then act as if an end tag token with that tag name had been seen. */
-        foreach (array('td', 'th') as $cell) {
-            if ($this->elementInScope($cell, true)) {
-                $this->inCell(
-                    array(
-                        'name' => $cell,
-                        'type' => HTML5::ENDTAG
-                    )
-                );
-
-                break;
-            }
-        }
-    }
-
-    public function save()
-    {
-        return $this->dom;
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php
deleted file mode 100644
index 3995fec..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * Abstract base node class that all others inherit from.
- *
- * Why do we not use the DOM extension?  (1) It is not always available,
- * (2) it has funny constraints on the data it can represent,
- * whereas we want a maximally flexible representation, and (3) its
- * interface is a bit cumbersome.
- */
-abstract class HTMLPurifier_Node
-{
-    /**
-     * Line number of the start token in the source document
-     * @type int
-     */
-    public $line;
-
-    /**
-     * Column number of the start token in the source document. Null if unknown.
-     * @type int
-     */
-    public $col;
-
-    /**
-     * Lookup array of processing that this token is exempt from.
-     * Currently, valid values are "ValidateAttributes".
-     * @type array
-     */
-    public $armor = array();
-
-    /**
-     * When true, this node should be ignored as non-existent.
-     *
-     * Who is responsible for ignoring dead nodes?  FixNesting is
-     * responsible for removing them before passing on to child
-     * validators.
-     */
-    public $dead = false;
-
-    /**
-     * Returns a pair of start and end tokens, where the end token
-     * is null if it is not necessary. Does not include children.
-     * @type array
-     */
-    abstract public function toTokenPair();
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Comment.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Comment.php
deleted file mode 100644
index 38ba193..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Comment.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * Concrete comment node class.
- */
-class HTMLPurifier_Node_Comment extends HTMLPurifier_Node
-{
-    /**
-     * Character data within comment.
-     * @type string
-     */
-    public $data;
-
-    /**
-     * @type bool
-     */
-    public $is_whitespace = true;
-
-    /**
-     * Transparent constructor.
-     *
-     * @param string $data String comment data.
-     * @param int $line
-     * @param int $col
-     */
-    public function __construct($data, $line = null, $col = null)
-    {
-        $this->data = $data;
-        $this->line = $line;
-        $this->col = $col;
-    }
-
-    public function toTokenPair() {
-        return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null);
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php
deleted file mode 100644
index 6cbf56d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-/**
- * Concrete element node class.
- */
-class HTMLPurifier_Node_Element extends HTMLPurifier_Node
-{
-    /**
-     * The lower-case name of the tag, like 'a', 'b' or 'blockquote'.
-     *
-     * @note Strictly speaking, XML tags are case sensitive, so we shouldn't
-     * be lower-casing them, but these tokens cater to HTML tags, which are
-     * insensitive.
-     * @type string
-     */
-    public $name;
-
-    /**
-     * Associative array of the node's attributes.
-     * @type array
-     */
-    public $attr = array();
-
-    /**
-     * List of child elements.
-     * @type array
-     */
-    public $children = array();
-
-    /**
-     * Does this use the <a></a> form or the </a> form, i.e.
-     * is it a pair of start/end tokens or an empty token.
-     * @bool
-     */
-    public $empty = false;
-
-    public $endCol = null, $endLine = null, $endArmor = array();
-
-    public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) {
-        $this->name = $name;
-        $this->attr = $attr;
-        $this->line = $line;
-        $this->col = $col;
-        $this->armor = $armor;
-    }
-
-    public function toTokenPair() {
-        // XXX inefficiency here, normalization is not necessary
-        if ($this->empty) {
-            return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null);
-        } else {
-            $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor);
-            $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor);
-            //$end->start = $start;
-            return array($start, $end);
-        }
-    }
-}
-
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php
deleted file mode 100644
index aec9166..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * Concrete text token class.
- *
- * Text tokens comprise of regular parsed character data (PCDATA) and raw
- * character data (from the CDATA sections). Internally, their
- * data is parsed with all entities expanded. Surprisingly, the text token
- * does have a "tag name" called #PCDATA, which is how the DTD represents it
- * in permissible child nodes.
- */
-class HTMLPurifier_Node_Text extends HTMLPurifier_Node
-{
-
-    /**
-     * PCDATA tag name compatible with DTD, see
-     * HTMLPurifier_ChildDef_Custom for details.
-     * @type string
-     */
-    public $name = '#PCDATA';
-
-    /**
-     * @type string
-     */
-    public $data;
-    /**< Parsed character data of text. */
-
-    /**
-     * @type bool
-     */
-    public $is_whitespace;
-
-    /**< Bool indicating if node is whitespace. */
-
-    /**
-     * Constructor, accepts data and determines if it is whitespace.
-     * @param string $data String parsed character data.
-     * @param int $line
-     * @param int $col
-     */
-    public function __construct($data, $is_whitespace, $line = null, $col = null)
-    {
-        $this->data = $data;
-        $this->is_whitespace = $is_whitespace;
-        $this->line = $line;
-        $this->col = $col;
-    }
-
-    public function toTokenPair() {
-        return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php
deleted file mode 100644
index 18c8bbb..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-/**
- * Class that handles operations involving percent-encoding in URIs.
- *
- * @warning
- *      Be careful when reusing instances of PercentEncoder. The object
- *      you use for normalize() SHOULD NOT be used for encode(), or
- *      vice-versa.
- */
-class HTMLPurifier_PercentEncoder
-{
-
-    /**
-     * Reserved characters to preserve when using encode().
-     * @type array
-     */
-    protected $preserve = array();
-
-    /**
-     * String of characters that should be preserved while using encode().
-     * @param bool $preserve
-     */
-    public function __construct($preserve = false)
-    {
-        // unreserved letters, ought to const-ify
-        for ($i = 48; $i <= 57; $i++) { // digits
-            $this->preserve[$i] = true;
-        }
-        for ($i = 65; $i <= 90; $i++) { // upper-case
-            $this->preserve[$i] = true;
-        }
-        for ($i = 97; $i <= 122; $i++) { // lower-case
-            $this->preserve[$i] = true;
-        }
-        $this->preserve[45] = true; // Dash         -
-        $this->preserve[46] = true; // Period       .
-        $this->preserve[95] = true; // Underscore   _
-        $this->preserve[126]= true; // Tilde        ~
-
-        // extra letters not to escape
-        if ($preserve !== false) {
-            for ($i = 0, $c = strlen($preserve); $i < $c; $i++) {
-                $this->preserve[ord($preserve[$i])] = true;
-            }
-        }
-    }
-
-    /**
-     * Our replacement for urlencode, it encodes all non-reserved characters,
-     * as well as any extra characters that were instructed to be preserved.
-     * @note
-     *      Assumes that the string has already been normalized, making any
-     *      and all percent escape sequences valid. Percents will not be
-     *      re-escaped, regardless of their status in $preserve
-     * @param string $string String to be encoded
-     * @return string Encoded string.
-     */
-    public function encode($string)
-    {
-        $ret = '';
-        for ($i = 0, $c = strlen($string); $i < $c; $i++) {
-            if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) {
-                $ret .= '%' . sprintf('%02X', $int);
-            } else {
-                $ret .= $string[$i];
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * Fix up percent-encoding by decoding unreserved characters and normalizing.
-     * @warning This function is affected by $preserve, even though the
-     *          usual desired behavior is for this not to preserve those
-     *          characters. Be careful when reusing instances of PercentEncoder!
-     * @param string $string String to normalize
-     * @return string
-     */
-    public function normalize($string)
-    {
-        if ($string == '') {
-            return '';
-        }
-        $parts = explode('%', $string);
-        $ret = array_shift($parts);
-        foreach ($parts as $part) {
-            $length = strlen($part);
-            if ($length < 2) {
-                $ret .= '%25' . $part;
-                continue;
-            }
-            $encoding = substr($part, 0, 2);
-            $text     = substr($part, 2);
-            if (!ctype_xdigit($encoding)) {
-                $ret .= '%25' . $part;
-                continue;
-            }
-            $int = hexdec($encoding);
-            if (isset($this->preserve[$int])) {
-                $ret .= chr($int) . $text;
-                continue;
-            }
-            $encoding = strtoupper($encoding);
-            $ret .= '%' . $encoding . $text;
-        }
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php
deleted file mode 100644
index 549e4ce..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php
+++ /dev/null
@@ -1,218 +0,0 @@
-<?php
-
-// OUT OF DATE, NEEDS UPDATING!
-// USE XMLWRITER!
-
-class HTMLPurifier_Printer
-{
-
-    /**
-     * For HTML generation convenience funcs.
-     * @type HTMLPurifier_Generator
-     */
-    protected $generator;
-
-    /**
-     * For easy access.
-     * @type HTMLPurifier_Config
-     */
-    protected $config;
-
-    /**
-     * Initialize $generator.
-     */
-    public function __construct()
-    {
-    }
-
-    /**
-     * Give generator necessary configuration if possible
-     * @param HTMLPurifier_Config $config
-     */
-    public function prepareGenerator($config)
-    {
-        $all = $config->getAll();
-        $context = new HTMLPurifier_Context();
-        $this->generator = new HTMLPurifier_Generator($config, $context);
-    }
-
-    /**
-     * Main function that renders object or aspect of that object
-     * @note Parameters vary depending on printer
-     */
-    // function render() {}
-
-    /**
-     * Returns a start tag
-     * @param string $tag Tag name
-     * @param array $attr Attribute array
-     * @return string
-     */
-    protected function start($tag, $attr = array())
-    {
-        return $this->generator->generateFromToken(
-            new HTMLPurifier_Token_Start($tag, $attr ? $attr : array())
-        );
-    }
-
-    /**
-     * Returns an end tag
-     * @param string $tag Tag name
-     * @return string
-     */
-    protected function end($tag)
-    {
-        return $this->generator->generateFromToken(
-            new HTMLPurifier_Token_End($tag)
-        );
-    }
-
-    /**
-     * Prints a complete element with content inside
-     * @param string $tag Tag name
-     * @param string $contents Element contents
-     * @param array $attr Tag attributes
-     * @param bool $escape whether or not to escape contents
-     * @return string
-     */
-    protected function element($tag, $contents, $attr = array(), $escape = true)
-    {
-        return $this->start($tag, $attr) .
-            ($escape ? $this->escape($contents) : $contents) .
-            $this->end($tag);
-    }
-
-    /**
-     * @param string $tag
-     * @param array $attr
-     * @return string
-     */
-    protected function elementEmpty($tag, $attr = array())
-    {
-        return $this->generator->generateFromToken(
-            new HTMLPurifier_Token_Empty($tag, $attr)
-        );
-    }
-
-    /**
-     * @param string $text
-     * @return string
-     */
-    protected function text($text)
-    {
-        return $this->generator->generateFromToken(
-            new HTMLPurifier_Token_Text($text)
-        );
-    }
-
-    /**
-     * Prints a simple key/value row in a table.
-     * @param string $name Key
-     * @param mixed $value Value
-     * @return string
-     */
-    protected function row($name, $value)
-    {
-        if (is_bool($value)) {
-            $value = $value ? 'On' : 'Off';
-        }
-        return
-            $this->start('tr') . "\n" .
-            $this->element('th', $name) . "\n" .
-            $this->element('td', $value) . "\n" .
-            $this->end('tr');
-    }
-
-    /**
-     * Escapes a string for HTML output.
-     * @param string $string String to escape
-     * @return string
-     */
-    protected function escape($string)
-    {
-        $string = HTMLPurifier_Encoder::cleanUTF8($string);
-        $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
-        return $string;
-    }
-
-    /**
-     * Takes a list of strings and turns them into a single list
-     * @param string[] $array List of strings
-     * @param bool $polite Bool whether or not to add an end before the last
-     * @return string
-     */
-    protected function listify($array, $polite = false)
-    {
-        if (empty($array)) {
-            return 'None';
-        }
-        $ret = '';
-        $i = count($array);
-        foreach ($array as $value) {
-            $i--;
-            $ret .= $value;
-            if ($i > 0 && !($polite && $i == 1)) {
-                $ret .= ', ';
-            }
-            if ($polite && $i == 1) {
-                $ret .= 'and ';
-            }
-        }
-        return $ret;
-    }
-
-    /**
-     * Retrieves the class of an object without prefixes, as well as metadata
-     * @param object $obj Object to determine class of
-     * @param string $sec_prefix Further prefix to remove
-     * @return string
-     */
-    protected function getClass($obj, $sec_prefix = '')
-    {
-        static $five = null;
-        if ($five === null) {
-            $five = version_compare(PHP_VERSION, '5', '>=');
-        }
-        $prefix = 'HTMLPurifier_' . $sec_prefix;
-        if (!$five) {
-            $prefix = strtolower($prefix);
-        }
-        $class = str_replace($prefix, '', get_class($obj));
-        $lclass = strtolower($class);
-        $class .= '(';
-        switch ($lclass) {
-            case 'enum':
-                $values = array();
-                foreach ($obj->valid_values as $value => $bool) {
-                    $values[] = $value;
-                }
-                $class .= implode(', ', $values);
-                break;
-            case 'css_composite':
-                $values = array();
-                foreach ($obj->defs as $def) {
-                    $values[] = $this->getClass($def, $sec_prefix);
-                }
-                $class .= implode(', ', $values);
-                break;
-            case 'css_multiple':
-                $class .= $this->getClass($obj->single, $sec_prefix) . ', ';
-                $class .= $obj->max;
-                break;
-            case 'css_denyelementdecorator':
-                $class .= $this->getClass($obj->def, $sec_prefix) . ', ';
-                $class .= $obj->element;
-                break;
-            case 'css_importantdecorator':
-                $class .= $this->getClass($obj->def, $sec_prefix);
-                if ($obj->allow) {
-                    $class .= ', !important';
-                }
-                break;
-        }
-        $class .= ')';
-        return $class;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php
deleted file mode 100644
index 29505fe..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer
-{
-    /**
-     * @type HTMLPurifier_CSSDefinition
-     */
-    protected $def;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return string
-     */
-    public function render($config)
-    {
-        $this->def = $config->getCSSDefinition();
-        $ret = '';
-
-        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
-        $ret .= $this->start('table');
-
-        $ret .= $this->element('caption', 'Properties ($info)');
-
-        $ret .= $this->start('thead');
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', 'Property', array('class' => 'heavy'));
-        $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;'));
-        $ret .= $this->end('tr');
-        $ret .= $this->end('thead');
-
-        ksort($this->def->info);
-        foreach ($this->def->info as $property => $obj) {
-            $name = $this->getClass($obj, 'AttrDef_');
-            $ret .= $this->row($property, $name);
-        }
-
-        $ret .= $this->end('table');
-        $ret .= $this->end('div');
-
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css
deleted file mode 100644
index 3ff1a88..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css
+++ /dev/null
@@ -1,10 +0,0 @@
-
-.hp-config {}
-
-.hp-config tbody th {text-align:right; padding-right:0.5em;}
-.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;}
-.hp-config .namespace th {text-align:center;}
-.hp-config .verbose {display:none;}
-.hp-config .controls {text-align:center;}
-
-/* vim: et sw=4 sts=4 */
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js
deleted file mode 100644
index cba00c9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js
+++ /dev/null
@@ -1,5 +0,0 @@
-function toggleWriteability(id_of_patient, checked) {
-    document.getElementById(id_of_patient).disabled = checked;
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php
deleted file mode 100644
index 33ae113..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php
+++ /dev/null
@@ -1,451 +0,0 @@
-<?php
-
-/**
- * @todo Rewrite to use Interchange objects
- */
-class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
-{
-
-    /**
-     * Printers for specific fields.
-     * @type HTMLPurifier_Printer[]
-     */
-    protected $fields = array();
-
-    /**
-     * Documentation URL, can have fragment tagged on end.
-     * @type string
-     */
-    protected $docURL;
-
-    /**
-     * Name of form element to stuff config in.
-     * @type string
-     */
-    protected $name;
-
-    /**
-     * Whether or not to compress directive names, clipping them off
-     * after a certain amount of letters. False to disable or integer letters
-     * before clipping.
-     * @type bool
-     */
-    protected $compress = false;
-
-    /**
-     * @param string $name Form element name for directives to be stuffed into
-     * @param string $doc_url String documentation URL, will have fragment tagged on
-     * @param bool $compress Integer max length before compressing a directive name, set to false to turn off
-     */
-    public function __construct(
-        $name,
-        $doc_url = null,
-        $compress = false
-    ) {
-        parent::__construct();
-        $this->docURL = $doc_url;
-        $this->name = $name;
-        $this->compress = $compress;
-        // initialize sub-printers
-        $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default();
-        $this->fields[HTMLPurifier_VarParser::C_BOOL] = new HTMLPurifier_Printer_ConfigForm_bool();
-    }
-
-    /**
-     * Sets default column and row size for textareas in sub-printers
-     * @param $cols Integer columns of textarea, null to use default
-     * @param $rows Integer rows of textarea, null to use default
-     */
-    public function setTextareaDimensions($cols = null, $rows = null)
-    {
-        if ($cols) {
-            $this->fields['default']->cols = $cols;
-        }
-        if ($rows) {
-            $this->fields['default']->rows = $rows;
-        }
-    }
-
-    /**
-     * Retrieves styling, in case it is not accessible by webserver
-     */
-    public static function getCSS()
-    {
-        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
-    }
-
-    /**
-     * Retrieves JavaScript, in case it is not accessible by webserver
-     */
-    public static function getJavaScript()
-    {
-        return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
-    }
-
-    /**
-     * Returns HTML output for a configuration form
-     * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array
-     *        where [0] has an HTML namespace and [1] is being rendered.
-     * @param array|bool $allowed Optional namespace(s) and directives to restrict form to.
-     * @param bool $render_controls
-     * @return string
-     */
-    public function render($config, $allowed = true, $render_controls = true)
-    {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-
-        $this->config = $config;
-        $this->genConfig = $gen_config;
-        $this->prepareGenerator($gen_config);
-
-        $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);
-        $all = array();
-        foreach ($allowed as $key) {
-            list($ns, $directive) = $key;
-            $all[$ns][$directive] = $config->get($ns . '.' . $directive);
-        }
-
-        $ret = '';
-        $ret .= $this->start('table', array('class' => 'hp-config'));
-        $ret .= $this->start('thead');
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
-        $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
-        $ret .= $this->end('tr');
-        $ret .= $this->end('thead');
-        foreach ($all as $ns => $directives) {
-            $ret .= $this->renderNamespace($ns, $directives);
-        }
-        if ($render_controls) {
-            $ret .= $this->start('tbody');
-            $ret .= $this->start('tr');
-            $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
-            $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
-            $ret .= '[<a href="?">Reset</a>]';
-            $ret .= $this->end('td');
-            $ret .= $this->end('tr');
-            $ret .= $this->end('tbody');
-        }
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders a single namespace
-     * @param $ns String namespace name
-     * @param array $directives array of directives to values
-     * @return string
-     */
-    protected function renderNamespace($ns, $directives)
-    {
-        $ret = '';
-        $ret .= $this->start('tbody', array('class' => 'namespace'));
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', $ns, array('colspan' => 2));
-        $ret .= $this->end('tr');
-        $ret .= $this->end('tbody');
-        $ret .= $this->start('tbody');
-        foreach ($directives as $directive => $value) {
-            $ret .= $this->start('tr');
-            $ret .= $this->start('th');
-            if ($this->docURL) {
-                $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
-                $ret .= $this->start('a', array('href' => $url));
-            }
-            $attr = array('for' => "{$this->name}:$ns.$directive");
-
-            // crop directive name if it's too long
-            if (!$this->compress || (strlen($directive) < $this->compress)) {
-                $directive_disp = $directive;
-            } else {
-                $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
-                $attr['title'] = $directive;
-            }
-
-            $ret .= $this->element(
-                'label',
-                $directive_disp,
-                // component printers must create an element with this id
-                $attr
-            );
-            if ($this->docURL) {
-                $ret .= $this->end('a');
-            }
-            $ret .= $this->end('th');
-
-            $ret .= $this->start('td');
-            $def = $this->config->def->info["$ns.$directive"];
-            if (is_int($def)) {
-                $allow_null = $def < 0;
-                $type = abs($def);
-            } else {
-                $type = $def->type;
-                $allow_null = isset($def->allow_null);
-            }
-            if (!isset($this->fields[$type])) {
-                $type = 0;
-            } // default
-            $type_obj = $this->fields[$type];
-            if ($allow_null) {
-                $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
-            }
-            $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
-            $ret .= $this->end('td');
-            $ret .= $this->end('tr');
-        }
-        $ret .= $this->end('tbody');
-        return $ret;
-    }
-
-}
-
-/**
- * Printer decorator for directives that accept null
- */
-class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
-{
-    /**
-     * Printer being decorated
-     * @type HTMLPurifier_Printer
-     */
-    protected $obj;
-
-    /**
-     * @param HTMLPurifier_Printer $obj Printer to decorate
-     */
-    public function __construct($obj)
-    {
-        parent::__construct();
-        $this->obj = $obj;
-    }
-
-    /**
-     * @param string $ns
-     * @param string $directive
-     * @param string $value
-     * @param string $name
-     * @param HTMLPurifier_Config|array $config
-     * @return string
-     */
-    public function render($ns, $directive, $value, $name, $config)
-    {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-        $this->prepareGenerator($gen_config);
-
-        $ret = '';
-        $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
-        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
-        $ret .= $this->text(' Null/Disabled');
-        $ret .= $this->end('label');
-        $attr = array(
-            'type' => 'checkbox',
-            'value' => '1',
-            'class' => 'null-toggle',
-            'name' => "$name" . "[Null_$ns.$directive]",
-            'id' => "$name:Null_$ns.$directive",
-            'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
-        );
-        if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
-            // modify inline javascript slightly
-            $attr['onclick'] =
-                "toggleWriteability('$name:Yes_$ns.$directive',checked);" .
-                "toggleWriteability('$name:No_$ns.$directive',checked)";
-        }
-        if ($value === null) {
-            $attr['checked'] = 'checked';
-        }
-        $ret .= $this->elementEmpty('input', $attr);
-        $ret .= $this->text(' or ');
-        $ret .= $this->elementEmpty('br');
-        $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config));
-        return $ret;
-    }
-}
-
-/**
- * Swiss-army knife configuration form field printer
- */
-class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
-{
-    /**
-     * @type int
-     */
-    public $cols = 18;
-
-    /**
-     * @type int
-     */
-    public $rows = 5;
-
-    /**
-     * @param string $ns
-     * @param string $directive
-     * @param string $value
-     * @param string $name
-     * @param HTMLPurifier_Config|array $config
-     * @return string
-     */
-    public function render($ns, $directive, $value, $name, $config)
-    {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-        $this->prepareGenerator($gen_config);
-        // this should probably be split up a little
-        $ret = '';
-        $def = $config->def->info["$ns.$directive"];
-        if (is_int($def)) {
-            $type = abs($def);
-        } else {
-            $type = $def->type;
-        }
-        if (is_array($value)) {
-            switch ($type) {
-                case HTMLPurifier_VarParser::LOOKUP:
-                    $array = $value;
-                    $value = array();
-                    foreach ($array as $val => $b) {
-                        $value[] = $val;
-                    }
-                    //TODO does this need a break?
-                case HTMLPurifier_VarParser::ALIST:
-                    $value = implode(PHP_EOL, $value);
-                    break;
-                case HTMLPurifier_VarParser::HASH:
-                    $nvalue = '';
-                    foreach ($value as $i => $v) {
-                        if (is_array($v)) {
-                            // HACK
-                            $v = implode(";", $v);
-                        }
-                        $nvalue .= "$i:$v" . PHP_EOL;
-                    }
-                    $value = $nvalue;
-                    break;
-                default:
-                    $value = '';
-            }
-        }
-        if ($type === HTMLPurifier_VarParser::C_MIXED) {
-            return 'Not supported';
-            $value = serialize($value);
-        }
-        $attr = array(
-            'name' => "$name" . "[$ns.$directive]",
-            'id' => "$name:$ns.$directive"
-        );
-        if ($value === null) {
-            $attr['disabled'] = 'disabled';
-        }
-        if (isset($def->allowed)) {
-            $ret .= $this->start('select', $attr);
-            foreach ($def->allowed as $val => $b) {
-                $attr = array();
-                if ($value == $val) {
-                    $attr['selected'] = 'selected';
-                }
-                $ret .= $this->element('option', $val, $attr);
-            }
-            $ret .= $this->end('select');
-        } elseif ($type === HTMLPurifier_VarParser::TEXT ||
-                $type === HTMLPurifier_VarParser::ITEXT ||
-                $type === HTMLPurifier_VarParser::ALIST ||
-                $type === HTMLPurifier_VarParser::HASH ||
-                $type === HTMLPurifier_VarParser::LOOKUP) {
-            $attr['cols'] = $this->cols;
-            $attr['rows'] = $this->rows;
-            $ret .= $this->start('textarea', $attr);
-            $ret .= $this->text($value);
-            $ret .= $this->end('textarea');
-        } else {
-            $attr['value'] = $value;
-            $attr['type'] = 'text';
-            $ret .= $this->elementEmpty('input', $attr);
-        }
-        return $ret;
-    }
-}
-
-/**
- * Bool form field printer
- */
-class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer
-{
-    /**
-     * @param string $ns
-     * @param string $directive
-     * @param string $value
-     * @param string $name
-     * @param HTMLPurifier_Config|array $config
-     * @return string
-     */
-    public function render($ns, $directive, $value, $name, $config)
-    {
-        if (is_array($config) && isset($config[0])) {
-            $gen_config = $config[0];
-            $config = $config[1];
-        } else {
-            $gen_config = $config;
-        }
-        $this->prepareGenerator($gen_config);
-        $ret = '';
-        $ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
-
-        $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive"));
-        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
-        $ret .= $this->text(' Yes');
-        $ret .= $this->end('label');
-
-        $attr = array(
-            'type' => 'radio',
-            'name' => "$name" . "[$ns.$directive]",
-            'id' => "$name:Yes_$ns.$directive",
-            'value' => '1'
-        );
-        if ($value === true) {
-            $attr['checked'] = 'checked';
-        }
-        if ($value === null) {
-            $attr['disabled'] = 'disabled';
-        }
-        $ret .= $this->elementEmpty('input', $attr);
-
-        $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
-        $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
-        $ret .= $this->text(' No');
-        $ret .= $this->end('label');
-
-        $attr = array(
-            'type' => 'radio',
-            'name' => "$name" . "[$ns.$directive]",
-            'id' => "$name:No_$ns.$directive",
-            'value' => '0'
-        );
-        if ($value === false) {
-            $attr['checked'] = 'checked';
-        }
-        if ($value === null) {
-            $attr['disabled'] = 'disabled';
-        }
-        $ret .= $this->elementEmpty('input', $attr);
-
-        $ret .= $this->end('div');
-
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php
deleted file mode 100644
index ae86391..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php
+++ /dev/null
@@ -1,324 +0,0 @@
-<?php
-
-class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer
-{
-
-    /**
-     * @type HTMLPurifier_HTMLDefinition, for easy access
-     */
-    protected $def;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return string
-     */
-    public function render($config)
-    {
-        $ret = '';
-        $this->config =& $config;
-
-        $this->def = $config->getHTMLDefinition();
-
-        $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer'));
-
-        $ret .= $this->renderDoctype();
-        $ret .= $this->renderEnvironment();
-        $ret .= $this->renderContentSets();
-        $ret .= $this->renderInfo();
-
-        $ret .= $this->end('div');
-
-        return $ret;
-    }
-
-    /**
-     * Renders the Doctype table
-     * @return string
-     */
-    protected function renderDoctype()
-    {
-        $doctype = $this->def->doctype;
-        $ret = '';
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Doctype');
-        $ret .= $this->row('Name', $doctype->name);
-        $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No');
-        $ret .= $this->row('Default Modules', implode(', ', $doctype->modules));
-        $ret .= $this->row('Default Tidy Modules', implode(', ', $doctype->tidyModules));
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-
-    /**
-     * Renders environment table, which is miscellaneous info
-     * @return string
-     */
-    protected function renderEnvironment()
-    {
-        $def = $this->def;
-
-        $ret = '';
-
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Environment');
-
-        $ret .= $this->row('Parent of fragment', $def->info_parent);
-        $ret .= $this->renderChildren($def->info_parent_def->child);
-        $ret .= $this->row('Block wrap name', $def->info_block_wrapper);
-
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', 'Global attributes');
-        $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0);
-        $ret .= $this->end('tr');
-
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', 'Tag transforms');
-        $list = array();
-        foreach ($def->info_tag_transform as $old => $new) {
-            $new = $this->getClass($new, 'TagTransform_');
-            $list[] = "<$old> with $new";
-        }
-        $ret .= $this->element('td', $this->listify($list));
-        $ret .= $this->end('tr');
-
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', 'Pre-AttrTransform');
-        $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre));
-        $ret .= $this->end('tr');
-
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', 'Post-AttrTransform');
-        $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post));
-        $ret .= $this->end('tr');
-
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders the Content Sets table
-     * @return string
-     */
-    protected function renderContentSets()
-    {
-        $ret = '';
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Content Sets');
-        foreach ($this->def->info_content_sets as $name => $lookup) {
-            $ret .= $this->heavyHeader($name);
-            $ret .= $this->start('tr');
-            $ret .= $this->element('td', $this->listifyTagLookup($lookup));
-            $ret .= $this->end('tr');
-        }
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders the Elements ($info) table
-     * @return string
-     */
-    protected function renderInfo()
-    {
-        $ret = '';
-        $ret .= $this->start('table');
-        $ret .= $this->element('caption', 'Elements ($info)');
-        ksort($this->def->info);
-        $ret .= $this->heavyHeader('Allowed tags', 2);
-        $ret .= $this->start('tr');
-        $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2));
-        $ret .= $this->end('tr');
-        foreach ($this->def->info as $name => $def) {
-            $ret .= $this->start('tr');
-            $ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2));
-            $ret .= $this->end('tr');
-            $ret .= $this->start('tr');
-            $ret .= $this->element('th', 'Inline content');
-            $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No');
-            $ret .= $this->end('tr');
-            if (!empty($def->excludes)) {
-                $ret .= $this->start('tr');
-                $ret .= $this->element('th', 'Excludes');
-                $ret .= $this->element('td', $this->listifyTagLookup($def->excludes));
-                $ret .= $this->end('tr');
-            }
-            if (!empty($def->attr_transform_pre)) {
-                $ret .= $this->start('tr');
-                $ret .= $this->element('th', 'Pre-AttrTransform');
-                $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre));
-                $ret .= $this->end('tr');
-            }
-            if (!empty($def->attr_transform_post)) {
-                $ret .= $this->start('tr');
-                $ret .= $this->element('th', 'Post-AttrTransform');
-                $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post));
-                $ret .= $this->end('tr');
-            }
-            if (!empty($def->auto_close)) {
-                $ret .= $this->start('tr');
-                $ret .= $this->element('th', 'Auto closed by');
-                $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close));
-                $ret .= $this->end('tr');
-            }
-            $ret .= $this->start('tr');
-            $ret .= $this->element('th', 'Allowed attributes');
-            $ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0);
-            $ret .= $this->end('tr');
-
-            if (!empty($def->required_attr)) {
-                $ret .= $this->row('Required attributes', $this->listify($def->required_attr));
-            }
-
-            $ret .= $this->renderChildren($def->child);
-        }
-        $ret .= $this->end('table');
-        return $ret;
-    }
-
-    /**
-     * Renders a row describing the allowed children of an element
-     * @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element
-     * @return string
-     */
-    protected function renderChildren($def)
-    {
-        $context = new HTMLPurifier_Context();
-        $ret = '';
-        $ret .= $this->start('tr');
-        $elements = array();
-        $attr = array();
-        if (isset($def->elements)) {
-            if ($def->type == 'strictblockquote') {
-                $def->validateChildren(array(), $this->config, $context);
-            }
-            $elements = $def->elements;
-        }
-        if ($def->type == 'chameleon') {
-            $attr['rowspan'] = 2;
-        } elseif ($def->type == 'empty') {
-            $elements = array();
-        } elseif ($def->type == 'table') {
-            $elements = array_flip(
-                array(
-                    'col',
-                    'caption',
-                    'colgroup',
-                    'thead',
-                    'tfoot',
-                    'tbody',
-                    'tr'
-                )
-            );
-        }
-        $ret .= $this->element('th', 'Allowed children', $attr);
-
-        if ($def->type == 'chameleon') {
-
-            $ret .= $this->element(
-                'td',
-                '<em>Block</em>: ' .
-                $this->escape($this->listifyTagLookup($def->block->elements)),
-                null,
-                0
-            );
-            $ret .= $this->end('tr');
-            $ret .= $this->start('tr');
-            $ret .= $this->element(
-                'td',
-                '<em>Inline</em>: ' .
-                $this->escape($this->listifyTagLookup($def->inline->elements)),
-                null,
-                0
-            );
-
-        } elseif ($def->type == 'custom') {
-
-            $ret .= $this->element(
-                'td',
-                '<em>' . ucfirst($def->type) . '</em>: ' .
-                $def->dtd_regex
-            );
-
-        } else {
-            $ret .= $this->element(
-                'td',
-                '<em>' . ucfirst($def->type) . '</em>: ' .
-                $this->escape($this->listifyTagLookup($elements)),
-                null,
-                0
-            );
-        }
-        $ret .= $this->end('tr');
-        return $ret;
-    }
-
-    /**
-     * Listifies a tag lookup table.
-     * @param array $array Tag lookup array in form of array('tagname' => true)
-     * @return string
-     */
-    protected function listifyTagLookup($array)
-    {
-        ksort($array);
-        $list = array();
-        foreach ($array as $name => $discard) {
-            if ($name !== '#PCDATA' && !isset($this->def->info[$name])) {
-                continue;
-            }
-            $list[] = $name;
-        }
-        return $this->listify($list);
-    }
-
-    /**
-     * Listifies a list of objects by retrieving class names and internal state
-     * @param array $array List of objects
-     * @return string
-     * @todo Also add information about internal state
-     */
-    protected function listifyObjectList($array)
-    {
-        ksort($array);
-        $list = array();
-        foreach ($array as $obj) {
-            $list[] = $this->getClass($obj, 'AttrTransform_');
-        }
-        return $this->listify($list);
-    }
-
-    /**
-     * Listifies a hash of attributes to AttrDef classes
-     * @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef)
-     * @return string
-     */
-    protected function listifyAttr($array)
-    {
-        ksort($array);
-        $list = array();
-        foreach ($array as $name => $obj) {
-            if ($obj === false) {
-                continue;
-            }
-            $list[] = "$name&nbsp;=&nbsp;<i>" . $this->getClass($obj, 'AttrDef_') . '</i>';
-        }
-        return $this->listify($list);
-    }
-
-    /**
-     * Creates a heavy header row
-     * @param string $text
-     * @param int $num
-     * @return string
-     */
-    protected function heavyHeader($text, $num = 1)
-    {
-        $ret = '';
-        $ret .= $this->start('tr');
-        $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy'));
-        $ret .= $this->end('tr');
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php
deleted file mode 100644
index 189348f..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php
+++ /dev/null
@@ -1,122 +0,0 @@
-<?php
-
-/**
- * Generic property list implementation
- */
-class HTMLPurifier_PropertyList
-{
-    /**
-     * Internal data-structure for properties.
-     * @type array
-     */
-    protected $data = array();
-
-    /**
-     * Parent plist.
-     * @type HTMLPurifier_PropertyList
-     */
-    protected $parent;
-
-    /**
-     * Cache.
-     * @type array
-     */
-    protected $cache;
-
-    /**
-     * @param HTMLPurifier_PropertyList $parent Parent plist
-     */
-    public function __construct($parent = null)
-    {
-        $this->parent = $parent;
-    }
-
-    /**
-     * Recursively retrieves the value for a key
-     * @param string $name
-     * @throws HTMLPurifier_Exception
-     */
-    public function get($name)
-    {
-        if ($this->has($name)) {
-            return $this->data[$name];
-        }
-        // possible performance bottleneck, convert to iterative if necessary
-        if ($this->parent) {
-            return $this->parent->get($name);
-        }
-        throw new HTMLPurifier_Exception("Key '$name' not found");
-    }
-
-    /**
-     * Sets the value of a key, for this plist
-     * @param string $name
-     * @param mixed $value
-     */
-    public function set($name, $value)
-    {
-        $this->data[$name] = $value;
-    }
-
-    /**
-     * Returns true if a given key exists
-     * @param string $name
-     * @return bool
-     */
-    public function has($name)
-    {
-        return array_key_exists($name, $this->data);
-    }
-
-    /**
-     * Resets a value to the value of it's parent, usually the default. If
-     * no value is specified, the entire plist is reset.
-     * @param string $name
-     */
-    public function reset($name = null)
-    {
-        if ($name == null) {
-            $this->data = array();
-        } else {
-            unset($this->data[$name]);
-        }
-    }
-
-    /**
-     * Squashes this property list and all of its property lists into a single
-     * array, and returns the array. This value is cached by default.
-     * @param bool $force If true, ignores the cache and regenerates the array.
-     * @return array
-     */
-    public function squash($force = false)
-    {
-        if ($this->cache !== null && !$force) {
-            return $this->cache;
-        }
-        if ($this->parent) {
-            return $this->cache = array_merge($this->parent->squash($force), $this->data);
-        } else {
-            return $this->cache = $this->data;
-        }
-    }
-
-    /**
-     * Returns the parent plist.
-     * @return HTMLPurifier_PropertyList
-     */
-    public function getParent()
-    {
-        return $this->parent;
-    }
-
-    /**
-     * Sets the parent plist.
-     * @param HTMLPurifier_PropertyList $plist Parent plist
-     */
-    public function setParent($plist)
-    {
-        $this->parent = $plist;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php
deleted file mode 100644
index f68fc8c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * Property list iterator. Do not instantiate this class directly.
- */
-class HTMLPurifier_PropertyListIterator extends FilterIterator
-{
-
-    /**
-     * @type int
-     */
-    protected $l;
-    /**
-     * @type string
-     */
-    protected $filter;
-
-    /**
-     * @param Iterator $iterator Array of data to iterate over
-     * @param string $filter Optional prefix to only allow values of
-     */
-    public function __construct(Iterator $iterator, $filter = null)
-    {
-        parent::__construct($iterator);
-        $this->l = strlen($filter);
-        $this->filter = $filter;
-    }
-
-    /**
-     * @return bool
-     */
-    #[\ReturnTypeWillChange]
-    public function accept()
-    {
-        $key = $this->getInnerIterator()->key();
-        if (strncmp($key, $this->filter, $this->l) !== 0) {
-            return false;
-        }
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php
deleted file mode 100644
index f58db90..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * A simple array-backed queue, based off of the classic Okasaki
- * persistent amortized queue.  The basic idea is to maintain two
- * stacks: an input stack and an output stack.  When the output
- * stack runs out, reverse the input stack and use it as the output
- * stack.
- *
- * We don't use the SPL implementation because it's only supported
- * on PHP 5.3 and later.
- *
- * Exercise: Prove that push/pop on this queue take amortized O(1) time.
- *
- * Exercise: Extend this queue to be a deque, while preserving amortized
- * O(1) time.  Some care must be taken on rebalancing to avoid quadratic
- * behaviour caused by repeatedly shuffling data from the input stack
- * to the output stack and back.
- */
-class HTMLPurifier_Queue {
-    private $input;
-    private $output;
-
-    public function __construct($input = array()) {
-        $this->input = $input;
-        $this->output = array();
-    }
-
-    /**
-     * Shifts an element off the front of the queue.
-     */
-    public function shift() {
-        if (empty($this->output)) {
-            $this->output = array_reverse($this->input);
-            $this->input = array();
-        }
-        if (empty($this->output)) {
-            return NULL;
-        }
-        return array_pop($this->output);
-    }
-
-    /**
-     * Pushes an element onto the front of the queue.
-     */
-    public function push($x) {
-        array_push($this->input, $x);
-    }
-
-    /**
-     * Checks if it's empty.
-     */
-    public function isEmpty() {
-        return empty($this->input) && empty($this->output);
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php
deleted file mode 100644
index e1ff3b7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * Supertype for classes that define a strategy for modifying/purifying tokens.
- *
- * While HTMLPurifier's core purpose is fixing HTML into something proper,
- * strategies provide plug points for extra configuration or even extra
- * features, such as custom tags, custom parsing of text, etc.
- */
-
-
-abstract class HTMLPurifier_Strategy
-{
-
-    /**
-     * Executes the strategy on the tokens.
-     *
-     * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token objects to be operated on.
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token[] Processed array of token objects.
-     */
-    abstract public function execute($tokens, $config, $context);
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php
deleted file mode 100644
index d7d35ce..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * Composite strategy that runs multiple strategies on tokens.
- */
-abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy
-{
-
-    /**
-     * List of strategies to run tokens through.
-     * @type HTMLPurifier_Strategy[]
-     */
-    protected $strategies = array();
-
-    /**
-     * @param HTMLPurifier_Token[] $tokens
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token[]
-     */
-    public function execute($tokens, $config, $context)
-    {
-        foreach ($this->strategies as $strategy) {
-            $tokens = $strategy->execute($tokens, $config, $context);
-        }
-        return $tokens;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php
deleted file mode 100644
index 4414c17..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * Core strategy composed of the big four strategies.
- */
-class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite
-{
-    public function __construct()
-    {
-        $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements();
-        $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed();
-        $this->strategies[] = new HTMLPurifier_Strategy_FixNesting();
-        $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php
deleted file mode 100644
index 6fa673d..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php
+++ /dev/null
@@ -1,181 +0,0 @@
-<?php
-
-/**
- * Takes a well formed list of tokens and fixes their nesting.
- *
- * HTML elements dictate which elements are allowed to be their children,
- * for example, you can't have a p tag in a span tag.  Other elements have
- * much more rigorous definitions: tables, for instance, require a specific
- * order for their elements.  There are also constraints not expressible by
- * document type definitions, such as the chameleon nature of ins/del
- * tags and global child exclusions.
- *
- * The first major objective of this strategy is to iterate through all
- * the nodes and determine whether or not their children conform to the
- * element's definition.  If they do not, the child definition may
- * optionally supply an amended list of elements that is valid or
- * require that the entire node be deleted (and the previous node
- * rescanned).
- *
- * The second objective is to ensure that explicitly excluded elements of
- * an element do not appear in its children.  Code that accomplishes this
- * task is pervasive through the strategy, though the two are distinct tasks
- * and could, theoretically, be seperated (although it's not recommended).
- *
- * @note Whether or not unrecognized children are silently dropped or
- *       translated into text depends on the child definitions.
- *
- * @todo Enable nodes to be bubbled out of the structure.  This is
- *       easier with our new algorithm.
- */
-
-class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
-{
-
-    /**
-     * @param HTMLPurifier_Token[] $tokens
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array|HTMLPurifier_Token[]
-     */
-    public function execute($tokens, $config, $context)
-    {
-
-        //####################################################################//
-        // Pre-processing
-
-        // O(n) pass to convert to a tree, so that we can efficiently
-        // refer to substrings
-        $top_node = HTMLPurifier_Arborize::arborize($tokens, $config, $context);
-
-        // get a copy of the HTML definition
-        $definition = $config->getHTMLDefinition();
-
-        $excludes_enabled = !$config->get('Core.DisableExcludes');
-
-        // setup the context variable 'IsInline', for chameleon processing
-        // is 'false' when we are not inline, 'true' when it must always
-        // be inline, and an integer when it is inline for a certain
-        // branch of the document tree
-        $is_inline = $definition->info_parent_def->descendants_are_inline;
-        $context->register('IsInline', $is_inline);
-
-        // setup error collector
-        $e =& $context->get('ErrorCollector', true);
-
-        //####################################################################//
-        // Loop initialization
-
-        // stack that contains all elements that are excluded
-        // it is organized by parent elements, similar to $stack,
-        // but it is only populated when an element with exclusions is
-        // processed, i.e. there won't be empty exclusions.
-        $exclude_stack = array($definition->info_parent_def->excludes);
-
-        // variable that contains the start token while we are processing
-        // nodes. This enables error reporting to do its job
-        $node = $top_node;
-        // dummy token
-        list($token, $d) = $node->toTokenPair();
-        $context->register('CurrentNode', $node);
-        $context->register('CurrentToken', $token);
-
-        //####################################################################//
-        // Loop
-
-        // We need to implement a post-order traversal iteratively, to
-        // avoid running into stack space limits.  This is pretty tricky
-        // to reason about, so we just manually stack-ify the recursive
-        // variant:
-        //
-        //  function f($node) {
-        //      foreach ($node->children as $child) {
-        //          f($child);
-        //      }
-        //      validate($node);
-        //  }
-        //
-        // Thus, we will represent a stack frame as array($node,
-        // $is_inline, stack of children)
-        // e.g. array_reverse($node->children) - already processed
-        // children.
-
-        $parent_def = $definition->info_parent_def;
-        $stack = array(
-            array($top_node,
-                  $parent_def->descendants_are_inline,
-                  $parent_def->excludes, // exclusions
-                  0)
-            );
-
-        while (!empty($stack)) {
-            list($node, $is_inline, $excludes, $ix) = array_pop($stack);
-            // recursive call
-            $go = false;
-            $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name];
-            while (isset($node->children[$ix])) {
-                $child = $node->children[$ix++];
-                if ($child instanceof HTMLPurifier_Node_Element) {
-                    $go = true;
-                    $stack[] = array($node, $is_inline, $excludes, $ix);
-                    $stack[] = array($child,
-                        // ToDo: I don't think it matters if it's def or
-                        // child_def, but double check this...
-                        $is_inline || $def->descendants_are_inline,
-                        empty($def->excludes) ? $excludes
-                                              : array_merge($excludes, $def->excludes),
-                        0);
-                    break;
-                }
-            };
-            if ($go) continue;
-            list($token, $d) = $node->toTokenPair();
-            // base case
-            if ($excludes_enabled && isset($excludes[$node->name])) {
-                $node->dead = true;
-                if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
-            } else {
-                // XXX I suppose it would be slightly more efficient to
-                // avoid the allocation here and have children
-                // strategies handle it
-                $children = array();
-                foreach ($node->children as $child) {
-                    if (!$child->dead) $children[] = $child;
-                }
-                $result = $def->child->validateChildren($children, $config, $context);
-                if ($result === true) {
-                    // nop
-                    $node->children = $children;
-                } elseif ($result === false) {
-                    $node->dead = true;
-                    if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
-                } else {
-                    $node->children = $result;
-                    if ($e) {
-                        // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators
-                        if (empty($result) && !empty($children)) {
-                            $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
-                        } else if ($result != $children) {
-                            $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
-                        }
-                    }
-                }
-            }
-        }
-
-        //####################################################################//
-        // Post-processing
-
-        // remove context variables
-        $context->destroy('IsInline');
-        $context->destroy('CurrentNode');
-        $context->destroy('CurrentToken');
-
-        //####################################################################//
-        // Return
-
-        return HTMLPurifier_Arborize::flatten($node, $config, $context);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php
deleted file mode 100644
index a6eb09e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php
+++ /dev/null
@@ -1,659 +0,0 @@
-<?php
-
-/**
- * Takes tokens makes them well-formed (balance end tags, etc.)
- *
- * Specification of the armor attributes this strategy uses:
- *
- *      - MakeWellFormed_TagClosedError: This armor field is used to
- *        suppress tag closed errors for certain tokens [TagClosedSuppress],
- *        in particular, if a tag was generated automatically by HTML
- *        Purifier, we may rely on our infrastructure to close it for us
- *        and shouldn't report an error to the user [TagClosedAuto].
- */
-class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
-{
-
-    /**
-     * Array stream of tokens being processed.
-     * @type HTMLPurifier_Token[]
-     */
-    protected $tokens;
-
-    /**
-     * Current token.
-     * @type HTMLPurifier_Token
-     */
-    protected $token;
-
-    /**
-     * Zipper managing the true state.
-     * @type HTMLPurifier_Zipper
-     */
-    protected $zipper;
-
-    /**
-     * Current nesting of elements.
-     * @type array
-     */
-    protected $stack;
-
-    /**
-     * Injectors active in this stream processing.
-     * @type HTMLPurifier_Injector[]
-     */
-    protected $injectors;
-
-    /**
-     * Current instance of HTMLPurifier_Config.
-     * @type HTMLPurifier_Config
-     */
-    protected $config;
-
-    /**
-     * Current instance of HTMLPurifier_Context.
-     * @type HTMLPurifier_Context
-     */
-    protected $context;
-
-    /**
-     * @param HTMLPurifier_Token[] $tokens
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token[]
-     * @throws HTMLPurifier_Exception
-     */
-    public function execute($tokens, $config, $context)
-    {
-        $definition = $config->getHTMLDefinition();
-
-        // local variables
-        $generator = new HTMLPurifier_Generator($config, $context);
-        $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
-        // used for autoclose early abortion
-        $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config);
-        $e = $context->get('ErrorCollector', true);
-        $i = false; // injector index
-        list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens);
-        if ($token === NULL) {
-            return array();
-        }
-        $reprocess = false; // whether or not to reprocess the same token
-        $stack = array();
-
-        // member variables
-        $this->stack =& $stack;
-        $this->tokens =& $tokens;
-        $this->token =& $token;
-        $this->zipper =& $zipper;
-        $this->config = $config;
-        $this->context = $context;
-
-        // context variables
-        $context->register('CurrentNesting', $stack);
-        $context->register('InputZipper', $zipper);
-        $context->register('CurrentToken', $token);
-
-        // -- begin INJECTOR --
-
-        $this->injectors = array();
-
-        $injectors = $config->getBatch('AutoFormat');
-        $def_injectors = $definition->info_injector;
-        $custom_injectors = $injectors['Custom'];
-        unset($injectors['Custom']); // special case
-        foreach ($injectors as $injector => $b) {
-            // XXX: Fix with a legitimate lookup table of enabled filters
-            if (strpos($injector, '.') !== false) {
-                continue;
-            }
-            $injector = "HTMLPurifier_Injector_$injector";
-            if (!$b) {
-                continue;
-            }
-            $this->injectors[] = new $injector;
-        }
-        foreach ($def_injectors as $injector) {
-            // assumed to be objects
-            $this->injectors[] = $injector;
-        }
-        foreach ($custom_injectors as $injector) {
-            if (!$injector) {
-                continue;
-            }
-            if (is_string($injector)) {
-                $injector = "HTMLPurifier_Injector_$injector";
-                $injector = new $injector;
-            }
-            $this->injectors[] = $injector;
-        }
-
-        // give the injectors references to the definition and context
-        // variables for performance reasons
-        foreach ($this->injectors as $ix => $injector) {
-            $error = $injector->prepare($config, $context);
-            if (!$error) {
-                continue;
-            }
-            array_splice($this->injectors, $ix, 1); // rm the injector
-            trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);
-        }
-
-        // -- end INJECTOR --
-
-        // a note on reprocessing:
-        //      In order to reduce code duplication, whenever some code needs
-        //      to make HTML changes in order to make things "correct", the
-        //      new HTML gets sent through the purifier, regardless of its
-        //      status. This means that if we add a start token, because it
-        //      was totally necessary, we don't have to update nesting; we just
-        //      punt ($reprocess = true; continue;) and it does that for us.
-
-        // isset is in loop because $tokens size changes during loop exec
-        for (;;
-             // only increment if we don't need to reprocess
-             $reprocess ? $reprocess = false : $token = $zipper->next($token)) {
-
-            // check for a rewind
-            if (is_int($i)) {
-                // possibility: disable rewinding if the current token has a
-                // rewind set on it already. This would offer protection from
-                // infinite loop, but might hinder some advanced rewinding.
-                $rewind_offset = $this->injectors[$i]->getRewindOffset();
-                if (is_int($rewind_offset)) {
-                    for ($j = 0; $j < $rewind_offset; $j++) {
-                        if (empty($zipper->front)) break;
-                        $token = $zipper->prev($token);
-                        // indicate that other injectors should not process this token,
-                        // but we need to reprocess it.  See Note [Injector skips]
-                        unset($token->skip[$i]);
-                        $token->rewind = $i;
-                        if ($token instanceof HTMLPurifier_Token_Start) {
-                            array_pop($this->stack);
-                        } elseif ($token instanceof HTMLPurifier_Token_End) {
-                            $this->stack[] = $token->start;
-                        }
-                    }
-                }
-                $i = false;
-            }
-
-            // handle case of document end
-            if ($token === NULL) {
-                // kill processing if stack is empty
-                if (empty($this->stack)) {
-                    break;
-                }
-
-                // peek
-                $top_nesting = array_pop($this->stack);
-                $this->stack[] = $top_nesting;
-
-                // send error [TagClosedSuppress]
-                if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) {
-                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting);
-                }
-
-                // append, don't splice, since this is the end
-                $token = new HTMLPurifier_Token_End($top_nesting->name);
-
-                // punt!
-                $reprocess = true;
-                continue;
-            }
-
-            //echo '<br>'; printZipper($zipper, $token);//printTokens($this->stack);
-            //flush();
-
-            // quick-check: if it's not a tag, no need to process
-            if (empty($token->is_tag)) {
-                if ($token instanceof HTMLPurifier_Token_Text) {
-                    foreach ($this->injectors as $i => $injector) {
-                        if (isset($token->skip[$i])) {
-                            // See Note [Injector skips]
-                            continue;
-                        }
-                        if ($token->rewind !== null && $token->rewind !== $i) {
-                            continue;
-                        }
-                        // XXX fuckup
-                        $r = $token;
-                        $injector->handleText($r);
-                        $token = $this->processToken($r, $i);
-                        $reprocess = true;
-                        break;
-                    }
-                }
-                // another possibility is a comment
-                continue;
-            }
-
-            if (isset($definition->info[$token->name])) {
-                $type = $definition->info[$token->name]->child->type;
-            } else {
-                $type = false; // Type is unknown, treat accordingly
-            }
-
-            // quick tag checks: anything that's *not* an end tag
-            $ok = false;
-            if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
-                // claims to be a start tag but is empty
-                $token = new HTMLPurifier_Token_Empty(
-                    $token->name,
-                    $token->attr,
-                    $token->line,
-                    $token->col,
-                    $token->armor
-                );
-                $ok = true;
-            } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
-                // claims to be empty but really is a start tag
-                // NB: this assignment is required
-                $old_token = $token;
-                $token = new HTMLPurifier_Token_End($token->name);
-                $token = $this->insertBefore(
-                    new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor)
-                );
-                // punt (since we had to modify the input stream in a non-trivial way)
-                $reprocess = true;
-                continue;
-            } elseif ($token instanceof HTMLPurifier_Token_Empty) {
-                // real empty token
-                $ok = true;
-            } elseif ($token instanceof HTMLPurifier_Token_Start) {
-                // start tag
-
-                // ...unless they also have to close their parent
-                if (!empty($this->stack)) {
-
-                    // Performance note: you might think that it's rather
-                    // inefficient, recalculating the autoclose information
-                    // for every tag that a token closes (since when we
-                    // do an autoclose, we push a new token into the
-                    // stream and then /process/ that, before
-                    // re-processing this token.)  But this is
-                    // necessary, because an injector can make an
-                    // arbitrary transformations to the autoclosing
-                    // tokens we introduce, so things may have changed
-                    // in the meantime.  Also, doing the inefficient thing is
-                    // "easy" to reason about (for certain perverse definitions
-                    // of "easy")
-
-                    $parent = array_pop($this->stack);
-                    $this->stack[] = $parent;
-
-                    $parent_def = null;
-                    $parent_elements = null;
-                    $autoclose = false;
-                    if (isset($definition->info[$parent->name])) {
-                        $parent_def = $definition->info[$parent->name];
-                        $parent_elements = $parent_def->child->getAllowedElements($config);
-                        $autoclose = !isset($parent_elements[$token->name]);
-                    }
-
-                    if ($autoclose && $definition->info[$token->name]->wrap) {
-                        // Check if an element can be wrapped by another
-                        // element to make it valid in a context (for
-                        // example, <ul><ul> needs a <li> in between)
-                        $wrapname = $definition->info[$token->name]->wrap;
-                        $wrapdef = $definition->info[$wrapname];
-                        $elements = $wrapdef->child->getAllowedElements($config);
-                        if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) {
-                            $newtoken = new HTMLPurifier_Token_Start($wrapname);
-                            $token = $this->insertBefore($newtoken);
-                            $reprocess = true;
-                            continue;
-                        }
-                    }
-
-                    $carryover = false;
-                    if ($autoclose && $parent_def->formatting) {
-                        $carryover = true;
-                    }
-
-                    if ($autoclose) {
-                        // check if this autoclose is doomed to fail
-                        // (this rechecks $parent, which his harmless)
-                        $autoclose_ok = isset($global_parent_allowed_elements[$token->name]);
-                        if (!$autoclose_ok) {
-                            foreach ($this->stack as $ancestor) {
-                                $elements = $definition->info[$ancestor->name]->child->getAllowedElements($config);
-                                if (isset($elements[$token->name])) {
-                                    $autoclose_ok = true;
-                                    break;
-                                }
-                                if ($definition->info[$token->name]->wrap) {
-                                    $wrapname = $definition->info[$token->name]->wrap;
-                                    $wrapdef = $definition->info[$wrapname];
-                                    $wrap_elements = $wrapdef->child->getAllowedElements($config);
-                                    if (isset($wrap_elements[$token->name]) && isset($elements[$wrapname])) {
-                                        $autoclose_ok = true;
-                                        break;
-                                    }
-                                }
-                            }
-                        }
-                        if ($autoclose_ok) {
-                            // errors need to be updated
-                            $new_token = new HTMLPurifier_Token_End($parent->name);
-                            $new_token->start = $parent;
-                            // [TagClosedSuppress]
-                            if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) {
-                                if (!$carryover) {
-                                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent);
-                                } else {
-                                    $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent);
-                                }
-                            }
-                            if ($carryover) {
-                                $element = clone $parent;
-                                // [TagClosedAuto]
-                                $element->armor['MakeWellFormed_TagClosedError'] = true;
-                                $element->carryover = true;
-                                $token = $this->processToken(array($new_token, $token, $element));
-                            } else {
-                                $token = $this->insertBefore($new_token);
-                            }
-                        } else {
-                            $token = $this->remove();
-                        }
-                        $reprocess = true;
-                        continue;
-                    }
-
-                }
-                $ok = true;
-            }
-
-            if ($ok) {
-                foreach ($this->injectors as $i => $injector) {
-                    if (isset($token->skip[$i])) {
-                        // See Note [Injector skips]
-                        continue;
-                    }
-                    if ($token->rewind !== null && $token->rewind !== $i) {
-                        continue;
-                    }
-                    $r = $token;
-                    $injector->handleElement($r);
-                    $token = $this->processToken($r, $i);
-                    $reprocess = true;
-                    break;
-                }
-                if (!$reprocess) {
-                    // ah, nothing interesting happened; do normal processing
-                    if ($token instanceof HTMLPurifier_Token_Start) {
-                        $this->stack[] = $token;
-                    } elseif ($token instanceof HTMLPurifier_Token_End) {
-                        throw new HTMLPurifier_Exception(
-                            'Improper handling of end tag in start code; possible error in MakeWellFormed'
-                        );
-                    }
-                }
-                continue;
-            }
-
-            // sanity check: we should be dealing with a closing tag
-            if (!$token instanceof HTMLPurifier_Token_End) {
-                throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier');
-            }
-
-            // make sure that we have something open
-            if (empty($this->stack)) {
-                if ($escape_invalid_tags) {
-                    if ($e) {
-                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
-                    }
-                    $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
-                } else {
-                    if ($e) {
-                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
-                    }
-                    $token = $this->remove();
-                }
-                $reprocess = true;
-                continue;
-            }
-
-            // first, check for the simplest case: everything closes neatly.
-            // Eventually, everything passes through here; if there are problems
-            // we modify the input stream accordingly and then punt, so that
-            // the tokens get processed again.
-            $current_parent = array_pop($this->stack);
-            if ($current_parent->name == $token->name) {
-                $token->start = $current_parent;
-                foreach ($this->injectors as $i => $injector) {
-                    if (isset($token->skip[$i])) {
-                        // See Note [Injector skips]
-                        continue;
-                    }
-                    if ($token->rewind !== null && $token->rewind !== $i) {
-                        continue;
-                    }
-                    $r = $token;
-                    $injector->handleEnd($r);
-                    $token = $this->processToken($r, $i);
-                    $this->stack[] = $current_parent;
-                    $reprocess = true;
-                    break;
-                }
-                continue;
-            }
-
-            // okay, so we're trying to close the wrong tag
-
-            // undo the pop previous pop
-            $this->stack[] = $current_parent;
-
-            // scroll back the entire nest, trying to find our tag.
-            // (feature could be to specify how far you'd like to go)
-            $size = count($this->stack);
-            // -2 because -1 is the last element, but we already checked that
-            $skipped_tags = false;
-            for ($j = $size - 2; $j >= 0; $j--) {
-                if ($this->stack[$j]->name == $token->name) {
-                    $skipped_tags = array_slice($this->stack, $j);
-                    break;
-                }
-            }
-
-            // we didn't find the tag, so remove
-            if ($skipped_tags === false) {
-                if ($escape_invalid_tags) {
-                    if ($e) {
-                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
-                    }
-                    $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token));
-                } else {
-                    if ($e) {
-                        $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
-                    }
-                    $token = $this->remove();
-                }
-                $reprocess = true;
-                continue;
-            }
-
-            // do errors, in REVERSE $j order: a,b,c with </a></b></c>
-            $c = count($skipped_tags);
-            if ($e) {
-                for ($j = $c - 1; $j > 0; $j--) {
-                    // notice we exclude $j == 0, i.e. the current ending tag, from
-                    // the errors... [TagClosedSuppress]
-                    if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) {
-                        $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]);
-                    }
-                }
-            }
-
-            // insert tags, in FORWARD $j order: c,b,a with </a></b></c>
-            $replace = array($token);
-            for ($j = 1; $j < $c; $j++) {
-                // ...as well as from the insertions
-                $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name);
-                $new_token->start = $skipped_tags[$j];
-                array_unshift($replace, $new_token);
-                if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) {
-                    // [TagClosedAuto]
-                    $element = clone $skipped_tags[$j];
-                    $element->carryover = true;
-                    $element->armor['MakeWellFormed_TagClosedError'] = true;
-                    $replace[] = $element;
-                }
-            }
-            $token = $this->processToken($replace);
-            $reprocess = true;
-            continue;
-        }
-
-        $context->destroy('CurrentToken');
-        $context->destroy('CurrentNesting');
-        $context->destroy('InputZipper');
-
-        unset($this->injectors, $this->stack, $this->tokens);
-        return $zipper->toArray($token);
-    }
-
-    /**
-     * Processes arbitrary token values for complicated substitution patterns.
-     * In general:
-     *
-     * If $token is an array, it is a list of tokens to substitute for the
-     * current token. These tokens then get individually processed. If there
-     * is a leading integer in the list, that integer determines how many
-     * tokens from the stream should be removed.
-     *
-     * If $token is a regular token, it is swapped with the current token.
-     *
-     * If $token is false, the current token is deleted.
-     *
-     * If $token is an integer, that number of tokens (with the first token
-     * being the current one) will be deleted.
-     *
-     * @param HTMLPurifier_Token|array|int|bool $token Token substitution value
-     * @param HTMLPurifier_Injector|int $injector Injector that performed the substitution; default is if
-     *        this is not an injector related operation.
-     * @throws HTMLPurifier_Exception
-     */
-    protected function processToken($token, $injector = -1)
-    {
-        // Zend OpCache miscompiles $token = array($token), so
-        // avoid this pattern.  See: https://github.com/ezyang/htmlpurifier/issues/108
-
-        // normalize forms of token
-        if (is_object($token)) {
-            $tmp = $token;
-            $token = array(1, $tmp);
-        }
-        if (is_int($token)) {
-            $tmp = $token;
-            $token = array($tmp);
-        }
-        if ($token === false) {
-            $token = array(1);
-        }
-        if (!is_array($token)) {
-            throw new HTMLPurifier_Exception('Invalid token type from injector');
-        }
-        if (!is_int($token[0])) {
-            array_unshift($token, 1);
-        }
-        if ($token[0] === 0) {
-            throw new HTMLPurifier_Exception('Deleting zero tokens is not valid');
-        }
-
-        // $token is now an array with the following form:
-        // array(number nodes to delete, new node 1, new node 2, ...)
-
-        $delete = array_shift($token);
-        list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
-
-        if ($injector > -1) {
-            // See Note [Injector skips]
-            // Determine appropriate skips.  Here's what the code does:
-            //  *If* we deleted one or more tokens, copy the skips
-            //  of those tokens into the skips of the new tokens (in $token).
-            //  Also, mark the newly inserted tokens as having come from
-            //  $injector.
-            $oldskip = isset($old[0]) ? $old[0]->skip : array();
-            foreach ($token as $object) {
-                $object->skip = $oldskip;
-                $object->skip[$injector] = true;
-            }
-        }
-
-        return $r;
-
-    }
-
-    /**
-     * Inserts a token before the current token. Cursor now points to
-     * this token.  You must reprocess after this.
-     * @param HTMLPurifier_Token $token
-     */
-    private function insertBefore($token)
-    {
-        // NB not $this->zipper->insertBefore(), due to positioning
-        // differences
-        $splice = $this->zipper->splice($this->token, 0, array($token));
-
-        return $splice[1];
-    }
-
-    /**
-     * Removes current token. Cursor now points to new token occupying previously
-     * occupied space.  You must reprocess after this.
-     */
-    private function remove()
-    {
-        return $this->zipper->delete();
-    }
-}
-
-// Note [Injector skips]
-// ~~~~~~~~~~~~~~~~~~~~~
-// When I originally designed this class, the idea behind the 'skip'
-// property of HTMLPurifier_Token was to help avoid infinite loops
-// in injector processing.  For example, suppose you wrote an injector
-// that bolded swear words.  Naively, you might write it so that
-// whenever you saw ****, you replaced it with <strong>****</strong>.
-//
-// When this happens, we will reprocess all of the tokens with the
-// other injectors.  Now there is an opportunity for infinite loop:
-// if we rerun the swear-word injector on these tokens, we might
-// see **** and then reprocess again to get
-// <strong><strong>****</strong></strong> ad infinitum.
-//
-// Thus, the idea of a skip is that once we process a token with
-// an injector, we mark all of those tokens as having "come from"
-// the injector, and we never run the injector again on these
-// tokens.
-//
-// There were two more complications, however:
-//
-//  - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if
-//    you had <b><i></i></b>, after you removed the <i></i>, you
-//    really would like this injector to go back and reprocess
-//    the <b> tag, discovering that it is now empty and can be
-//    removed.  So we reintroduced the possibility of infinite looping
-//    by adding a "rewind" function, which let you go back to an
-//    earlier point in the token stream and reprocess it with injectors.
-//    Needless to say, we need to UN-skip the token so it gets
-//    reprocessed.
-//
-//  - Suppose that you successfuly process a token, replace it with
-//    one with your skip mark, but now another injector wants to
-//    process the skipped token with another token.  Should you continue
-//    to skip that new token, or reprocess it?  If you reprocess,
-//    you can end up with an infinite loop where one injector converts
-//    <a> to <b>, and then another injector converts it back.  So
-//    we inherit the skips, but for some reason, I thought that we
-//    should inherit the skip from the first token of the token
-//    that we deleted.  Why?  Well, it seems to work OK.
-//
-// If I were to redesign this functionality, I would absolutely not
-// go about doing it this way: the semantics are just not very well
-// defined, and in any case you probably wanted to operate on trees,
-// not token streams.
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php
deleted file mode 100644
index 1a8149e..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-
-/**
- * Removes all unrecognized tags from the list of tokens.
- *
- * This strategy iterates through all the tokens and removes unrecognized
- * tokens. If a token is not recognized but a TagTransform is defined for
- * that element, the element will be transformed accordingly.
- */
-
-class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
-{
-
-    /**
-     * @param HTMLPurifier_Token[] $tokens
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return array|HTMLPurifier_Token[]
-     */
-    public function execute($tokens, $config, $context)
-    {
-        $definition = $config->getHTMLDefinition();
-        $generator = new HTMLPurifier_Generator($config, $context);
-        $result = array();
-
-        $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
-        $remove_invalid_img = $config->get('Core.RemoveInvalidImg');
-
-        // currently only used to determine if comments should be kept
-        $trusted = $config->get('HTML.Trusted');
-        $comment_lookup = $config->get('HTML.AllowedComments');
-        $comment_regexp = $config->get('HTML.AllowedCommentsRegexp');
-        $check_comments = $comment_lookup !== array() || $comment_regexp !== null;
-
-        $remove_script_contents = $config->get('Core.RemoveScriptContents');
-        $hidden_elements = $config->get('Core.HiddenElements');
-
-        // remove script contents compatibility
-        if ($remove_script_contents === true) {
-            $hidden_elements['script'] = true;
-        } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) {
-            unset($hidden_elements['script']);
-        }
-
-        $attr_validator = new HTMLPurifier_AttrValidator();
-
-        // removes tokens until it reaches a closing tag with its value
-        $remove_until = false;
-
-        // converts comments into text tokens when this is equal to a tag name
-        $textify_comments = false;
-
-        $token = false;
-        $context->register('CurrentToken', $token);
-
-        $e = false;
-        if ($config->get('Core.CollectErrors')) {
-            $e =& $context->get('ErrorCollector');
-        }
-
-        foreach ($tokens as $token) {
-            if ($remove_until) {
-                if (empty($token->is_tag) || $token->name !== $remove_until) {
-                    continue;
-                }
-            }
-            if (!empty($token->is_tag)) {
-                // DEFINITION CALL
-
-                // before any processing, try to transform the element
-                if (isset($definition->info_tag_transform[$token->name])) {
-                    $original_name = $token->name;
-                    // there is a transformation for this tag
-                    // DEFINITION CALL
-                    $token = $definition->
-                        info_tag_transform[$token->name]->transform($token, $config, $context);
-                    if ($e) {
-                        $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
-                    }
-                }
-
-                if (isset($definition->info[$token->name])) {
-                    // mostly everything's good, but
-                    // we need to make sure required attributes are in order
-                    if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
-                        $definition->info[$token->name]->required_attr &&
-                        ($token->name != 'img' || $remove_invalid_img) // ensure config option still works
-                    ) {
-                        $attr_validator->validateToken($token, $config, $context);
-                        $ok = true;
-                        foreach ($definition->info[$token->name]->required_attr as $name) {
-                            if (!isset($token->attr[$name])) {
-                                $ok = false;
-                                break;
-                            }
-                        }
-                        if (!$ok) {
-                            if ($e) {
-                                $e->send(
-                                    E_ERROR,
-                                    'Strategy_RemoveForeignElements: Missing required attribute',
-                                    $name
-                                );
-                            }
-                            continue;
-                        }
-                        $token->armor['ValidateAttributes'] = true;
-                    }
-
-                    if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) {
-                        $textify_comments = $token->name;
-                    } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) {
-                        $textify_comments = false;
-                    }
-
-                } elseif ($escape_invalid_tags) {
-                    // invalid tag, generate HTML representation and insert in
-                    if ($e) {
-                        $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
-                    }
-                    $token = new HTMLPurifier_Token_Text(
-                        $generator->generateFromToken($token)
-                    );
-                } else {
-                    // check if we need to destroy all of the tag's children
-                    // CAN BE GENERICIZED
-                    if (isset($hidden_elements[$token->name])) {
-                        if ($token instanceof HTMLPurifier_Token_Start) {
-                            $remove_until = $token->name;
-                        } elseif ($token instanceof HTMLPurifier_Token_Empty) {
-                            // do nothing: we're still looking
-                        } else {
-                            $remove_until = false;
-                        }
-                        if ($e) {
-                            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
-                        }
-                    } else {
-                        if ($e) {
-                            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
-                        }
-                    }
-                    continue;
-                }
-            } elseif ($token instanceof HTMLPurifier_Token_Comment) {
-                // textify comments in script tags when they are allowed
-                if ($textify_comments !== false) {
-                    $data = $token->data;
-                    $token = new HTMLPurifier_Token_Text($data);
-                } elseif ($trusted || $check_comments) {
-                    // always cleanup comments
-                    $trailing_hyphen = false;
-                    if ($e) {
-                        // perform check whether or not there's a trailing hyphen
-                        if (substr($token->data, -1) == '-') {
-                            $trailing_hyphen = true;
-                        }
-                    }
-                    $token->data = rtrim($token->data, '-');
-                    $found_double_hyphen = false;
-                    while (strpos($token->data, '--') !== false) {
-                        $found_double_hyphen = true;
-                        $token->data = str_replace('--', '-', $token->data);
-                    }
-                    if ($trusted || !empty($comment_lookup[trim($token->data)]) ||
-                        ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) {
-                        // OK good
-                        if ($e) {
-                            if ($trailing_hyphen) {
-                                $e->send(
-                                    E_NOTICE,
-                                    'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'
-                                );
-                            }
-                            if ($found_double_hyphen) {
-                                $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed');
-                            }
-                        }
-                    } else {
-                        if ($e) {
-                            $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
-                        }
-                        continue;
-                    }
-                } else {
-                    // strip comments
-                    if ($e) {
-                        $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
-                    }
-                    continue;
-                }
-            } elseif ($token instanceof HTMLPurifier_Token_Text) {
-            } else {
-                continue;
-            }
-            $result[] = $token;
-        }
-        if ($remove_until && $e) {
-            // we removed tokens until the end, throw error
-            $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until);
-        }
-        $context->destroy('CurrentToken');
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php
deleted file mode 100644
index fbb3d27..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * Validate all attributes in the tokens.
- */
-
-class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
-{
-
-    /**
-     * @param HTMLPurifier_Token[] $tokens
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token[]
-     */
-    public function execute($tokens, $config, $context)
-    {
-        // setup validator
-        $validator = new HTMLPurifier_AttrValidator();
-
-        $token = false;
-        $context->register('CurrentToken', $token);
-
-        foreach ($tokens as $key => $token) {
-
-            // only process tokens that have attributes,
-            //   namely start and empty tags
-            if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) {
-                continue;
-            }
-
-            // skip tokens that are armored
-            if (!empty($token->armor['ValidateAttributes'])) {
-                continue;
-            }
-
-            // note that we have no facilities here for removing tokens
-            $validator->validateToken($token, $config, $context);
-        }
-        $context->destroy('CurrentToken');
-        return $tokens;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php
deleted file mode 100644
index c41ae3a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
- * This is in almost every respect equivalent to an array except
- * that it keeps track of which keys were accessed.
- *
- * @warning For the sake of backwards compatibility with early versions
- *     of PHP 5, you must not use the $hash[$key] syntax; if you do
- *     our version of offsetGet is never called.
- */
-class HTMLPurifier_StringHash extends ArrayObject
-{
-    /**
-     * @type array
-     */
-    protected $accessed = array();
-
-    /**
-     * Retrieves a value, and logs the access.
-     * @param mixed $index
-     * @return mixed
-     */
-    #[\ReturnTypeWillChange]
-    public function offsetGet($index)
-    {
-        $this->accessed[$index] = true;
-        return parent::offsetGet($index);
-    }
-
-    /**
-     * Returns a lookup array of all array indexes that have been accessed.
-     * @return array in form array($index => true).
-     */
-    public function getAccessed()
-    {
-        return $this->accessed;
-    }
-
-    /**
-     * Resets the access array.
-     */
-    public function resetAccessed()
-    {
-        $this->accessed = array();
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php
deleted file mode 100644
index 7c73f80..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * Parses string hash files. File format is as such:
- *
- *      DefaultKeyValue
- *      KEY: Value
- *      KEY2: Value2
- *      --MULTILINE-KEY--
- *      Multiline
- *      value.
- *
- * Which would output something similar to:
- *
- *      array(
- *          'ID' => 'DefaultKeyValue',
- *          'KEY' => 'Value',
- *          'KEY2' => 'Value2',
- *          'MULTILINE-KEY' => "Multiline\nvalue.\n",
- *      )
- *
- * We use this as an easy to use file-format for configuration schema
- * files, but the class itself is usage agnostic.
- *
- * You can use ---- to forcibly terminate parsing of a single string-hash;
- * this marker is used in multi string-hashes to delimit boundaries.
- */
-class HTMLPurifier_StringHashParser
-{
-
-    /**
-     * @type string
-     */
-    public $default = 'ID';
-
-    /**
-     * Parses a file that contains a single string-hash.
-     * @param string $file
-     * @return array
-     */
-    public function parseFile($file)
-    {
-        if (!file_exists($file)) {
-            return false;
-        }
-        $fh = fopen($file, 'r');
-        if (!$fh) {
-            return false;
-        }
-        $ret = $this->parseHandle($fh);
-        fclose($fh);
-        return $ret;
-    }
-
-    /**
-     * Parses a file that contains multiple string-hashes delimited by '----'
-     * @param string $file
-     * @return array
-     */
-    public function parseMultiFile($file)
-    {
-        if (!file_exists($file)) {
-            return false;
-        }
-        $ret = array();
-        $fh = fopen($file, 'r');
-        if (!$fh) {
-            return false;
-        }
-        while (!feof($fh)) {
-            $ret[] = $this->parseHandle($fh);
-        }
-        fclose($fh);
-        return $ret;
-    }
-
-    /**
-     * Internal parser that acepts a file handle.
-     * @note While it's possible to simulate in-memory parsing by using
-     *       custom stream wrappers, if such a use-case arises we should
-     *       factor out the file handle into its own class.
-     * @param resource $fh File handle with pointer at start of valid string-hash
-     *            block.
-     * @return array
-     */
-    protected function parseHandle($fh)
-    {
-        $state   = false;
-        $single  = false;
-        $ret     = array();
-        do {
-            $line = fgets($fh);
-            if ($line === false) {
-                break;
-            }
-            $line = rtrim($line, "\n\r");
-            if (!$state && $line === '') {
-                continue;
-            }
-            if ($line === '----') {
-                break;
-            }
-            if (strncmp('--#', $line, 3) === 0) {
-                // Comment
-                continue;
-            } elseif (strncmp('--', $line, 2) === 0) {
-                // Multiline declaration
-                $state = trim($line, '- ');
-                if (!isset($ret[$state])) {
-                    $ret[$state] = '';
-                }
-                continue;
-            } elseif (!$state) {
-                $single = true;
-                if (strpos($line, ':') !== false) {
-                    // Single-line declaration
-                    list($state, $line) = explode(':', $line, 2);
-                    $line = trim($line);
-                } else {
-                    // Use default declaration
-                    $state  = $this->default;
-                }
-            }
-            if ($single) {
-                $ret[$state] = $line;
-                $single = false;
-                $state  = false;
-            } else {
-                $ret[$state] .= "$line\n";
-            }
-        } while (!feof($fh));
-        return $ret;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php
deleted file mode 100644
index 7b8d833..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * Defines a mutation of an obsolete tag into a valid tag.
- */
-abstract class HTMLPurifier_TagTransform
-{
-
-    /**
-     * Tag name to transform the tag to.
-     * @type string
-     */
-    public $transform_to;
-
-    /**
-     * Transforms the obsolete tag into the valid tag.
-     * @param HTMLPurifier_Token_Tag $tag Tag to be transformed.
-     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object
-     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object
-     */
-    abstract public function transform($tag, $config, $context);
-
-    /**
-     * Prepends CSS properties to the style attribute, creating the
-     * attribute if it doesn't exist.
-     * @warning Copied over from AttrTransform, be sure to keep in sync
-     * @param array $attr Attribute array to process (passed by reference)
-     * @param string $css CSS to prepend
-     */
-    protected function prependCSS(&$attr, $css)
-    {
-        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
-        $attr['style'] = $css . $attr['style'];
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php
deleted file mode 100644
index 768c9b1..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-
-/**
- * Transforms FONT tags to the proper form (SPAN with CSS styling)
- *
- * This transformation takes the three proprietary attributes of FONT and
- * transforms them into their corresponding CSS attributes.  These are color,
- * face, and size.
- *
- * @note Size is an interesting case because it doesn't map cleanly to CSS.
- *       Thanks to
- *       http://style.cleverchimp.com/font_size_intervals/altintervals.html
- *       for reasonable mappings.
- * @warning This doesn't work completely correctly; specifically, this
- *          TagTransform operates before well-formedness is enforced, so
- *          the "active formatting elements" algorithm doesn't get applied.
- */
-class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform
-{
-    /**
-     * @type string
-     */
-    public $transform_to = 'span';
-
-    /**
-     * @type array
-     */
-    protected $_size_lookup = array(
-        '0' => 'xx-small',
-        '1' => 'xx-small',
-        '2' => 'small',
-        '3' => 'medium',
-        '4' => 'large',
-        '5' => 'x-large',
-        '6' => 'xx-large',
-        '7' => '300%',
-        '-1' => 'smaller',
-        '-2' => '60%',
-        '+1' => 'larger',
-        '+2' => '150%',
-        '+3' => '200%',
-        '+4' => '300%'
-    );
-
-    /**
-     * @param HTMLPurifier_Token_Tag $tag
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_Token_End|string
-     */
-    public function transform($tag, $config, $context)
-    {
-        if ($tag instanceof HTMLPurifier_Token_End) {
-            $new_tag = clone $tag;
-            $new_tag->name = $this->transform_to;
-            return $new_tag;
-        }
-
-        $attr = $tag->attr;
-        $prepend_style = '';
-
-        // handle color transform
-        if (isset($attr['color'])) {
-            $prepend_style .= 'color:' . $attr['color'] . ';';
-            unset($attr['color']);
-        }
-
-        // handle face transform
-        if (isset($attr['face'])) {
-            $prepend_style .= 'font-family:' . $attr['face'] . ';';
-            unset($attr['face']);
-        }
-
-        // handle size transform
-        if (isset($attr['size'])) {
-            // normalize large numbers
-            if ($attr['size'] !== '') {
-                if ($attr['size'][0] == '+' || $attr['size'][0] == '-') {
-                    $size = (int)$attr['size'];
-                    if ($size < -2) {
-                        $attr['size'] = '-2';
-                    }
-                    if ($size > 4) {
-                        $attr['size'] = '+4';
-                    }
-                } else {
-                    $size = (int)$attr['size'];
-                    if ($size > 7) {
-                        $attr['size'] = '7';
-                    }
-                }
-            }
-            if (isset($this->_size_lookup[$attr['size']])) {
-                $prepend_style .= 'font-size:' .
-                    $this->_size_lookup[$attr['size']] . ';';
-            }
-            unset($attr['size']);
-        }
-
-        if ($prepend_style) {
-            $attr['style'] = isset($attr['style']) ?
-                $prepend_style . $attr['style'] :
-                $prepend_style;
-        }
-
-        $new_tag = clone $tag;
-        $new_tag->name = $this->transform_to;
-        $new_tag->attr = $attr;
-
-        return $new_tag;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php
deleted file mode 100644
index 71bf10b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Simple transformation, just change tag name to something else,
- * and possibly add some styling. This will cover most of the deprecated
- * tag cases.
- */
-class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform
-{
-    /**
-     * @type string
-     */
-    protected $style;
-
-    /**
-     * @param string $transform_to Tag name to transform to.
-     * @param string $style CSS style to add to the tag
-     */
-    public function __construct($transform_to, $style = null)
-    {
-        $this->transform_to = $transform_to;
-        $this->style = $style;
-    }
-
-    /**
-     * @param HTMLPurifier_Token_Tag $tag
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return string
-     */
-    public function transform($tag, $config, $context)
-    {
-        $new_tag = clone $tag;
-        $new_tag->name = $this->transform_to;
-        if (!is_null($this->style) &&
-            ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty)
-        ) {
-            $this->prependCSS($new_tag->attr, $this->style);
-        }
-        return $new_tag;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php
deleted file mode 100644
index 84d3619..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token.php
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-
-/**
- * Abstract base token class that all others inherit from.
- */
-abstract class HTMLPurifier_Token
-{
-    /**
-     * Line number node was on in source document. Null if unknown.
-     * @type int
-     */
-    public $line;
-
-    /**
-     * Column of line node was on in source document. Null if unknown.
-     * @type int
-     */
-    public $col;
-
-    /**
-     * Lookup array of processing that this token is exempt from.
-     * Currently, valid values are "ValidateAttributes" and
-     * "MakeWellFormed_TagClosedError"
-     * @type array
-     */
-    public $armor = array();
-
-    /**
-     * Used during MakeWellFormed.  See Note [Injector skips]
-     * @type
-     */
-    public $skip;
-
-    /**
-     * @type
-     */
-    public $rewind;
-
-    /**
-     * @type
-     */
-    public $carryover;
-
-    /**
-     * @param string $n
-     * @return null|string
-     */
-    public function __get($n)
-    {
-        if ($n === 'type') {
-            trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
-            switch (get_class($this)) {
-                case 'HTMLPurifier_Token_Start':
-                    return 'start';
-                case 'HTMLPurifier_Token_Empty':
-                    return 'empty';
-                case 'HTMLPurifier_Token_End':
-                    return 'end';
-                case 'HTMLPurifier_Token_Text':
-                    return 'text';
-                case 'HTMLPurifier_Token_Comment':
-                    return 'comment';
-                default:
-                    return null;
-            }
-        }
-    }
-
-    /**
-     * Sets the position of the token in the source document.
-     * @param int $l
-     * @param int $c
-     */
-    public function position($l = null, $c = null)
-    {
-        $this->line = $l;
-        $this->col = $c;
-    }
-
-    /**
-     * Convenience function for DirectLex settings line/col position.
-     * @param int $l
-     * @param int $c
-     */
-    public function rawPosition($l, $c)
-    {
-        if ($c === -1) {
-            $l++;
-        }
-        $this->line = $l;
-        $this->col = $c;
-    }
-
-    /**
-     * Converts a token into its corresponding node.
-     */
-    abstract public function toNode();
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php
deleted file mode 100644
index 23453c7..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * Concrete comment token class. Generally will be ignored.
- */
-class HTMLPurifier_Token_Comment extends HTMLPurifier_Token
-{
-    /**
-     * Character data within comment.
-     * @type string
-     */
-    public $data;
-
-    /**
-     * @type bool
-     */
-    public $is_whitespace = true;
-
-    /**
-     * Transparent constructor.
-     *
-     * @param string $data String comment data.
-     * @param int $line
-     * @param int $col
-     */
-    public function __construct($data, $line = null, $col = null)
-    {
-        $this->data = $data;
-        $this->line = $line;
-        $this->col = $col;
-    }
-
-    public function toNode() {
-        return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php
deleted file mode 100644
index 78a95f5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-/**
- * Concrete empty token class.
- */
-class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag
-{
-    public function toNode() {
-        $n = parent::toNode();
-        $n->empty = true;
-        return $n;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php
deleted file mode 100644
index 59b38fd..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * Concrete end token class.
- *
- * @warning This class accepts attributes even though end tags cannot. This
- * is for optimization reasons, as under normal circumstances, the Lexers
- * do not pass attributes.
- */
-class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag
-{
-    /**
-     * Token that started this node.
-     * Added by MakeWellFormed. Please do not edit this!
-     * @type HTMLPurifier_Token
-     */
-    public $start;
-
-    public function toNode() {
-        throw new Exception("HTMLPurifier_Token_End->toNode not supported!");
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php
deleted file mode 100644
index 019f317..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-/**
- * Concrete start token class.
- */
-class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag
-{
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php
deleted file mode 100644
index d643fa6..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * Abstract class of a tag token (start, end or empty), and its behavior.
- */
-abstract class HTMLPurifier_Token_Tag extends HTMLPurifier_Token
-{
-    /**
-     * Static bool marker that indicates the class is a tag.
-     *
-     * This allows us to check objects with <tt>!empty($obj->is_tag)</tt>
-     * without having to use a function call <tt>is_a()</tt>.
-     * @type bool
-     */
-    public $is_tag = true;
-
-    /**
-     * The lower-case name of the tag, like 'a', 'b' or 'blockquote'.
-     *
-     * @note Strictly speaking, XML tags are case sensitive, so we shouldn't
-     * be lower-casing them, but these tokens cater to HTML tags, which are
-     * insensitive.
-     * @type string
-     */
-    public $name;
-
-    /**
-     * Associative array of the tag's attributes.
-     * @type array
-     */
-    public $attr = array();
-
-    /**
-     * Non-overloaded constructor, which lower-cases passed tag name.
-     *
-     * @param string $name String name.
-     * @param array $attr Associative array of attributes.
-     * @param int $line
-     * @param int $col
-     * @param array $armor
-     */
-    public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array())
-    {
-        $this->name = ctype_lower($name) ? $name : strtolower($name);
-        foreach ($attr as $key => $value) {
-            // normalization only necessary when key is not lowercase
-            if (!ctype_lower($key)) {
-                $new_key = strtolower($key);
-                if (!isset($attr[$new_key])) {
-                    $attr[$new_key] = $attr[$key];
-                }
-                if ($new_key !== $key) {
-                    unset($attr[$key]);
-                }
-            }
-        }
-        $this->attr = $attr;
-        $this->line = $line;
-        $this->col = $col;
-        $this->armor = $armor;
-    }
-
-    public function toNode() {
-        return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php
deleted file mode 100644
index f26a1c2..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-/**
- * Concrete text token class.
- *
- * Text tokens comprise of regular parsed character data (PCDATA) and raw
- * character data (from the CDATA sections). Internally, their
- * data is parsed with all entities expanded. Surprisingly, the text token
- * does have a "tag name" called #PCDATA, which is how the DTD represents it
- * in permissible child nodes.
- */
-class HTMLPurifier_Token_Text extends HTMLPurifier_Token
-{
-
-    /**
-     * @type string
-     */
-    public $name = '#PCDATA';
-    /**< PCDATA tag name compatible with DTD. */
-
-    /**
-     * @type string
-     */
-    public $data;
-    /**< Parsed character data of text. */
-
-    /**
-     * @type bool
-     */
-    public $is_whitespace;
-
-    /**< Bool indicating if node is whitespace. */
-
-    /**
-     * Constructor, accepts data and determines if it is whitespace.
-     * @param string $data String parsed character data.
-     * @param int $line
-     * @param int $col
-     */
-    public function __construct($data, $line = null, $col = null)
-    {
-        $this->data = $data;
-        $this->is_whitespace = ctype_space($data);
-        $this->line = $line;
-        $this->col = $col;
-    }
-
-    public function toNode() {
-        return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php
deleted file mode 100644
index dea2446..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-
-/**
- * Factory for token generation.
- *
- * @note Doing some benchmarking indicates that the new operator is much
- *       slower than the clone operator (even discounting the cost of the
- *       constructor).  This class is for that optimization.
- *       Other then that, there's not much point as we don't
- *       maintain parallel HTMLPurifier_Token hierarchies (the main reason why
- *       you'd want to use an abstract factory).
- * @todo Port DirectLex to use this
- */
-class HTMLPurifier_TokenFactory
-{
-    // p stands for prototype
-
-    /**
-     * @type HTMLPurifier_Token_Start
-     */
-    private $p_start;
-
-    /**
-     * @type HTMLPurifier_Token_End
-     */
-    private $p_end;
-
-    /**
-     * @type HTMLPurifier_Token_Empty
-     */
-    private $p_empty;
-
-    /**
-     * @type HTMLPurifier_Token_Text
-     */
-    private $p_text;
-
-    /**
-     * @type HTMLPurifier_Token_Comment
-     */
-    private $p_comment;
-
-    /**
-     * Generates blank prototypes for cloning.
-     */
-    public function __construct()
-    {
-        $this->p_start = new HTMLPurifier_Token_Start('', array());
-        $this->p_end = new HTMLPurifier_Token_End('');
-        $this->p_empty = new HTMLPurifier_Token_Empty('', array());
-        $this->p_text = new HTMLPurifier_Token_Text('');
-        $this->p_comment = new HTMLPurifier_Token_Comment('');
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Start.
-     * @param string $name Tag name
-     * @param array $attr Associative array of attributes
-     * @return HTMLPurifier_Token_Start Generated HTMLPurifier_Token_Start
-     */
-    public function createStart($name, $attr = array())
-    {
-        $p = clone $this->p_start;
-        $p->__construct($name, $attr);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_End.
-     * @param string $name Tag name
-     * @return HTMLPurifier_Token_End Generated HTMLPurifier_Token_End
-     */
-    public function createEnd($name)
-    {
-        $p = clone $this->p_end;
-        $p->__construct($name);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Empty.
-     * @param string $name Tag name
-     * @param array $attr Associative array of attributes
-     * @return HTMLPurifier_Token_Empty Generated HTMLPurifier_Token_Empty
-     */
-    public function createEmpty($name, $attr = array())
-    {
-        $p = clone $this->p_empty;
-        $p->__construct($name, $attr);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Text.
-     * @param string $data Data of text token
-     * @return HTMLPurifier_Token_Text Generated HTMLPurifier_Token_Text
-     */
-    public function createText($data)
-    {
-        $p = clone $this->p_text;
-        $p->__construct($data);
-        return $p;
-    }
-
-    /**
-     * Creates a HTMLPurifier_Token_Comment.
-     * @param string $data Data of comment token
-     * @return HTMLPurifier_Token_Comment Generated HTMLPurifier_Token_Comment
-     */
-    public function createComment($data)
-    {
-        $p = clone $this->p_comment;
-        $p->__construct($data);
-        return $p;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php
deleted file mode 100644
index 9c5be39..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URI.php
+++ /dev/null
@@ -1,316 +0,0 @@
-<?php
-
-/**
- * HTML Purifier's internal representation of a URI.
- * @note
- *      Internal data-structures are completely escaped. If the data needs
- *      to be used in a non-URI context (which is very unlikely), be sure
- *      to decode it first. The URI may not necessarily be well-formed until
- *      validate() is called.
- */
-class HTMLPurifier_URI
-{
-    /**
-     * @type string
-     */
-    public $scheme;
-
-    /**
-     * @type string
-     */
-    public $userinfo;
-
-    /**
-     * @type string
-     */
-    public $host;
-
-    /**
-     * @type int
-     */
-    public $port;
-
-    /**
-     * @type string
-     */
-    public $path;
-
-    /**
-     * @type string
-     */
-    public $query;
-
-    /**
-     * @type string
-     */
-    public $fragment;
-
-    /**
-     * @param string $scheme
-     * @param string $userinfo
-     * @param string $host
-     * @param int $port
-     * @param string $path
-     * @param string $query
-     * @param string $fragment
-     * @note Automatically normalizes scheme and port
-     */
-    public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment)
-    {
-        $this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
-        $this->userinfo = $userinfo;
-        $this->host = $host;
-        $this->port = is_null($port) ? $port : (int)$port;
-        $this->path = $path;
-        $this->query = $query;
-        $this->fragment = $fragment;
-    }
-
-    /**
-     * Retrieves a scheme object corresponding to the URI's scheme/default
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_URIScheme Scheme object appropriate for validating this URI
-     */
-    public function getSchemeObj($config, $context)
-    {
-        $registry = HTMLPurifier_URISchemeRegistry::instance();
-        if ($this->scheme !== null) {
-            $scheme_obj = $registry->getScheme($this->scheme, $config, $context);
-            if (!$scheme_obj) {
-                return false;
-            } // invalid scheme, clean it out
-        } else {
-            // no scheme: retrieve the default one
-            $def = $config->getDefinition('URI');
-            $scheme_obj = $def->getDefaultScheme($config, $context);
-            if (!$scheme_obj) {
-                if ($def->defaultScheme !== null) {
-                    // something funky happened to the default scheme object
-                    trigger_error(
-                        'Default scheme object "' . $def->defaultScheme . '" was not readable',
-                        E_USER_WARNING
-                    );
-                } // suppress error if it's null
-                return false;
-            }
-        }
-        return $scheme_obj;
-    }
-
-    /**
-     * Generic validation method applicable for all schemes. May modify
-     * this URI in order to get it into a compliant form.
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool True if validation/filtering succeeds, false if failure
-     */
-    public function validate($config, $context)
-    {
-        // ABNF definitions from RFC 3986
-        $chars_sub_delims = '!$&\'()*+,;=';
-        $chars_gen_delims = ':/?#[]@';
-        $chars_pchar = $chars_sub_delims . ':@';
-
-        // validate host
-        if (!is_null($this->host)) {
-            $host_def = new HTMLPurifier_AttrDef_URI_Host();
-            $this->host = $host_def->validate($this->host, $config, $context);
-            if ($this->host === false) {
-                $this->host = null;
-            }
-        }
-
-        // validate scheme
-        // NOTE: It's not appropriate to check whether or not this
-        // scheme is in our registry, since a URIFilter may convert a
-        // URI that we don't allow into one we do.  So instead, we just
-        // check if the scheme can be dropped because there is no host
-        // and it is our default scheme.
-        if (!is_null($this->scheme) && is_null($this->host) || $this->host === '') {
-            // support for relative paths is pretty abysmal when the
-            // scheme is present, so axe it when possible
-            $def = $config->getDefinition('URI');
-            if ($def->defaultScheme === $this->scheme) {
-                $this->scheme = null;
-            }
-        }
-
-        // validate username
-        if (!is_null($this->userinfo)) {
-            $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
-            $this->userinfo = $encoder->encode($this->userinfo);
-        }
-
-        // validate port
-        if (!is_null($this->port)) {
-            if ($this->port < 1 || $this->port > 65535) {
-                $this->port = null;
-            }
-        }
-
-        // validate path
-        $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
-        if (!is_null($this->host)) { // this catches $this->host === ''
-            // path-abempty (hier and relative)
-            // http://www.example.com/my/path
-            // //www.example.com/my/path (looks odd, but works, and
-            //                            recognized by most browsers)
-            // (this set is valid or invalid on a scheme by scheme
-            // basis, so we'll deal with it later)
-            // file:///my/path
-            // ///my/path
-            $this->path = $segments_encoder->encode($this->path);
-        } elseif ($this->path !== '') {
-            if ($this->path[0] === '/') {
-                // path-absolute (hier and relative)
-                // http:/my/path
-                // /my/path
-                if (strlen($this->path) >= 2 && $this->path[1] === '/') {
-                    // This could happen if both the host gets stripped
-                    // out
-                    // http://my/path
-                    // //my/path
-                    $this->path = '';
-                } else {
-                    $this->path = $segments_encoder->encode($this->path);
-                }
-            } elseif (!is_null($this->scheme)) {
-                // path-rootless (hier)
-                // http:my/path
-                // Short circuit evaluation means we don't need to check nz
-                $this->path = $segments_encoder->encode($this->path);
-            } else {
-                // path-noscheme (relative)
-                // my/path
-                // (once again, not checking nz)
-                $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
-                $c = strpos($this->path, '/');
-                if ($c !== false) {
-                    $this->path =
-                        $segment_nc_encoder->encode(substr($this->path, 0, $c)) .
-                        $segments_encoder->encode(substr($this->path, $c));
-                } else {
-                    $this->path = $segment_nc_encoder->encode($this->path);
-                }
-            }
-        } else {
-            // path-empty (hier and relative)
-            $this->path = ''; // just to be safe
-        }
-
-        // qf = query and fragment
-        $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?');
-
-        if (!is_null($this->query)) {
-            $this->query = $qf_encoder->encode($this->query);
-        }
-
-        if (!is_null($this->fragment)) {
-            $this->fragment = $qf_encoder->encode($this->fragment);
-        }
-        return true;
-    }
-
-    /**
-     * Convert URI back to string
-     * @return string URI appropriate for output
-     */
-    public function toString()
-    {
-        // reconstruct authority
-        $authority = null;
-        // there is a rendering difference between a null authority
-        // (http:foo-bar) and an empty string authority
-        // (http:///foo-bar).
-        if (!is_null($this->host)) {
-            $authority = '';
-            if (!is_null($this->userinfo)) {
-                $authority .= $this->userinfo . '@';
-            }
-            $authority .= $this->host;
-            if (!is_null($this->port)) {
-                $authority .= ':' . $this->port;
-            }
-        }
-
-        // Reconstruct the result
-        // One might wonder about parsing quirks from browsers after
-        // this reconstruction.  Unfortunately, parsing behavior depends
-        // on what *scheme* was employed (file:///foo is handled *very*
-        // differently than http:///foo), so unfortunately we have to
-        // defer to the schemes to do the right thing.
-        $result = '';
-        if (!is_null($this->scheme)) {
-            $result .= $this->scheme . ':';
-        }
-        if (!is_null($authority)) {
-            $result .= '//' . $authority;
-        }
-        $result .= $this->path;
-        if (!is_null($this->query)) {
-            $result .= '?' . $this->query;
-        }
-        if (!is_null($this->fragment)) {
-            $result .= '#' . $this->fragment;
-        }
-
-        return $result;
-    }
-
-    /**
-     * Returns true if this URL might be considered a 'local' URL given
-     * the current context.  This is true when the host is null, or
-     * when it matches the host supplied to the configuration.
-     *
-     * Note that this does not do any scheme checking, so it is mostly
-     * only appropriate for metadata that doesn't care about protocol
-     * security.  isBenign is probably what you actually want.
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function isLocal($config, $context)
-    {
-        if ($this->host === null) {
-            return true;
-        }
-        $uri_def = $config->getDefinition('URI');
-        if ($uri_def->host === $this->host) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if this URL should be considered a 'benign' URL,
-     * that is:
-     *
-     *      - It is a local URL (isLocal), and
-     *      - It has a equal or better level of security
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function isBenign($config, $context)
-    {
-        if (!$this->isLocal($config, $context)) {
-            return false;
-        }
-
-        $scheme_obj = $this->getSchemeObj($config, $context);
-        if (!$scheme_obj) {
-            return false;
-        } // conservative approach
-
-        $current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context);
-        if ($current_scheme_obj->secure) {
-            if (!$scheme_obj->secure) {
-                return false;
-            }
-        }
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php
deleted file mode 100644
index e0bd8bc..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
-{
-
-    public $type = 'URI';
-    protected $filters = array();
-    protected $postFilters = array();
-    protected $registeredFilters = array();
-
-    /**
-     * HTMLPurifier_URI object of the base specified at %URI.Base
-     */
-    public $base;
-
-    /**
-     * String host to consider "home" base, derived off of $base
-     */
-    public $host;
-
-    /**
-     * Name of default scheme based on %URI.DefaultScheme and %URI.Base
-     */
-    public $defaultScheme;
-
-    public function __construct()
-    {
-        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternal());
-        $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources());
-        $this->registerFilter(new HTMLPurifier_URIFilter_DisableResources());
-        $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist());
-        $this->registerFilter(new HTMLPurifier_URIFilter_SafeIframe());
-        $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute());
-        $this->registerFilter(new HTMLPurifier_URIFilter_Munge());
-    }
-
-    public function registerFilter($filter)
-    {
-        $this->registeredFilters[$filter->name] = $filter;
-    }
-
-    public function addFilter($filter, $config)
-    {
-        $r = $filter->prepare($config);
-        if ($r === false) return; // null is ok, for backwards compat
-        if ($filter->post) {
-            $this->postFilters[$filter->name] = $filter;
-        } else {
-            $this->filters[$filter->name] = $filter;
-        }
-    }
-
-    protected function doSetup($config)
-    {
-        $this->setupMemberVariables($config);
-        $this->setupFilters($config);
-    }
-
-    protected function setupFilters($config)
-    {
-        foreach ($this->registeredFilters as $name => $filter) {
-            if ($filter->always_load) {
-                $this->addFilter($filter, $config);
-            } else {
-                $conf = $config->get('URI.' . $name);
-                if ($conf !== false && $conf !== null) {
-                    $this->addFilter($filter, $config);
-                }
-            }
-        }
-        unset($this->registeredFilters);
-    }
-
-    protected function setupMemberVariables($config)
-    {
-        $this->host = $config->get('URI.Host');
-        $base_uri = $config->get('URI.Base');
-        if (!is_null($base_uri)) {
-            $parser = new HTMLPurifier_URIParser();
-            $this->base = $parser->parse($base_uri);
-            $this->defaultScheme = $this->base->scheme;
-            if (is_null($this->host)) $this->host = $this->base->host;
-        }
-        if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme');
-    }
-
-    public function getDefaultScheme($config, $context)
-    {
-        return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context);
-    }
-
-    public function filter(&$uri, $config, $context)
-    {
-        foreach ($this->filters as $name => $f) {
-            $result = $f->filter($uri, $config, $context);
-            if (!$result) return false;
-        }
-        return true;
-    }
-
-    public function postFilter(&$uri, $config, $context)
-    {
-        foreach ($this->postFilters as $name => $f) {
-            $result = $f->filter($uri, $config, $context);
-            if (!$result) return false;
-        }
-        return true;
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php
deleted file mode 100644
index 09724e9..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * Chainable filters for custom URI processing.
- *
- * These filters can perform custom actions on a URI filter object,
- * including transformation or blacklisting.  A filter named Foo
- * must have a corresponding configuration directive %URI.Foo,
- * unless always_load is specified to be true.
- *
- * The following contexts may be available while URIFilters are being
- * processed:
- *
- *      - EmbeddedURI: true if URI is an embedded resource that will
- *        be loaded automatically on page load
- *      - CurrentToken: a reference to the token that is currently
- *        being processed
- *      - CurrentAttr: the name of the attribute that is currently being
- *        processed
- *      - CurrentCSSProperty: the name of the CSS property that is
- *        currently being processed (if applicable)
- *
- * @warning This filter is called before scheme object validation occurs.
- *          Make sure, if you require a specific scheme object, you
- *          you check that it exists. This allows filters to convert
- *          proprietary URI schemes into regular ones.
- */
-abstract class HTMLPurifier_URIFilter
-{
-
-    /**
-     * Unique identifier of filter.
-     * @type string
-     */
-    public $name;
-
-    /**
-     * True if this filter should be run after scheme validation.
-     * @type bool
-     */
-    public $post = false;
-
-    /**
-     * True if this filter should always be loaded.
-     * This permits a filter to be named Foo without the corresponding
-     * %URI.Foo directive existing.
-     * @type bool
-     */
-    public $always_load = false;
-
-    /**
-     * Performs initialization for the filter.  If the filter returns
-     * false, this means that it shouldn't be considered active.
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function prepare($config)
-    {
-        return true;
-    }
-
-    /**
-     * Filter a URI object
-     * @param HTMLPurifier_URI $uri Reference to URI object variable
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool Whether or not to continue processing: false indicates
-     *         URL is no good, true indicates continue processing. Note that
-     *         all changes are committed directly on the URI object
-     */
-    abstract public function filter(&$uri, $config, $context);
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php
deleted file mode 100644
index ced1b13..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternal.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter
-{
-    /**
-     * @type string
-     */
-    public $name = 'DisableExternal';
-
-    /**
-     * @type array
-     */
-    protected $ourHostParts = false;
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return void
-     */
-    public function prepare($config)
-    {
-        $our_host = $config->getDefinition('URI')->host;
-        if ($our_host !== null) {
-            $this->ourHostParts = array_reverse(explode('.', $our_host));
-        }
-    }
-
-    /**
-     * @param HTMLPurifier_URI $uri Reference
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function filter(&$uri, $config, $context)
-    {
-        if (is_null($uri->host)) {
-            return true;
-        }
-        if ($this->ourHostParts === false) {
-            return false;
-        }
-        $host_parts = array_reverse(explode('.', $uri->host));
-        foreach ($this->ourHostParts as $i => $x) {
-            if (!isset($host_parts[$i])) {
-                return false;
-            }
-            if ($host_parts[$i] != $this->ourHostParts[$i]) {
-                return false;
-            }
-        }
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php
deleted file mode 100644
index c656216..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal
-{
-    /**
-     * @type string
-     */
-    public $name = 'DisableExternalResources';
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function filter(&$uri, $config, $context)
-    {
-        if (!$context->get('EmbeddedURI', true)) {
-            return true;
-        }
-        return parent::filter($uri, $config, $context);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php
deleted file mode 100644
index d5c412c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_DisableResources extends HTMLPurifier_URIFilter
-{
-    /**
-     * @type string
-     */
-    public $name = 'DisableResources';
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function filter(&$uri, $config, $context)
-    {
-        return !$context->get('EmbeddedURI', true);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php
deleted file mode 100644
index 32197c0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-// It's not clear to me whether or not Punycode means that hostnames
-// do not have canonical forms anymore. As far as I can tell, it's
-// not a problem (punycoding should be identity when no Unicode
-// points are involved), but I'm not 100% sure
-class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
-{
-    /**
-     * @type string
-     */
-    public $name = 'HostBlacklist';
-
-    /**
-     * @type array
-     */
-    protected $blacklist = array();
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function prepare($config)
-    {
-        $this->blacklist = $config->get('URI.HostBlacklist');
-        return true;
-    }
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function filter(&$uri, $config, $context)
-    {
-        foreach ($this->blacklist as $blacklisted_host_fragment) {
-            if ($uri->host !== null && strpos($uri->host, $blacklisted_host_fragment) !== false) {
-                return false;
-            }
-        }
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php
deleted file mode 100644
index c507bbf..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php
+++ /dev/null
@@ -1,158 +0,0 @@
-<?php
-
-// does not support network paths
-
-class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
-{
-    /**
-     * @type string
-     */
-    public $name = 'MakeAbsolute';
-
-    /**
-     * @type
-     */
-    protected $base;
-
-    /**
-     * @type array
-     */
-    protected $basePathStack = array();
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function prepare($config)
-    {
-        $def = $config->getDefinition('URI');
-        $this->base = $def->base;
-        if (is_null($this->base)) {
-            trigger_error(
-                'URI.MakeAbsolute is being ignored due to lack of ' .
-                'value for URI.Base configuration',
-                E_USER_WARNING
-            );
-            return false;
-        }
-        $this->base->fragment = null; // fragment is invalid for base URI
-        $stack = explode('/', $this->base->path);
-        array_pop($stack); // discard last segment
-        $stack = $this->_collapseStack($stack); // do pre-parsing
-        $this->basePathStack = $stack;
-        return true;
-    }
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function filter(&$uri, $config, $context)
-    {
-        if (is_null($this->base)) {
-            return true;
-        } // abort early
-        if ($uri->path === '' && is_null($uri->scheme) &&
-            is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) {
-            // reference to current document
-            $uri = clone $this->base;
-            return true;
-        }
-        if (!is_null($uri->scheme)) {
-            // absolute URI already: don't change
-            if (!is_null($uri->host)) {
-                return true;
-            }
-            $scheme_obj = $uri->getSchemeObj($config, $context);
-            if (!$scheme_obj) {
-                // scheme not recognized
-                return false;
-            }
-            if (!$scheme_obj->hierarchical) {
-                // non-hierarchal URI with explicit scheme, don't change
-                return true;
-            }
-            // special case: had a scheme but always is hierarchical and had no authority
-        }
-        if (!is_null($uri->host)) {
-            // network path, don't bother
-            return true;
-        }
-        if ($uri->path === '') {
-            $uri->path = $this->base->path;
-        } elseif ($uri->path[0] !== '/') {
-            // relative path, needs more complicated processing
-            $stack = explode('/', $uri->path);
-            $new_stack = array_merge($this->basePathStack, $stack);
-            if ($new_stack[0] !== '' && !is_null($this->base->host)) {
-                array_unshift($new_stack, '');
-            }
-            $new_stack = $this->_collapseStack($new_stack);
-            $uri->path = implode('/', $new_stack);
-        } else {
-            // absolute path, but still we should collapse
-            $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path)));
-        }
-        // re-combine
-        $uri->scheme = $this->base->scheme;
-        if (is_null($uri->userinfo)) {
-            $uri->userinfo = $this->base->userinfo;
-        }
-        if (is_null($uri->host)) {
-            $uri->host = $this->base->host;
-        }
-        if (is_null($uri->port)) {
-            $uri->port = $this->base->port;
-        }
-        return true;
-    }
-
-    /**
-     * Resolve dots and double-dots in a path stack
-     * @param array $stack
-     * @return array
-     */
-    private function _collapseStack($stack)
-    {
-        $result = array();
-        $is_folder = false;
-        for ($i = 0; isset($stack[$i]); $i++) {
-            $is_folder = false;
-            // absorb an internally duplicated slash
-            if ($stack[$i] == '' && $i && isset($stack[$i + 1])) {
-                continue;
-            }
-            if ($stack[$i] == '..') {
-                if (!empty($result)) {
-                    $segment = array_pop($result);
-                    if ($segment === '' && empty($result)) {
-                        // error case: attempted to back out too far:
-                        // restore the leading slash
-                        $result[] = '';
-                    } elseif ($segment === '..') {
-                        $result[] = '..'; // cannot remove .. with ..
-                    }
-                } else {
-                    // relative path, preserve the double-dots
-                    $result[] = '..';
-                }
-                $is_folder = true;
-                continue;
-            }
-            if ($stack[$i] == '.') {
-                // silently absorb
-                $is_folder = true;
-                continue;
-            }
-            $result[] = $stack[$i];
-        }
-        if ($is_folder) {
-            $result[] = '';
-        }
-        return $result;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php
deleted file mode 100644
index e1393de..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
-{
-    /**
-     * @type string
-     */
-    public $name = 'Munge';
-
-    /**
-     * @type bool
-     */
-    public $post = true;
-
-    /**
-     * @type string
-     */
-    private $target;
-
-    /**
-     * @type HTMLPurifier_URIParser
-     */
-    private $parser;
-
-    /**
-     * @type bool
-     */
-    private $doEmbed;
-
-    /**
-     * @type string
-     */
-    private $secretKey;
-
-    /**
-     * @type array
-     */
-    protected $replace = array();
-
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function prepare($config)
-    {
-        $this->target = $config->get('URI.' . $this->name);
-        $this->parser = new HTMLPurifier_URIParser();
-        $this->doEmbed = $config->get('URI.MungeResources');
-        $this->secretKey = $config->get('URI.MungeSecretKey');
-        if ($this->secretKey && !function_exists('hash_hmac')) {
-            throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support.");
-        }
-        return true;
-    }
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function filter(&$uri, $config, $context)
-    {
-        if ($context->get('EmbeddedURI', true) && !$this->doEmbed) {
-            return true;
-        }
-
-        $scheme_obj = $uri->getSchemeObj($config, $context);
-        if (!$scheme_obj) {
-            return true;
-        } // ignore unknown schemes, maybe another postfilter did it
-        if (!$scheme_obj->browsable) {
-            return true;
-        } // ignore non-browseable schemes, since we can't munge those in a reasonable way
-        if ($uri->isBenign($config, $context)) {
-            return true;
-        } // don't redirect if a benign URL
-
-        $this->makeReplace($uri, $config, $context);
-        $this->replace = array_map('rawurlencode', $this->replace);
-
-        $new_uri = strtr($this->target, $this->replace);
-        $new_uri = $this->parser->parse($new_uri);
-        // don't redirect if the target host is the same as the
-        // starting host
-        if ($uri->host === $new_uri->host) {
-            return true;
-        }
-        $uri = $new_uri; // overwrite
-        return true;
-    }
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     */
-    protected function makeReplace($uri, $config, $context)
-    {
-        $string = $uri->toString();
-        // always available
-        $this->replace['%s'] = $string;
-        $this->replace['%r'] = $context->get('EmbeddedURI', true) ?: '';
-        $token = $context->get('CurrentToken', true) ?: '';
-        $this->replace['%n'] = $token ? $token->name : '';
-        $this->replace['%m'] = $context->get('CurrentAttr', true) ?: '';
-        $this->replace['%p'] = $context->get('CurrentCSSProperty', true) ?: '';
-        // not always available
-        if ($this->secretKey) {
-            $this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey);
-        }
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php
deleted file mode 100644
index f609c47..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * Implements safety checks for safe iframes.
- *
- * @warning This filter is *critical* for ensuring that %HTML.SafeIframe
- * works safely.
- */
-class HTMLPurifier_URIFilter_SafeIframe extends HTMLPurifier_URIFilter
-{
-    /**
-     * @type string
-     */
-    public $name = 'SafeIframe';
-
-    /**
-     * @type bool
-     */
-    public $always_load = true;
-
-    /**
-     * @type string
-     */
-    protected $regexp = null;
-
-    // XXX: The not so good bit about how this is all set up now is we
-    // can't check HTML.SafeIframe in the 'prepare' step: we have to
-    // defer till the actual filtering.
-    /**
-     * @param HTMLPurifier_Config $config
-     * @return bool
-     */
-    public function prepare($config)
-    {
-        $this->regexp = $config->get('URI.SafeIframeRegexp');
-        return true;
-    }
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function filter(&$uri, $config, $context)
-    {
-        // check if filter not applicable
-        if (!$config->get('HTML.SafeIframe')) {
-            return true;
-        }
-        // check if the filter should actually trigger
-        if (!$context->get('EmbeddedURI', true)) {
-            return true;
-        }
-        $token = $context->get('CurrentToken', true);
-        if (!($token && $token->name == 'iframe')) {
-            return true;
-        }
-        // check if we actually have some whitelists enabled
-        if ($this->regexp === null) {
-            return false;
-        }
-        // actually check the whitelists
-        return preg_match($this->regexp, $uri->toString());
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php
deleted file mode 100644
index 0e7381a..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * Parses a URI into the components and fragment identifier as specified
- * by RFC 3986.
- */
-class HTMLPurifier_URIParser
-{
-
-    /**
-     * Instance of HTMLPurifier_PercentEncoder to do normalization with.
-     */
-    protected $percentEncoder;
-
-    public function __construct()
-    {
-        $this->percentEncoder = new HTMLPurifier_PercentEncoder();
-    }
-
-    /**
-     * Parses a URI.
-     * @param $uri string URI to parse
-     * @return HTMLPurifier_URI representation of URI. This representation has
-     *         not been validated yet and may not conform to RFC.
-     */
-    public function parse($uri)
-    {
-        $uri = $this->percentEncoder->normalize($uri);
-
-        // Regexp is as per Appendix B.
-        // Note that ["<>] are an addition to the RFC's recommended
-        // characters, because they represent external delimeters.
-        $r_URI = '!'.
-            '(([a-zA-Z0-9\.\+\-]+):)?'. // 2. Scheme
-            '(//([^/?#"<>]*))?'. // 4. Authority
-            '([^?#"<>]*)'.       // 5. Path
-            '(\?([^#"<>]*))?'.   // 7. Query
-            '(#([^"<>]*))?'.     // 8. Fragment
-            '!';
-
-        $matches = array();
-        $result = preg_match($r_URI, $uri, $matches);
-
-        if (!$result) return false; // *really* invalid URI
-
-        // seperate out parts
-        $scheme     = !empty($matches[1]) ? $matches[2] : null;
-        $authority  = !empty($matches[3]) ? $matches[4] : null;
-        $path       = $matches[5]; // always present, can be empty
-        $query      = !empty($matches[6]) ? $matches[7] : null;
-        $fragment   = !empty($matches[8]) ? $matches[9] : null;
-
-        // further parse authority
-        if ($authority !== null) {
-            $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/";
-            $matches = array();
-            preg_match($r_authority, $authority, $matches);
-            $userinfo   = !empty($matches[1]) ? $matches[2] : null;
-            $host       = !empty($matches[3]) ? $matches[3] : '';
-            $port       = !empty($matches[4]) ? (int) $matches[5] : null;
-        } else {
-            $port = $host = $userinfo = null;
-        }
-
-        return new HTMLPurifier_URI(
-            $scheme, $userinfo, $host, $port, $path, $query, $fragment);
-    }
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php
deleted file mode 100644
index fe9e82c..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-/**
- * Validator for the components of a URI for a specific scheme
- */
-abstract class HTMLPurifier_URIScheme
-{
-
-    /**
-     * Scheme's default port (integer). If an explicit port number is
-     * specified that coincides with the default port, it will be
-     * elided.
-     * @type int
-     */
-    public $default_port = null;
-
-    /**
-     * Whether or not URIs of this scheme are locatable by a browser
-     * http and ftp are accessible, while mailto and news are not.
-     * @type bool
-     */
-    public $browsable = false;
-
-    /**
-     * Whether or not data transmitted over this scheme is encrypted.
-     * https is secure, http is not.
-     * @type bool
-     */
-    public $secure = false;
-
-    /**
-     * Whether or not the URI always uses <hier_part>, resolves edge cases
-     * with making relative URIs absolute
-     * @type bool
-     */
-    public $hierarchical = false;
-
-    /**
-     * Whether or not the URI may omit a hostname when the scheme is
-     * explicitly specified, ala file:///path/to/file. As of writing,
-     * 'file' is the only scheme that browsers support his properly.
-     * @type bool
-     */
-    public $may_omit_host = false;
-
-    /**
-     * Validates the components of a URI for a specific scheme.
-     * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool success or failure
-     */
-    abstract public function doValidate(&$uri, $config, $context);
-
-    /**
-     * Public interface for validating components of a URI.  Performs a
-     * bunch of default actions. Don't overload this method.
-     * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool success or failure
-     */
-    public function validate(&$uri, $config, $context)
-    {
-        if ($this->default_port == $uri->port) {
-            $uri->port = null;
-        }
-        // kludge: browsers do funny things when the scheme but not the
-        // authority is set
-        if (!$this->may_omit_host &&
-            // if the scheme is present, a missing host is always in error
-            (!is_null($uri->scheme) && ($uri->host === '' || is_null($uri->host))) ||
-            // if the scheme is not present, a *blank* host is in error,
-            // since this translates into '///path' which most browsers
-            // interpret as being 'http://path'.
-            (is_null($uri->scheme) && $uri->host === '')
-        ) {
-            do {
-                if (is_null($uri->scheme)) {
-                    if (substr($uri->path, 0, 2) != '//') {
-                        $uri->host = null;
-                        break;
-                    }
-                    // URI is '////path', so we cannot nullify the
-                    // host to preserve semantics.  Try expanding the
-                    // hostname instead (fall through)
-                }
-                // first see if we can manually insert a hostname
-                $host = $config->get('URI.Host');
-                if (!is_null($host)) {
-                    $uri->host = $host;
-                } else {
-                    // we can't do anything sensible, reject the URL.
-                    return false;
-                }
-            } while (false);
-        }
-        return $this->doValidate($uri, $config, $context);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php
deleted file mode 100644
index 41c49d5..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * Implements data: URI for base64 encoded images supported by GD.
- */
-class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme
-{
-    /**
-     * @type bool
-     */
-    public $browsable = true;
-
-    /**
-     * @type array
-     */
-    public $allowed_types = array(
-        // you better write validation code for other types if you
-        // decide to allow them
-        'image/jpeg' => true,
-        'image/gif' => true,
-        'image/png' => true,
-    );
-    // this is actually irrelevant since we only write out the path
-    // component
-    /**
-     * @type bool
-     */
-    public $may_omit_host = true;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        $result = explode(',', $uri->path, 2);
-        $is_base64 = false;
-        $charset = null;
-        $content_type = null;
-        if (count($result) == 2) {
-            list($metadata, $data) = $result;
-            // do some legwork on the metadata
-            $metas = explode(';', $metadata);
-            while (!empty($metas)) {
-                $cur = array_shift($metas);
-                if ($cur == 'base64') {
-                    $is_base64 = true;
-                    break;
-                }
-                if (substr($cur, 0, 8) == 'charset=') {
-                    // doesn't match if there are arbitrary spaces, but
-                    // whatever dude
-                    if ($charset !== null) {
-                        continue;
-                    } // garbage
-                    $charset = substr($cur, 8); // not used
-                } else {
-                    if ($content_type !== null) {
-                        continue;
-                    } // garbage
-                    $content_type = $cur;
-                }
-            }
-        } else {
-            $data = $result[0];
-        }
-        if ($content_type !== null && empty($this->allowed_types[$content_type])) {
-            return false;
-        }
-        if ($charset !== null) {
-            // error; we don't allow plaintext stuff
-            $charset = null;
-        }
-        $data = rawurldecode($data);
-        if ($is_base64) {
-            $raw_data = base64_decode($data);
-        } else {
-            $raw_data = $data;
-        }
-        if ( strlen($raw_data) < 12 ) {
-            // error; exif_imagetype throws exception with small files,
-            // and this likely indicates a corrupt URI/failed parse anyway
-            return false;
-        }
-        // XXX probably want to refactor this into a general mechanism
-        // for filtering arbitrary content types
-        if (function_exists('sys_get_temp_dir')) {
-            $file = tempnam(sys_get_temp_dir(), "");
-        } else {
-            $file = tempnam("/tmp", "");
-        }
-        file_put_contents($file, $raw_data);
-        if (function_exists('exif_imagetype')) {
-            $image_code = exif_imagetype($file);
-            unlink($file);
-        } elseif (function_exists('getimagesize')) {
-            set_error_handler(array($this, 'muteErrorHandler'));
-            $info = getimagesize($file);
-            restore_error_handler();
-            unlink($file);
-            if ($info == false) {
-                return false;
-            }
-            $image_code = $info[2];
-        } else {
-            trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR);
-        }
-        $real_content_type = image_type_to_mime_type($image_code);
-        if ($real_content_type != $content_type) {
-            // we're nice guys; if the content type is something else we
-            // support, change it over
-            if (empty($this->allowed_types[$real_content_type])) {
-                return false;
-            }
-            $content_type = $real_content_type;
-        }
-        // ok, it's kosher, rewrite what we need
-        $uri->userinfo = null;
-        $uri->host = null;
-        $uri->port = null;
-        $uri->fragment = null;
-        $uri->query = null;
-        $uri->path = "$content_type;base64," . base64_encode($raw_data);
-        return true;
-    }
-
-    /**
-     * @param int $errno
-     * @param string $errstr
-     */
-    public function muteErrorHandler($errno, $errstr)
-    {
-    }
-}
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php
deleted file mode 100644
index 215be4b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * Validates file as defined by RFC 1630 and RFC 1738.
- */
-class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme
-{
-    /**
-     * Generally file:// URLs are not accessible from most
-     * machines, so placing them as an img src is incorrect.
-     * @type bool
-     */
-    public $browsable = false;
-
-    /**
-     * Basically the *only* URI scheme for which this is true, since
-     * accessing files on the local machine is very common.  In fact,
-     * browsers on some operating systems don't understand the
-     * authority, though I hear it is used on Windows to refer to
-     * network shares.
-     * @type bool
-     */
-    public $may_omit_host = true;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        // Authentication method is not supported
-        $uri->userinfo = null;
-        // file:// makes no provisions for accessing the resource
-        $uri->port = null;
-        // While it seems to work on Firefox, the querystring has
-        // no possible effect and is thus stripped.
-        $uri->query = null;
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php
deleted file mode 100644
index 1eb43ee..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738.
- */
-class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme
-{
-    /**
-     * @type int
-     */
-    public $default_port = 21;
-
-    /**
-     * @type bool
-     */
-    public $browsable = true; // usually
-
-    /**
-     * @type bool
-     */
-    public $hierarchical = true;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        $uri->query = null;
-
-        // typecode check
-        $semicolon_pos = strrpos($uri->path, ';'); // reverse
-        if ($semicolon_pos !== false) {
-            $type = substr($uri->path, $semicolon_pos + 1); // no semicolon
-            $uri->path = substr($uri->path, 0, $semicolon_pos);
-            $type_ret = '';
-            if (strpos($type, '=') !== false) {
-                // figure out whether or not the declaration is correct
-                list($key, $typecode) = explode('=', $type, 2);
-                if ($key !== 'type') {
-                    // invalid key, tack it back on encoded
-                    $uri->path .= '%3B' . $type;
-                } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') {
-                    $type_ret = ";type=$typecode";
-                }
-            } else {
-                $uri->path .= '%3B' . $type;
-            }
-            $uri->path = str_replace(';', '%3B', $uri->path);
-            $uri->path .= $type_ret;
-        }
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php
deleted file mode 100644
index ce69ec4..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * Validates http (HyperText Transfer Protocol) as defined by RFC 2616
- */
-class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme
-{
-    /**
-     * @type int
-     */
-    public $default_port = 80;
-
-    /**
-     * @type bool
-     */
-    public $browsable = true;
-
-    /**
-     * @type bool
-     */
-    public $hierarchical = true;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        $uri->userinfo = null;
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php
deleted file mode 100644
index 0e96882..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * Validates https (Secure HTTP) according to http scheme.
- */
-class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http
-{
-    /**
-     * @type int
-     */
-    public $default_port = 443;
-    /**
-     * @type bool
-     */
-    public $secure = true;
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php
deleted file mode 100644
index c3a6b60..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-// VERY RELAXED! Shouldn't cause problems, not even Firefox checks if the
-// email is valid, but be careful!
-
-/**
- * Validates mailto (for E-mail) according to RFC 2368
- * @todo Validate the email address
- * @todo Filter allowed query parameters
- */
-
-class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme
-{
-    /**
-     * @type bool
-     */
-    public $browsable = false;
-
-    /**
-     * @type bool
-     */
-    public $may_omit_host = true;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        $uri->userinfo = null;
-        $uri->host     = null;
-        $uri->port     = null;
-        // we need to validate path against RFC 2368's addr-spec
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php
deleted file mode 100644
index 7490927..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/**
- * Validates news (Usenet) as defined by generic RFC 1738
- */
-class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme
-{
-    /**
-     * @type bool
-     */
-    public $browsable = false;
-
-    /**
-     * @type bool
-     */
-    public $may_omit_host = true;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        $uri->userinfo = null;
-        $uri->host = null;
-        $uri->port = null;
-        $uri->query = null;
-        // typecode check needed on path
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php
deleted file mode 100644
index f211d71..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Validates nntp (Network News Transfer Protocol) as defined by generic RFC 1738
- */
-class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme
-{
-    /**
-     * @type int
-     */
-    public $default_port = 119;
-
-    /**
-     * @type bool
-     */
-    public $browsable = false;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        $uri->userinfo = null;
-        $uri->query = null;
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php
deleted file mode 100644
index 8cd1933..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * Validates tel (for phone numbers).
- *
- * The relevant specifications for this protocol are RFC 3966 and RFC 5341,
- * but this class takes a much simpler approach: we normalize phone
- * numbers so that they only include (possibly) a leading plus,
- * and then any number of digits and x'es.
- */
-
-class HTMLPurifier_URIScheme_tel extends HTMLPurifier_URIScheme
-{
-    /**
-     * @type bool
-     */
-    public $browsable = false;
-
-    /**
-     * @type bool
-     */
-    public $may_omit_host = true;
-
-    /**
-     * @param HTMLPurifier_URI $uri
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return bool
-     */
-    public function doValidate(&$uri, $config, $context)
-    {
-        $uri->userinfo = null;
-        $uri->host     = null;
-        $uri->port     = null;
-
-        // Delete all non-numeric characters, non-x characters
-        // from phone number, EXCEPT for a leading plus sign.
-        $uri->path = preg_replace('/(?!^\+)[^\dx]/', '',
-                     // Normalize e(x)tension to lower-case
-                     str_replace('X', 'x', $uri->path));
-
-        return true;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php
deleted file mode 100644
index 4ac8a0b..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/**
- * Registry for retrieving specific URI scheme validator objects.
- */
-class HTMLPurifier_URISchemeRegistry
-{
-
-    /**
-     * Retrieve sole instance of the registry.
-     * @param HTMLPurifier_URISchemeRegistry $prototype Optional prototype to overload sole instance with,
-     *                   or bool true to reset to default registry.
-     * @return HTMLPurifier_URISchemeRegistry
-     * @note Pass a registry object $prototype with a compatible interface and
-     *       the function will copy it and return it all further times.
-     */
-    public static function instance($prototype = null)
-    {
-        static $instance = null;
-        if ($prototype !== null) {
-            $instance = $prototype;
-        } elseif ($instance === null || $prototype == true) {
-            $instance = new HTMLPurifier_URISchemeRegistry();
-        }
-        return $instance;
-    }
-
-    /**
-     * Cache of retrieved schemes.
-     * @type HTMLPurifier_URIScheme[]
-     */
-    protected $schemes = array();
-
-    /**
-     * Retrieves a scheme validator object
-     * @param string $scheme String scheme name like http or mailto
-     * @param HTMLPurifier_Config $config
-     * @param HTMLPurifier_Context $context
-     * @return HTMLPurifier_URIScheme
-     */
-    public function getScheme($scheme, $config, $context)
-    {
-        if (!$config) {
-            $config = HTMLPurifier_Config::createDefault();
-        }
-
-        // important, otherwise attacker could include arbitrary file
-        $allowed_schemes = $config->get('URI.AllowedSchemes');
-        if (!$config->get('URI.OverrideAllowedSchemes') &&
-            !isset($allowed_schemes[$scheme])
-        ) {
-            return;
-        }
-
-        if (isset($this->schemes[$scheme])) {
-            return $this->schemes[$scheme];
-        }
-        if (!isset($allowed_schemes[$scheme])) {
-            return;
-        }
-
-        $class = 'HTMLPurifier_URIScheme_' . $scheme;
-        if (!class_exists($class)) {
-            return;
-        }
-        $this->schemes[$scheme] = new $class();
-        return $this->schemes[$scheme];
-    }
-
-    /**
-     * Registers a custom scheme to the cache, bypassing reflection.
-     * @param string $scheme Scheme name
-     * @param HTMLPurifier_URIScheme $scheme_obj
-     */
-    public function register($scheme, $scheme_obj)
-    {
-        $this->schemes[$scheme] = $scheme_obj;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php
deleted file mode 100644
index 166f3bf..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php
+++ /dev/null
@@ -1,307 +0,0 @@
-<?php
-
-/**
- * Class for converting between different unit-lengths as specified by
- * CSS.
- */
-class HTMLPurifier_UnitConverter
-{
-
-    const ENGLISH = 1;
-    const METRIC = 2;
-    const DIGITAL = 3;
-
-    /**
-     * Units information array. Units are grouped into measuring systems
-     * (English, Metric), and are assigned an integer representing
-     * the conversion factor between that unit and the smallest unit in
-     * the system. Numeric indexes are actually magical constants that
-     * encode conversion data from one system to the next, with a O(n^2)
-     * constraint on memory (this is generally not a problem, since
-     * the number of measuring systems is small.)
-     */
-    protected static $units = array(
-        self::ENGLISH => array(
-            'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary
-            'pt' => 4,
-            'pc' => 48,
-            'in' => 288,
-            self::METRIC => array('pt', '0.352777778', 'mm'),
-        ),
-        self::METRIC => array(
-            'mm' => 1,
-            'cm' => 10,
-            self::ENGLISH => array('mm', '2.83464567', 'pt'),
-        ),
-    );
-
-    /**
-     * Minimum bcmath precision for output.
-     * @type int
-     */
-    protected $outputPrecision;
-
-    /**
-     * Bcmath precision for internal calculations.
-     * @type int
-     */
-    protected $internalPrecision;
-
-    /**
-     * Whether or not BCMath is available.
-     * @type bool
-     */
-    private $bcmath;
-
-    public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false)
-    {
-        $this->outputPrecision = $output_precision;
-        $this->internalPrecision = $internal_precision;
-        $this->bcmath = !$force_no_bcmath && function_exists('bcmul');
-    }
-
-    /**
-     * Converts a length object of one unit into another unit.
-     * @param HTMLPurifier_Length $length
-     *      Instance of HTMLPurifier_Length to convert. You must validate()
-     *      it before passing it here!
-     * @param string $to_unit
-     *      Unit to convert to.
-     * @return HTMLPurifier_Length|bool
-     * @note
-     *      About precision: This conversion function pays very special
-     *      attention to the incoming precision of values and attempts
-     *      to maintain a number of significant figure. Results are
-     *      fairly accurate up to nine digits. Some caveats:
-     *          - If a number is zero-padded as a result of this significant
-     *            figure tracking, the zeroes will be eliminated.
-     *          - If a number contains less than four sigfigs ($outputPrecision)
-     *            and this causes some decimals to be excluded, those
-     *            decimals will be added on.
-     */
-    public function convert($length, $to_unit)
-    {
-        if (!$length->isValid()) {
-            return false;
-        }
-
-        $n = $length->getN();
-        $unit = $length->getUnit();
-
-        if ($n === '0' || $unit === false) {
-            return new HTMLPurifier_Length('0', false);
-        }
-
-        $state = $dest_state = false;
-        foreach (self::$units as $k => $x) {
-            if (isset($x[$unit])) {
-                $state = $k;
-            }
-            if (isset($x[$to_unit])) {
-                $dest_state = $k;
-            }
-        }
-        if (!$state || !$dest_state) {
-            return false;
-        }
-
-        // Some calculations about the initial precision of the number;
-        // this will be useful when we need to do final rounding.
-        $sigfigs = $this->getSigFigs($n);
-        if ($sigfigs < $this->outputPrecision) {
-            $sigfigs = $this->outputPrecision;
-        }
-
-        // BCMath's internal precision deals only with decimals. Use
-        // our default if the initial number has no decimals, or increase
-        // it by how ever many decimals, thus, the number of guard digits
-        // will always be greater than or equal to internalPrecision.
-        $log = (int)floor(log(abs($n), 10));
-        $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision
-
-        for ($i = 0; $i < 2; $i++) {
-
-            // Determine what unit IN THIS SYSTEM we need to convert to
-            if ($dest_state === $state) {
-                // Simple conversion
-                $dest_unit = $to_unit;
-            } else {
-                // Convert to the smallest unit, pending a system shift
-                $dest_unit = self::$units[$state][$dest_state][0];
-            }
-
-            // Do the conversion if necessary
-            if ($dest_unit !== $unit) {
-                $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp);
-                $n = $this->mul($n, $factor, $cp);
-                $unit = $dest_unit;
-            }
-
-            // Output was zero, so bail out early. Shouldn't ever happen.
-            if ($n === '') {
-                $n = '0';
-                $unit = $to_unit;
-                break;
-            }
-
-            // It was a simple conversion, so bail out
-            if ($dest_state === $state) {
-                break;
-            }
-
-            if ($i !== 0) {
-                // Conversion failed! Apparently, the system we forwarded
-                // to didn't have this unit. This should never happen!
-                return false;
-            }
-
-            // Pre-condition: $i == 0
-
-            // Perform conversion to next system of units
-            $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp);
-            $unit = self::$units[$state][$dest_state][2];
-            $state = $dest_state;
-
-            // One more loop around to convert the unit in the new system.
-
-        }
-
-        // Post-condition: $unit == $to_unit
-        if ($unit !== $to_unit) {
-            return false;
-        }
-
-        // Useful for debugging:
-        //echo "<pre>n";
-        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n</pre>\n";
-
-        $n = $this->round($n, $sigfigs);
-        if (strpos($n, '.') !== false) {
-            $n = rtrim($n, '0');
-        }
-        $n = rtrim($n, '.');
-
-        return new HTMLPurifier_Length($n, $unit);
-    }
-
-    /**
-     * Returns the number of significant figures in a string number.
-     * @param string $n Decimal number
-     * @return int number of sigfigs
-     */
-    public function getSigFigs($n)
-    {
-        $n = ltrim($n, '0+-');
-        $dp = strpos($n, '.'); // decimal position
-        if ($dp === false) {
-            $sigfigs = strlen(rtrim($n, '0'));
-        } else {
-            $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character
-            if ($dp !== 0) {
-                $sigfigs--;
-            }
-        }
-        return $sigfigs;
-    }
-
-    /**
-     * Adds two numbers, using arbitrary precision when available.
-     * @param string $s1
-     * @param string $s2
-     * @param int $scale
-     * @return string
-     */
-    private function add($s1, $s2, $scale)
-    {
-        if ($this->bcmath) {
-            return bcadd($s1, $s2, $scale);
-        } else {
-            return $this->scale((float)$s1 + (float)$s2, $scale);
-        }
-    }
-
-    /**
-     * Multiples two numbers, using arbitrary precision when available.
-     * @param string $s1
-     * @param string $s2
-     * @param int $scale
-     * @return string
-     */
-    private function mul($s1, $s2, $scale)
-    {
-        if ($this->bcmath) {
-            return bcmul($s1, $s2, $scale);
-        } else {
-            return $this->scale((float)$s1 * (float)$s2, $scale);
-        }
-    }
-
-    /**
-     * Divides two numbers, using arbitrary precision when available.
-     * @param string $s1
-     * @param string $s2
-     * @param int $scale
-     * @return string
-     */
-    private function div($s1, $s2, $scale)
-    {
-        if ($this->bcmath) {
-            return bcdiv($s1, $s2, $scale);
-        } else {
-            return $this->scale((float)$s1 / (float)$s2, $scale);
-        }
-    }
-
-    /**
-     * Rounds a number according to the number of sigfigs it should have,
-     * using arbitrary precision when available.
-     * @param float $n
-     * @param int $sigfigs
-     * @return string
-     */
-    private function round($n, $sigfigs)
-    {
-        $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1
-        $rp = $sigfigs - $new_log - 1; // Number of decimal places needed
-        $neg = $n < 0 ? '-' : ''; // Negative sign
-        if ($this->bcmath) {
-            if ($rp >= 0) {
-                $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1);
-                $n = bcdiv($n, '1', $rp);
-            } else {
-                // This algorithm partially depends on the standardized
-                // form of numbers that comes out of bcmath.
-                $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0);
-                $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1);
-            }
-            return $n;
-        } else {
-            return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1);
-        }
-    }
-
-    /**
-     * Scales a float to $scale digits right of decimal point, like BCMath.
-     * @param float $r
-     * @param int $scale
-     * @return string
-     */
-    private function scale($r, $scale)
-    {
-        if ($scale < 0) {
-            // The f sprintf type doesn't support negative numbers, so we
-            // need to cludge things manually. First get the string.
-            $r = sprintf('%.0f', (float)$r);
-            // Due to floating point precision loss, $r will more than likely
-            // look something like 4652999999999.9234. We grab one more digit
-            // than we need to precise from $r and then use that to round
-            // appropriately.
-            $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1);
-            // Now we return it, truncating the zero that was rounded off.
-            return substr($precise, 0, -1) . str_repeat('0', -$scale + 1);
-        }
-        return sprintf('%.' . $scale . 'f', (float)$r);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php
deleted file mode 100644
index 0c97c82..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php
+++ /dev/null
@@ -1,198 +0,0 @@
-<?php
-
-/**
- * Parses string representations into their corresponding native PHP
- * variable type. The base implementation does a simple type-check.
- */
-class HTMLPurifier_VarParser
-{
-
-    const C_STRING = 1;
-    const ISTRING = 2;
-    const TEXT = 3;
-    const ITEXT = 4;
-    const C_INT = 5;
-    const C_FLOAT = 6;
-    const C_BOOL = 7;
-    const LOOKUP = 8;
-    const ALIST = 9;
-    const HASH = 10;
-    const C_MIXED = 11;
-
-    /**
-     * Lookup table of allowed types. Mainly for backwards compatibility, but
-     * also convenient for transforming string type names to the integer constants.
-     */
-    public static $types = array(
-        'string' => self::C_STRING,
-        'istring' => self::ISTRING,
-        'text' => self::TEXT,
-        'itext' => self::ITEXT,
-        'int' => self::C_INT,
-        'float' => self::C_FLOAT,
-        'bool' => self::C_BOOL,
-        'lookup' => self::LOOKUP,
-        'list' => self::ALIST,
-        'hash' => self::HASH,
-        'mixed' => self::C_MIXED
-    );
-
-    /**
-     * Lookup table of types that are string, and can have aliases or
-     * allowed value lists.
-     */
-    public static $stringTypes = array(
-        self::C_STRING => true,
-        self::ISTRING => true,
-        self::TEXT => true,
-        self::ITEXT => true,
-    );
-
-    /**
-     * Validate a variable according to type.
-     * It may return NULL as a valid type if $allow_null is true.
-     *
-     * @param mixed $var Variable to validate
-     * @param int $type Type of variable, see HTMLPurifier_VarParser->types
-     * @param bool $allow_null Whether or not to permit null as a value
-     * @return string Validated and type-coerced variable
-     * @throws HTMLPurifier_VarParserException
-     */
-    final public function parse($var, $type, $allow_null = false)
-    {
-        if (is_string($type)) {
-            if (!isset(HTMLPurifier_VarParser::$types[$type])) {
-                throw new HTMLPurifier_VarParserException("Invalid type '$type'");
-            } else {
-                $type = HTMLPurifier_VarParser::$types[$type];
-            }
-        }
-        $var = $this->parseImplementation($var, $type, $allow_null);
-        if ($allow_null && $var === null) {
-            return null;
-        }
-        // These are basic checks, to make sure nothing horribly wrong
-        // happened in our implementations.
-        switch ($type) {
-            case (self::C_STRING):
-            case (self::ISTRING):
-            case (self::TEXT):
-            case (self::ITEXT):
-                if (!is_string($var)) {
-                    break;
-                }
-                if ($type == self::ISTRING || $type == self::ITEXT) {
-                    $var = strtolower($var);
-                }
-                return $var;
-            case (self::C_INT):
-                if (!is_int($var)) {
-                    break;
-                }
-                return $var;
-            case (self::C_FLOAT):
-                if (!is_float($var)) {
-                    break;
-                }
-                return $var;
-            case (self::C_BOOL):
-                if (!is_bool($var)) {
-                    break;
-                }
-                return $var;
-            case (self::LOOKUP):
-            case (self::ALIST):
-            case (self::HASH):
-                if (!is_array($var)) {
-                    break;
-                }
-                if ($type === self::LOOKUP) {
-                    foreach ($var as $k) {
-                        if ($k !== true) {
-                            $this->error('Lookup table contains value other than true');
-                        }
-                    }
-                } elseif ($type === self::ALIST) {
-                    $keys = array_keys($var);
-                    if (array_keys($keys) !== $keys) {
-                        $this->error('Indices for list are not uniform');
-                    }
-                }
-                return $var;
-            case (self::C_MIXED):
-                return $var;
-            default:
-                $this->errorInconsistent(get_class($this), $type);
-        }
-        $this->errorGeneric($var, $type);
-    }
-
-    /**
-     * Actually implements the parsing. Base implementation does not
-     * do anything to $var. Subclasses should overload this!
-     * @param mixed $var
-     * @param int $type
-     * @param bool $allow_null
-     * @return string
-     */
-    protected function parseImplementation($var, $type, $allow_null)
-    {
-        return $var;
-    }
-
-    /**
-     * Throws an exception.
-     * @throws HTMLPurifier_VarParserException
-     */
-    protected function error($msg)
-    {
-        throw new HTMLPurifier_VarParserException($msg);
-    }
-
-    /**
-     * Throws an inconsistency exception.
-     * @note This should not ever be called. It would be called if we
-     *       extend the allowed values of HTMLPurifier_VarParser without
-     *       updating subclasses.
-     * @param string $class
-     * @param int $type
-     * @throws HTMLPurifier_Exception
-     */
-    protected function errorInconsistent($class, $type)
-    {
-        throw new HTMLPurifier_Exception(
-            "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) .
-            " not implemented"
-        );
-    }
-
-    /**
-     * Generic error for if a type didn't work.
-     * @param mixed $var
-     * @param int $type
-     */
-    protected function errorGeneric($var, $type)
-    {
-        $vtype = gettype($var);
-        $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype");
-    }
-
-    /**
-     * @param int $type
-     * @return string
-     */
-    public static function getTypeName($type)
-    {
-        static $lookup;
-        if (!$lookup) {
-            // Lazy load the alternative lookup table
-            $lookup = array_flip(HTMLPurifier_VarParser::$types);
-        }
-        if (!isset($lookup[$type])) {
-            return 'unknown';
-        }
-        return $lookup[$type];
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php
deleted file mode 100644
index 3bfbe83..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-
-/**
- * Performs safe variable parsing based on types which can be used by
- * users. This may not be able to represent all possible data inputs,
- * however.
- */
-class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
-{
-    /**
-     * @param mixed $var
-     * @param int $type
-     * @param bool $allow_null
-     * @return array|bool|float|int|mixed|null|string
-     * @throws HTMLPurifier_VarParserException
-     */
-    protected function parseImplementation($var, $type, $allow_null)
-    {
-        if ($allow_null && $var === null) {
-            return null;
-        }
-        switch ($type) {
-            // Note: if code "breaks" from the switch, it triggers a generic
-            // exception to be thrown. Specific errors can be specifically
-            // done here.
-            case self::C_MIXED:
-            case self::ISTRING:
-            case self::C_STRING:
-            case self::TEXT:
-            case self::ITEXT:
-                return $var;
-            case self::C_INT:
-                if (is_string($var) && ctype_digit($var)) {
-                    $var = (int)$var;
-                }
-                return $var;
-            case self::C_FLOAT:
-                if ((is_string($var) && is_numeric($var)) || is_int($var)) {
-                    $var = (float)$var;
-                }
-                return $var;
-            case self::C_BOOL:
-                if (is_int($var) && ($var === 0 || $var === 1)) {
-                    $var = (bool)$var;
-                } elseif (is_string($var)) {
-                    if ($var == 'on' || $var == 'true' || $var == '1') {
-                        $var = true;
-                    } elseif ($var == 'off' || $var == 'false' || $var == '0') {
-                        $var = false;
-                    } else {
-                        throw new HTMLPurifier_VarParserException("Unrecognized value '$var' for $type");
-                    }
-                }
-                return $var;
-            case self::ALIST:
-            case self::HASH:
-            case self::LOOKUP:
-                if (is_string($var)) {
-                    // special case: technically, this is an array with
-                    // a single empty string item, but having an empty
-                    // array is more intuitive
-                    if ($var == '') {
-                        return array();
-                    }
-                    if (strpos($var, "\n") === false && strpos($var, "\r") === false) {
-                        // simplistic string to array method that only works
-                        // for simple lists of tag names or alphanumeric characters
-                        $var = explode(',', $var);
-                    } else {
-                        $var = preg_split('/(,|[\n\r]+)/', $var);
-                    }
-                    // remove spaces
-                    foreach ($var as $i => $j) {
-                        $var[$i] = trim($j);
-                    }
-                    if ($type === self::HASH) {
-                        // key:value,key2:value2
-                        $nvar = array();
-                        foreach ($var as $keypair) {
-                            $c = explode(':', $keypair, 2);
-                            if (!isset($c[1])) {
-                                continue;
-                            }
-                            $nvar[trim($c[0])] = trim($c[1]);
-                        }
-                        $var = $nvar;
-                    }
-                }
-                if (!is_array($var)) {
-                    break;
-                }
-                $keys = array_keys($var);
-                if ($keys === array_keys($keys)) {
-                    if ($type == self::ALIST) {
-                        return $var;
-                    } elseif ($type == self::LOOKUP) {
-                        $new = array();
-                        foreach ($var as $key) {
-                            $new[$key] = true;
-                        }
-                        return $new;
-                    } else {
-                        break;
-                    }
-                }
-                if ($type === self::ALIST) {
-                    trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING);
-                    return array_values($var);
-                }
-                if ($type === self::LOOKUP) {
-                    foreach ($var as $key => $value) {
-                        if ($value !== true) {
-                            trigger_error(
-                                "Lookup array has non-true value at key '$key'; " .
-                                "maybe your input array was not indexed numerically",
-                                E_USER_WARNING
-                            );
-                        }
-                        $var[$key] = true;
-                    }
-                }
-                return $var;
-            default:
-                $this->errorInconsistent(__CLASS__, $type);
-        }
-        $this->errorGeneric($var, $type);
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php
deleted file mode 100644
index f11c318..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * This variable parser uses PHP's internal code engine. Because it does
- * this, it can represent all inputs; however, it is dangerous and cannot
- * be used by users.
- */
-class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser
-{
-
-    /**
-     * @param mixed $var
-     * @param int $type
-     * @param bool $allow_null
-     * @return null|string
-     */
-    protected function parseImplementation($var, $type, $allow_null)
-    {
-        return $this->evalExpression($var);
-    }
-
-    /**
-     * @param string $expr
-     * @return mixed
-     * @throws HTMLPurifier_VarParserException
-     */
-    protected function evalExpression($expr)
-    {
-        $var = null;
-        $result = eval("\$var = $expr;");
-        if ($result === false) {
-            throw new HTMLPurifier_VarParserException("Fatal error in evaluated code");
-        }
-        return $var;
-    }
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php
deleted file mode 100644
index 5df3414..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-/**
- * Exception type for HTMLPurifier_VarParser
- */
-class HTMLPurifier_VarParserException extends HTMLPurifier_Exception
-{
-
-}
-
-// vim: et sw=4 sts=4
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php
deleted file mode 100644
index 6e21ea0..0000000
--- a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php
+++ /dev/null
@@ -1,157 +0,0 @@
-<?php
-
-/**
- * A zipper is a purely-functional data structure which contains
- * a focus that can be efficiently manipulated.  It is known as
- * a "one-hole context".  This mutable variant implements a zipper
- * for a list as a pair of two arrays, laid out as follows:
- *
- *      Base list: 1 2 3 4 [ ] 6 7 8 9
- *      Front list: 1 2 3 4
- *      Back list: 9 8 7 6
- *
- * User is expected to keep track of the "current element" and properly
- * fill it back in as necessary.  (ToDo: Maybe it's more user friendly
- * to implicitly track the current element?)
- *
- * Nota bene: the current class gets confused if you try to store NULLs
- * in the list.
- */
-
-class HTMLPurifier_Zipper
-{
-    public $front, $back;
-
-    public function __construct($front, $back) {
-        $this->front = $front;
-        $this->back = $back;
-    }
-
-    /**
-     * Creates a zipper from an array, with a hole in the
-     * 0-index position.
-     * @param Array to zipper-ify.
-     * @return Tuple of zipper and element of first position.
-     */
-    static public function fromArray($array) {
-        $z = new self(array(), array_reverse($array));
-        $t = $z->delete(); // delete the "dummy hole"
-        return array($z, $t);
-    }
-
-    /**
-     * Convert zipper back into a normal array, optionally filling in
-     * the hole with a value. (Usually you should supply a $t, unless you
-     * are at the end of the array.)
-     */
-    public function toArray($t = NULL) {
-        $a = $this->front;
-        if ($t !== NULL) $a[] = $t;
-        for ($i = count($this->back)-1; $i >= 0; $i--) {
-            $a[] = $this->back[$i];
-        }
-        return $a;
-    }
-
-    /**
-     * Move hole to the next element.
-     * @param $t Element to fill hole with
-     * @return Original contents of new hole.
-     */
-    public function next($t) {
-        if ($t !== NULL) array_push($this->front, $t);
-        return empty($this->back) ? NULL : array_pop($this->back);
-    }
-
-    /**
-     * Iterated hole advancement.
-     * @param $t Element to fill hole with
-     * @param $i How many forward to advance hole
-     * @return Original contents of new hole, i away
-     */
-    public function advance($t, $n) {
-        for ($i = 0; $i < $n; $i++) {
-            $t = $this->next($t);
-        }
-        return $t;
-    }
-
-    /**
-     * Move hole to the previous element
-     * @param $t Element to fill hole with
-     * @return Original contents of new hole.
-     */
-    public function prev($t) {
-        if ($t !== NULL) array_push($this->back, $t);
-        return empty($this->front) ? NULL : array_pop($this->front);
-    }
-
-    /**
-     * Delete contents of current hole, shifting hole to
-     * next element.
-     * @return Original contents of new hole.
-     */
-    public function delete() {
-        return empty($this->back) ? NULL : array_pop($this->back);
-    }
-
-    /**
-     * Returns true if we are at the end of the list.
-     * @return bool
-     */
-    public function done() {
-        return empty($this->back);
-    }
-
-    /**
-     * Insert element before hole.
-     * @param Element to insert
-     */
-    public function insertBefore($t) {
-        if ($t !== NULL) array_push($this->front, $t);
-    }
-
-    /**
-     * Insert element after hole.
-     * @param Element to insert
-     */
-    public function insertAfter($t) {
-        if ($t !== NULL) array_push($this->back, $t);
-    }
-
-    /**
-     * Splice in multiple elements at hole.  Functional specification
-     * in terms of array_splice:
-     *
-     *      $arr1 = $arr;
-     *      $old1 = array_splice($arr1, $i, $delete, $replacement);
-     *
-     *      list($z, $t) = HTMLPurifier_Zipper::fromArray($arr);
-     *      $t = $z->advance($t, $i);
-     *      list($old2, $t) = $z->splice($t, $delete, $replacement);
-     *      $arr2 = $z->toArray($t);
-     *
-     *      assert($old1 === $old2);
-     *      assert($arr1 === $arr2);
-     *
-     * NB: the absolute index location after this operation is
-     * *unchanged!*
-     *
-     * @param Current contents of hole.
-     */
-    public function splice($t, $delete, $replacement) {
-        // delete
-        $old = array();
-        $r = $t;
-        for ($i = $delete; $i > 0; $i--) {
-            $old[] = $r;
-            $r = $this->delete();
-        }
-        // insert
-        for ($i = count($replacement)-1; $i >= 0; $i--) {
-            $this->insertAfter($r);
-            $r = $replacement[$i];
-        }
-        return array($old, $r);
-    }
-}
diff --git a/vendor/guzzlehttp/guzzle/CHANGELOG.md b/vendor/guzzlehttp/guzzle/CHANGELOG.md
index 12949ba..e0b6216 100644
--- a/vendor/guzzlehttp/guzzle/CHANGELOG.md
+++ b/vendor/guzzlehttp/guzzle/CHANGELOG.md
@@ -2,6 +2,106 @@
 
 Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version.
 
+
+## 7.9.2 - 2024-07-24
+
+### Fixed
+
+- Adjusted handler selection to use cURL if its version is 7.21.2 or higher, rather than 7.34.0
+
+
+## 7.9.1 - 2024-07-19
+
+### Fixed
+
+- Fix TLS 1.3 check for HTTP/2 requests
+
+
+## 7.9.0 - 2024-07-18
+
+### Changed
+
+- Improve protocol version checks to provide feedback around unsupported protocols
+- Only select the cURL handler by default if 7.34.0 or higher is linked
+- Improved `CurlMultiHandler` to avoid busy wait if possible
+- Dropped support for EOL `guzzlehttp/psr7` v1
+- Improved URI user info redaction in errors
+
+## 7.8.2 - 2024-07-18
+
+### Added
+
+- Support for PHP 8.4
+
+
+## 7.8.1 - 2023-12-03
+
+### Changed
+
+- Updated links in docs to their canonical versions
+- Replaced `call_user_func*` with native calls
+
+
+## 7.8.0 - 2023-08-27
+
+### Added
+
+- Support for PHP 8.3
+- Added automatic closing of handles on `CurlFactory` object destruction
+
+
+## 7.7.1 - 2023-08-27
+
+### Changed
+
+- Remove the need for `AllowDynamicProperties` in `CurlMultiHandler`
+
+
+## 7.7.0 - 2023-05-21
+
+### Added
+
+- Support `guzzlehttp/promises` v2
+
+
+## 7.6.1 - 2023-05-15
+
+### Fixed
+
+- Fix `SetCookie::fromString` MaxAge deprecation warning and skip invalid MaxAge values
+
+
+## 7.6.0 - 2023-05-14
+
+### Added
+
+- Support for setting the minimum TLS version in a unified way
+- Apply on request the version set in options parameters
+
+
+## 7.5.2 - 2023-05-14
+
+### Fixed
+
+- Fixed set cookie constructor validation
+- Fixed handling of files with `'0'` body
+
+### Changed
+
+- Corrected docs and default connect timeout value to 300 seconds
+
+
+## 7.5.1 - 2023-04-17
+
+### Fixed
+
+- Fixed `NO_PROXY` settings so that setting the `proxy` option to `no` overrides the env variable
+
+### Changed
+
+- Adjusted `guzzlehttp/psr7` version constraint to `^1.9.1 || ^2.4.5`
+
+
 ## 7.5.0 - 2022-08-28
 
 ### Added
@@ -9,20 +109,30 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 - Support PHP 8.2
 - Add request to delay closure params
 
+
 ## 7.4.5 - 2022-06-20
 
+### Fixed
+
 * Fix change in port should be considered a change in origin
 * Fix `CURLOPT_HTTPAUTH` option not cleared on change of origin
 
+
 ## 7.4.4 - 2022-06-09
 
+### Fixed
+
 * Fix failure to strip Authorization header on HTTP downgrade
 * Fix failure to strip the Cookie header on change in host or HTTP downgrade
 
+
 ## 7.4.3 - 2022-05-25
 
+### Fixed
+
 * Fix cross-domain cookie leakage
 
+
 ## 7.4.2 - 2022-03-20
 
 ### Fixed
@@ -31,6 +141,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 - Reject non-HTTP schemes in StreamHandler
 - Set a default ssl.peer_name context in StreamHandler to allow `force_ip_resolve`
 
+
 ## 7.4.1 - 2021-12-06
 
 ### Changed
@@ -42,6 +153,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 
 - Only close curl handle if it's done [#2950](https://github.com/guzzle/guzzle/pull/2950)
 
+
 ## 7.4.0 - 2021-10-18
 
 ### Added
@@ -59,6 +171,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 
 - Be more strict with types [#2914](https://github.com/guzzle/guzzle/pull/2914), [#2917](https://github.com/guzzle/guzzle/pull/2917), [#2919](https://github.com/guzzle/guzzle/pull/2919), [#2945](https://github.com/guzzle/guzzle/pull/2945)
 
+
 ## 7.3.0 - 2021-03-23
 
 ### Added
@@ -71,6 +184,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 
 - Handle exceptions on invalid header consistently between PHP versions and handlers [#2872](https://github.com/guzzle/guzzle/pull/2872)
 
+
 ## 7.2.0 - 2020-10-10
 
 ### Added
@@ -93,6 +207,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 
 - Using environment variable GUZZLE_CURL_SELECT_TIMEOUT [#2786](https://github.com/guzzle/guzzle/pull/2786)
 
+
 ## 7.1.1 - 2020-09-30
 
 ### Fixed
@@ -104,6 +219,7 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 - We dont connect curl `sink` on HEAD requests.
 - Removed some PHP 5 workarounds
 
+
 ## 7.1.0 - 2020-09-22
 
 ### Added
@@ -126,14 +242,17 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version
 - `Utils::defaultCaBundle()`
 - `CurlFactory::LOW_CURL_VERSION_NUMBER`
 
+
 ## 7.0.1 - 2020-06-27
 
 * Fix multiply defined functions fatal error [#2699](https://github.com/guzzle/guzzle/pull/2699)
 
+
 ## 7.0.0 - 2020-06-27
 
 No changes since 7.0.0-rc1.
 
+
 ## 7.0.0-rc1 - 2020-06-15
 
 ### Changed
@@ -141,6 +260,7 @@ No changes since 7.0.0-rc1.
 * Use error level for logging errors in Middleware [#2629](https://github.com/guzzle/guzzle/pull/2629)
 * Disabled IDN support by default and require ext-intl to use it [#2675](https://github.com/guzzle/guzzle/pull/2675)
 
+
 ## 7.0.0-beta2 - 2020-05-25
 
 ### Added
@@ -166,6 +286,7 @@ No changes since 7.0.0-rc1.
 
 * Pool option `pool_size` [#2528](https://github.com/guzzle/guzzle/pull/2528)
 
+
 ## 7.0.0-beta1 - 2019-12-30
 
 The diff might look very big but 95% of Guzzle users will be able to upgrade without modification.
@@ -199,15 +320,18 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * `uri_template()` and `UriTemplate` [#2440](https://github.com/guzzle/guzzle/pull/2440)
 * Request options `save_to` and `exceptions` [#2464](https://github.com/guzzle/guzzle/pull/2464)
 
+
 ## 6.5.2 - 2019-12-23
 
 * idn_to_ascii() fix for old PHP versions [#2489](https://github.com/guzzle/guzzle/pull/2489)
 
+
 ## 6.5.1 - 2019-12-21
 
 * Better defaults for PHP installations with old ICU lib [#2454](https://github.com/guzzle/guzzle/pull/2454)
 * IDN support for redirects [#2424](https://github.com/guzzle/guzzle/pull/2424)
 
+
 ## 6.5.0 - 2019-12-07
 
 * Improvement: Added support for reset internal queue in MockHandler. [#2143](https://github.com/guzzle/guzzle/pull/2143)
@@ -217,11 +341,13 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Fix: Prevent undefined offset when using array for ssl_key options. [#2348](https://github.com/guzzle/guzzle/pull/2348)
 * Deprecated `ClientInterface::VERSION`
 
+
 ## 6.4.1 - 2019-10-23
 
 * No `guzzle.phar` was created in 6.4.0 due expired API token. This release will fix that
 * Added `parent::__construct()` to `FileCookieJar` and `SessionCookieJar`
 
+
 ## 6.4.0 - 2019-10-23
 
 * Improvement: Improved error messages when using curl < 7.21.2 [#2108](https://github.com/guzzle/guzzle/pull/2108)
@@ -234,6 +360,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Fix: Prevent concurrent writes to file when saving `CookieJar` [#2335](https://github.com/guzzle/guzzle/pull/2335)
 * Improvement: Update `MockHandler` so we can test transfer time [#2362](https://github.com/guzzle/guzzle/pull/2362)
 
+
 ## 6.3.3 - 2018-04-22
 
 * Fix: Default headers when decode_content is specified
@@ -275,13 +402,14 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684)
 * Improvement:  	Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827)
 
-
 + Minor code cleanups, documentation fixes and clarifications.
 
+
 ## 6.2.3 - 2017-02-28
 
 * Fix deprecations with guzzle/psr7 version 1.4
 
+
 ## 6.2.2 - 2016-10-08
 
 * Allow to pass nullable Response to delay callable
@@ -289,6 +417,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Fix drain case where content-length is the literal string zero
 * Obfuscate in-URL credentials in exceptions
 
+
 ## 6.2.1 - 2016-07-18
 
 * Address HTTP_PROXY security vulnerability, CVE-2016-5385:
@@ -299,6 +428,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
   a server does not honor `Connection: close`.
 * Ignore URI fragment when sending requests.
 
+
 ## 6.2.0 - 2016-03-21
 
 * Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`.
@@ -318,6 +448,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: provide an empty string to `http_build_query` for HHVM workaround.
   https://github.com/guzzle/guzzle/pull/1367
 
+
 ## 6.1.1 - 2015-11-22
 
 * Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler
@@ -333,6 +464,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: fixed regression where MockHandler was not using `sink`.
   https://github.com/guzzle/guzzle/pull/1292
 
+
 ## 6.1.0 - 2015-09-08
 
 * Feature: Added the `on_stats` request option to provide access to transfer
@@ -367,6 +499,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
   https://github.com/guzzle/guzzle/pull/1189
 
+
 ## 6.0.2 - 2015-07-04
 
 * Fixed a memory leak in the curl handlers in which references to callbacks
@@ -384,6 +517,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Functions are now conditionally required using an additional level of
   indirection to help with global Composer installations.
 
+
 ## 6.0.1 - 2015-05-27
 
 * Fixed a bug with serializing the `query` request option where the `&`
@@ -392,6 +526,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
   use `form_params` or `multipart` instead.
 * Various doc fixes.
 
+
 ## 6.0.0 - 2015-05-26
 
 * See the UPGRADING.md document for more information.
@@ -416,6 +551,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * `$maxHandles` has been removed from CurlMultiHandler.
 * `MultipartPostBody` is now part of the `guzzlehttp/psr7` package.
 
+
 ## 5.3.0 - 2015-05-19
 
 * Mock now supports `save_to`
@@ -426,6 +562,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated.
 * URL scheme is now always lowercased.
 
+
 ## 6.0.0-beta.1
 
 * Requires PHP >= 5.5
@@ -478,6 +615,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * `GuzzleHttp\QueryParser` has been replaced with the
   `GuzzleHttp\Psr7\parse_query`.
 
+
 ## 5.2.0 - 2015-01-27
 
 * Added `AppliesHeadersInterface` to make applying headers to a request based
@@ -488,6 +626,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
   RingBridge.
 * Added a guard in the Pool class to not use recursion for request retries.
 
+
 ## 5.1.0 - 2014-12-19
 
 * Pool class no longer uses recursion when a request is intercepted.
@@ -508,6 +647,7 @@ Please see [the upgrade document](UPGRADING.md) that describes all BC breaking c
 * Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
   specific exceptions if necessary.
 
+
 ## 5.0.3 - 2014-11-03
 
 This change updates query strings so that they are treated as un-encoded values
@@ -522,6 +662,7 @@ string that should not be parsed or encoded (unless a call to getQuery() is
 subsequently made, forcing the query-string to be converted into a Query
 object).
 
+
 ## 5.0.2 - 2014-10-30
 
 * Added a trailing `\r\n` to multipart/form-data payloads. See
@@ -541,7 +682,9 @@ object).
   * Note: This has been changed in 5.0.3 to now encode query string values by
     default unless the `rawString` argument is provided when setting the query
     string on a URL: Now allowing many more characters to be present in the
-    query string without being percent encoded. See https://tools.ietf.org/html/rfc3986#appendix-A
+    query string without being percent encoded. See
+    https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
+
 
 ## 5.0.1 - 2014-10-16
 
@@ -554,6 +697,7 @@ Bugfix release.
 * Fixed an issue where transfer statistics were not being populated in the
   RingBridge. https://github.com/guzzle/guzzle/issues/866
 
+
 ## 5.0.0 - 2014-10-12
 
 Adding support for non-blocking responses and some minor API cleanup.
@@ -635,6 +779,7 @@ interfaces.
       argument. They now accept an associative array of options, including the
       "size" key and "metadata" key which can be used to provide custom metadata.
 
+
 ## 4.2.2 - 2014-09-08
 
 * Fixed a memory leak in the CurlAdapter when reusing cURL handles.
@@ -1077,7 +1222,7 @@ interfaces.
 
 ## 3.4.0 - 2013-04-11
 
-* Bug fix: URLs are now resolved correctly based on https://tools.ietf.org/html/rfc3986#section-5.2. #289
+* Bug fix: URLs are now resolved correctly based on https://datatracker.ietf.org/doc/html/rfc3986#section-5.2. #289
 * Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
 * Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
 * Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
diff --git a/vendor/guzzlehttp/guzzle/README.md b/vendor/guzzlehttp/guzzle/README.md
index f287fa9..cdaebee 100644
--- a/vendor/guzzlehttp/guzzle/README.md
+++ b/vendor/guzzlehttp/guzzle/README.md
@@ -3,7 +3,7 @@
 # Guzzle, PHP HTTP client
 
 [![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
-[![Build Status](https://img.shields.io/github/workflow/status/guzzle/guzzle/CI?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
+[![Build Status](https://img.shields.io/github/actions/workflow/status/guzzle/guzzle/ci.yml?label=ci%20build&style=flat-square)](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
 [![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
 
 Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
@@ -60,13 +60,13 @@ composer require guzzlehttp/guzzle
 
 ## Version Guidance
 
-| Version | Status         | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version  |
-|---------|----------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
-| 3.x     | EOL            | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >=5.3.3,<7.0 |
-| 4.x     | EOL            | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >=5.4,<7.0   |
-| 5.x     | EOL            | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >=5.4,<7.4   |
-| 6.x     | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >=5.5,<8.0   |
-| 7.x     | Latest         | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes   | >=7.2.5,<8.2 |
+| Version | Status              | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version  |
+|---------|---------------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
+| 3.x     | EOL (2016-10-31)    | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >=5.3.3,<7.0 |
+| 4.x     | EOL (2016-10-31)    | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >=5.4,<7.0   |
+| 5.x     | EOL (2019-10-31)    | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >=5.4,<7.4   |
+| 6.x     | EOL (2023-10-31)    | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >=5.5,<8.0   |
+| 7.x     | Latest              | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes   | >=7.2.5,<8.5 |
 
 [guzzle-3-repo]: https://github.com/guzzle/guzzle3
 [guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
diff --git a/vendor/guzzlehttp/guzzle/UPGRADING.md b/vendor/guzzlehttp/guzzle/UPGRADING.md
index 45417a7..4efbb59 100644
--- a/vendor/guzzlehttp/guzzle/UPGRADING.md
+++ b/vendor/guzzlehttp/guzzle/UPGRADING.md
@@ -27,7 +27,7 @@ Please make sure:
 - Function `GuzzleHttp\Exception\RequestException::getResponseBodySummary` is removed.
   Use `\GuzzleHttp\Psr7\get_message_body_summary` as an alternative.
 - Function `GuzzleHttp\Cookie\CookieJar::getCookieValue` is removed.
-- Request option `exception` is removed. Please use `http_errors`.
+- Request option `exceptions` is removed. Please use `http_errors`.
 - Request option `save_to` is removed. Please use `sink`.
 - Pool option `pool_size` is removed. Please use `concurrency`.
 - We now look for environment variables in the `$_SERVER` super global, due to thread safety issues with `getenv`. We continue to fallback to `getenv` in CLI environments, for maximum compatibility.
@@ -189,11 +189,11 @@ $client = new GuzzleHttp\Client(['handler' => $handler]);
 
 ## POST Requests
 
-This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
+This version added the [`form_params`](https://docs.guzzlephp.org/en/latest/request-options.html#form_params)
 and `multipart` request options. `form_params` is an associative array of
 strings or array of strings and is used to serialize an
 `application/x-www-form-urlencoded` POST request. The
-[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
+[`multipart`](https://docs.guzzlephp.org/en/latest/request-options.html#multipart)
 option is now used to send a multipart/form-data POST request.
 
 `GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
@@ -209,7 +209,7 @@ The `base_url` option has been renamed to `base_uri`.
 
 ## Rewritten Adapter Layer
 
-Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
+Guzzle now uses [RingPHP](https://ringphp.readthedocs.org/en/latest) to send
 HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
 is still supported, but it has now been renamed to `handler`. Instead of
 passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
@@ -575,7 +575,7 @@ You can intercept a request and inject a response using the `intercept()` event
 of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
 `GuzzleHttp\Event\ErrorEvent` event.
 
-See: http://docs.guzzlephp.org/en/latest/events.html
+See: https://docs.guzzlephp.org/en/latest/events.html
 
 ## Inflection
 
@@ -668,9 +668,9 @@ in separate repositories:
 
 The service description layer of Guzzle has moved into two separate packages:
 
-- http://github.com/guzzle/command Provides a high level abstraction over web
+- https://github.com/guzzle/command Provides a high level abstraction over web
   services by representing web service operations using commands.
-- http://github.com/guzzle/guzzle-services Provides an implementation of
+- https://github.com/guzzle/guzzle-services Provides an implementation of
   guzzle/command that provides request serialization and response parsing using
   Guzzle service descriptions.
 
@@ -870,7 +870,7 @@ HeaderInterface (e.g. toArray(), getAll(), etc.).
 3.3 to 3.4
 ----------
 
-Base URLs of a client now follow the rules of https://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
+Base URLs of a client now follow the rules of https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.2 when merging URLs.
 
 3.2 to 3.3
 ----------
diff --git a/vendor/guzzlehttp/guzzle/composer.json b/vendor/guzzlehttp/guzzle/composer.json
index f369ce6..cbede14 100644
--- a/vendor/guzzlehttp/guzzle/composer.json
+++ b/vendor/guzzlehttp/guzzle/composer.json
@@ -50,11 +50,39 @@
             "homepage": "https://github.com/Tobion"
         }
     ],
+    "repositories": [
+        {
+            "type": "package",
+            "package": {
+                "name": "guzzle/client-integration-tests",
+                "version": "v3.0.2",
+                "dist": {
+                    "url": "https://codeload.github.com/guzzle/client-integration-tests/zip/2c025848417c1135031fdf9c728ee53d0a7ceaee",
+                    "type": "zip"
+                },
+                "require": {
+                    "php": "^7.2.5 || ^8.0",
+                    "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.11",
+                    "php-http/message": "^1.0 || ^2.0",
+                    "guzzlehttp/psr7": "^1.7 || ^2.0",
+                    "th3n3rd/cartesian-product": "^0.3"
+                },
+                "autoload": {
+                    "psr-4": {
+                        "Http\\Client\\Tests\\": "src/"
+                    }
+                },
+                "bin": [
+                    "bin/http_test_server"
+                ]
+            }
+        }
+    ],
     "require": {
         "php": "^7.2.5 || ^8.0",
         "ext-json": "*",
-        "guzzlehttp/promises": "^1.5",
-        "guzzlehttp/psr7": "^1.9 || ^2.4",
+        "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
+        "guzzlehttp/psr7": "^2.7.0",
         "psr/http-client": "^1.0",
         "symfony/deprecation-contracts": "^2.2 || ^3.0"
     },
@@ -63,9 +91,10 @@
     },
     "require-dev": {
         "ext-curl": "*",
-        "bamarni/composer-bin-plugin": "^1.8.1",
-        "php-http/client-integration-tests": "^3.0",
-        "phpunit/phpunit": "^8.5.29 || ^9.5.23",
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "guzzle/client-integration-tests": "3.0.2",
+        "php-http/message-factory": "^1.1",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20",
         "psr/log": "^1.1 || ^2.0 || ^3.0"
     },
     "suggest": {
@@ -84,9 +113,6 @@
         "bamarni-bin": {
             "bin-links": true,
             "forward-command": false
-        },
-        "branch-alias": {
-            "dev-master": "7.5-dev"
         }
     },
     "autoload": {
diff --git a/vendor/guzzlehttp/guzzle/src/BodySummarizer.php b/vendor/guzzlehttp/guzzle/src/BodySummarizer.php
index 6eca94e..761506d 100644
--- a/vendor/guzzlehttp/guzzle/src/BodySummarizer.php
+++ b/vendor/guzzlehttp/guzzle/src/BodySummarizer.php
@@ -11,7 +11,7 @@ final class BodySummarizer implements BodySummarizerInterface
      */
     private $truncateAt;
 
-    public function __construct(int $truncateAt = null)
+    public function __construct(?int $truncateAt = null)
     {
         $this->truncateAt = $truncateAt;
     }
@@ -22,7 +22,7 @@ final class BodySummarizer implements BodySummarizerInterface
     public function summarize(MessageInterface $message): ?string
     {
         return $this->truncateAt === null
-            ? \GuzzleHttp\Psr7\Message::bodySummary($message)
-            : \GuzzleHttp\Psr7\Message::bodySummary($message, $this->truncateAt);
+            ? Psr7\Message::bodySummary($message)
+            : Psr7\Message::bodySummary($message, $this->truncateAt);
     }
 }
diff --git a/vendor/guzzlehttp/guzzle/src/Client.php b/vendor/guzzlehttp/guzzle/src/Client.php
index 58f1d89..c78919a 100644
--- a/vendor/guzzlehttp/guzzle/src/Client.php
+++ b/vendor/guzzlehttp/guzzle/src/Client.php
@@ -52,7 +52,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
      *
      * @param array $config Client configuration settings.
      *
-     * @see \GuzzleHttp\RequestOptions for a list of available request options.
+     * @see RequestOptions for a list of available request options.
      */
     public function __construct(array $config = [])
     {
@@ -120,13 +120,14 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     public function send(RequestInterface $request, array $options = []): ResponseInterface
     {
         $options[RequestOptions::SYNCHRONOUS] = true;
+
         return $this->sendAsync($request, $options)->wait();
     }
 
     /**
      * The HttpClient PSR (PSR-18) specify this method.
      *
-     * @inheritDoc
+     * {@inheritDoc}
      */
     public function sendRequest(RequestInterface $request): ResponseInterface
     {
@@ -184,6 +185,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     public function request(string $method, $uri = '', array $options = []): ResponseInterface
     {
         $options[RequestOptions::SYNCHRONOUS] = true;
+
         return $this->requestAsync($method, $uri, $options)->wait();
     }
 
@@ -228,11 +230,11 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     {
         $defaults = [
             'allow_redirects' => RedirectMiddleware::$defaultSettings,
-            'http_errors'     => true,
-            'decode_content'  => true,
-            'verify'          => true,
-            'cookies'         => false,
-            'idn_conversion'  => false,
+            'http_errors' => true,
+            'decode_content' => true,
+            'verify' => true,
+            'cookies' => false,
+            'idn_conversion' => false,
         ];
 
         // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
@@ -354,10 +356,10 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
         if (isset($options['form_params'])) {
             if (isset($options['multipart'])) {
                 throw new InvalidArgumentException('You cannot use '
-                    . 'form_params and multipart at the same time. Use the '
-                    . 'form_params option if you want to send application/'
-                    . 'x-www-form-urlencoded requests, and the multipart '
-                    . 'option to send multipart/form-data requests.');
+                    .'form_params and multipart at the same time. Use the '
+                    .'form_params option if you want to send application/'
+                    .'x-www-form-urlencoded requests, and the multipart '
+                    .'option to send multipart/form-data requests.');
             }
             $options['body'] = \http_build_query($options['form_params'], '', '&');
             unset($options['form_params']);
@@ -403,7 +405,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
                     // Ensure that we don't have the header in different case and set the new value.
                     $modify['set_headers'] = Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']);
                     $modify['set_headers']['Authorization'] = 'Basic '
-                        . \base64_encode("$value[0]:$value[1]");
+                        .\base64_encode("$value[0]:$value[1]");
                     break;
                 case 'digest':
                     // @todo: Do not rely on curl
@@ -437,13 +439,17 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
             }
         }
 
+        if (isset($options['version'])) {
+            $modify['version'] = $options['version'];
+        }
+
         $request = Psr7\Utils::modifyRequest($request, $modify);
         if ($request->getBody() instanceof Psr7\MultipartStream) {
             // Use a multipart/form-data POST if a Content-Type is not set.
             // Ensure that we don't have the header in different case and set the new value.
             $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
             $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
-                . $request->getBody()->getBoundary();
+                .$request->getBody()->getBoundary();
         }
 
         // Merge in conditional headers if they are not present.
@@ -469,9 +475,9 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface
     private function invalidBody(): InvalidArgumentException
     {
         return new InvalidArgumentException('Passing in the "body" request '
-            . 'option as an array to send a request is not supported. '
-            . 'Please use the "form_params" request option to send a '
-            . 'application/x-www-form-urlencoded request, or the "multipart" '
-            . 'request option to send a multipart/form-data request.');
+            .'option as an array to send a request is not supported. '
+            .'Please use the "form_params" request option to send a '
+            .'application/x-www-form-urlencoded request, or the "multipart" '
+            .'request option to send a multipart/form-data request.');
     }
 }
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
index 9985a98..b616cf2 100644
--- a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
@@ -50,10 +50,10 @@ class CookieJar implements CookieJarInterface
         $cookieJar = new self();
         foreach ($cookies as $name => $value) {
             $cookieJar->setCookie(new SetCookie([
-                'Domain'  => $domain,
-                'Name'    => $name,
-                'Value'   => $value,
-                'Discard' => true
+                'Domain' => $domain,
+                'Name' => $name,
+                'Value' => $value,
+                'Discard' => true,
             ]));
         }
 
@@ -96,9 +96,6 @@ class CookieJar implements CookieJarInterface
         return null;
     }
 
-    /**
-     * @inheritDoc
-     */
     public function toArray(): array
     {
         return \array_map(static function (SetCookie $cookie): array {
@@ -106,13 +103,11 @@ class CookieJar implements CookieJarInterface
         }, $this->getIterator()->getArrayCopy());
     }
 
-    /**
-     * @inheritDoc
-     */
     public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void
     {
         if (!$domain) {
             $this->cookies = [];
+
             return;
         } elseif (!$path) {
             $this->cookies = \array_filter(
@@ -125,25 +120,22 @@ class CookieJar implements CookieJarInterface
             $this->cookies = \array_filter(
                 $this->cookies,
                 static function (SetCookie $cookie) use ($path, $domain): bool {
-                    return !($cookie->matchesPath($path) &&
-                        $cookie->matchesDomain($domain));
+                    return !($cookie->matchesPath($path)
+                        && $cookie->matchesDomain($domain));
                 }
             );
         } else {
             $this->cookies = \array_filter(
                 $this->cookies,
                 static function (SetCookie $cookie) use ($path, $domain, $name) {
-                    return !($cookie->getName() == $name &&
-                        $cookie->matchesPath($path) &&
-                        $cookie->matchesDomain($domain));
+                    return !($cookie->getName() == $name
+                        && $cookie->matchesPath($path)
+                        && $cookie->matchesDomain($domain));
                 }
             );
         }
     }
 
-    /**
-     * @inheritDoc
-     */
     public function clearSessionCookies(): void
     {
         $this->cookies = \array_filter(
@@ -154,9 +146,6 @@ class CookieJar implements CookieJarInterface
         );
     }
 
-    /**
-     * @inheritDoc
-     */
     public function setCookie(SetCookie $cookie): bool
     {
         // If the name string is empty (but not 0), ignore the set-cookie
@@ -170,9 +159,10 @@ class CookieJar implements CookieJarInterface
         $result = $cookie->validate();
         if ($result !== true) {
             if ($this->strictMode) {
-                throw new \RuntimeException('Invalid cookie: ' . $result);
+                throw new \RuntimeException('Invalid cookie: '.$result);
             }
             $this->removeCookieIfEmpty($cookie);
+
             return false;
         }
 
@@ -180,9 +170,9 @@ class CookieJar implements CookieJarInterface
         foreach ($this->cookies as $i => $c) {
             // Two cookies are identical, when their path, and domain are
             // identical.
-            if ($c->getPath() != $cookie->getPath() ||
-                $c->getDomain() != $cookie->getDomain() ||
-                $c->getName() != $cookie->getName()
+            if ($c->getPath() != $cookie->getPath()
+                || $c->getDomain() != $cookie->getDomain()
+                || $c->getName() != $cookie->getName()
             ) {
                 continue;
             }
@@ -253,7 +243,7 @@ class CookieJar implements CookieJarInterface
     /**
      * Computes cookie path following RFC 6265 section 5.1.4
      *
-     * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
+     * @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
      */
     private function getCookiePathFromRequest(RequestInterface $request): string
     {
@@ -284,13 +274,13 @@ class CookieJar implements CookieJarInterface
         $path = $uri->getPath() ?: '/';
 
         foreach ($this->cookies as $cookie) {
-            if ($cookie->matchesPath($path) &&
-                $cookie->matchesDomain($host) &&
-                !$cookie->isExpired() &&
-                (!$cookie->getSecure() || $scheme === 'https')
+            if ($cookie->matchesPath($path)
+                && $cookie->matchesDomain($host)
+                && !$cookie->isExpired()
+                && (!$cookie->getSecure() || $scheme === 'https')
             ) {
-                $values[] = $cookie->getName() . '='
-                    . $cookie->getValue();
+                $values[] = $cookie->getName().'='
+                    .$cookie->getValue();
             }
         }
 
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
index 7df374b..93ada58 100644
--- a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
@@ -13,7 +13,8 @@ use Psr\Http\Message\ResponseInterface;
  * necessary. Subclasses are also responsible for storing and retrieving
  * cookies from a file, database, etc.
  *
- * @link https://docs.python.org/2/library/cookielib.html Inspiration
+ * @see https://docs.python.org/2/library/cookielib.html Inspiration
+ *
  * @extends \IteratorAggregate<SetCookie>
  */
 interface CookieJarInterface extends \Countable, \IteratorAggregate
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
index 5d51ca9..cb3e67c 100644
--- a/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
@@ -71,7 +71,7 @@ class SessionCookieJar extends CookieJar
                 $this->setCookie(new SetCookie($cookie));
             }
         } elseif (\strlen($data)) {
-            throw new \RuntimeException("Invalid cookie data");
+            throw new \RuntimeException('Invalid cookie data');
         }
     }
 }
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
index a613c77..c9806da 100644
--- a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
+++ b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
@@ -11,15 +11,15 @@ class SetCookie
      * @var array
      */
     private static $defaults = [
-        'Name'     => null,
-        'Value'    => null,
-        'Domain'   => null,
-        'Path'     => '/',
-        'Max-Age'  => null,
-        'Expires'  => null,
-        'Secure'   => false,
-        'Discard'  => false,
-        'HttpOnly' => false
+        'Name' => null,
+        'Value' => null,
+        'Domain' => null,
+        'Path' => '/',
+        'Max-Age' => null,
+        'Expires' => null,
+        'Secure' => false,
+        'Discard' => false,
+        'HttpOnly' => false,
     ];
 
     /**
@@ -58,7 +58,13 @@ class SetCookie
             } else {
                 foreach (\array_keys(self::$defaults) as $search) {
                     if (!\strcasecmp($search, $key)) {
-                        $data[$search] = $value;
+                        if ($search === 'Max-Age') {
+                            if (is_numeric($value)) {
+                                $data[$search] = (int) $value;
+                            }
+                        } else {
+                            $data[$search] = $value;
+                        }
                         continue 2;
                     }
                 }
@@ -74,13 +80,49 @@ class SetCookie
      */
     public function __construct(array $data = [])
     {
-        /** @var array|null $replaced will be null in case of replace error */
-        $replaced = \array_replace(self::$defaults, $data);
-        if ($replaced === null) {
-            throw new \InvalidArgumentException('Unable to replace the default values for the Cookie.');
+        $this->data = self::$defaults;
+
+        if (isset($data['Name'])) {
+            $this->setName($data['Name']);
+        }
+
+        if (isset($data['Value'])) {
+            $this->setValue($data['Value']);
+        }
+
+        if (isset($data['Domain'])) {
+            $this->setDomain($data['Domain']);
+        }
+
+        if (isset($data['Path'])) {
+            $this->setPath($data['Path']);
+        }
+
+        if (isset($data['Max-Age'])) {
+            $this->setMaxAge($data['Max-Age']);
+        }
+
+        if (isset($data['Expires'])) {
+            $this->setExpires($data['Expires']);
+        }
+
+        if (isset($data['Secure'])) {
+            $this->setSecure($data['Secure']);
+        }
+
+        if (isset($data['Discard'])) {
+            $this->setDiscard($data['Discard']);
+        }
+
+        if (isset($data['HttpOnly'])) {
+            $this->setHttpOnly($data['HttpOnly']);
+        }
+
+        // Set the remaining values that don't have extra validation logic
+        foreach (array_diff(array_keys($data), array_keys(self::$defaults)) as $key) {
+            $this->data[$key] = $data[$key];
         }
 
-        $this->data = $replaced;
         // Extract the Expires value and turn it into a UNIX timestamp if needed
         if (!$this->getExpires() && $this->getMaxAge()) {
             // Calculate the Expires date
@@ -92,13 +134,13 @@ class SetCookie
 
     public function __toString()
     {
-        $str = $this->data['Name'] . '=' . ($this->data['Value'] ?? '') . '; ';
+        $str = $this->data['Name'].'='.($this->data['Value'] ?? '').'; ';
         foreach ($this->data as $k => $v) {
             if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
                 if ($k === 'Expires') {
-                    $str .= 'Expires=' . \gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
+                    $str .= 'Expires='.\gmdate('D, d M Y H:i:s \G\M\T', $v).'; ';
                 } else {
-                    $str .= ($v === true ? $k : "{$k}={$v}") . '; ';
+                    $str .= ($v === true ? $k : "{$k}={$v}").'; ';
                 }
             }
         }
@@ -378,7 +420,7 @@ class SetCookie
         }
 
         // Remove the leading '.' as per spec in RFC 6265.
-        // https://tools.ietf.org/html/rfc6265#section-5.2.3
+        // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
         $cookieDomain = \ltrim(\strtolower($cookieDomain), '.');
 
         $domain = \strtolower($domain);
@@ -389,12 +431,12 @@ class SetCookie
         }
 
         // Matching the subdomain according to RFC 6265.
-        // https://tools.ietf.org/html/rfc6265#section-5.1.3
+        // https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
         if (\filter_var($domain, \FILTER_VALIDATE_IP)) {
             return false;
         }
 
-        return (bool) \preg_match('/\.' . \preg_quote($cookieDomain, '/') . '$/', $domain);
+        return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/', $domain);
     }
 
     /**
@@ -423,8 +465,8 @@ class SetCookie
             $name
         )) {
             return 'Cookie name must not contain invalid characters: ASCII '
-                . 'Control characters (0-31;127), space, tab and the '
-                . 'following characters: ()<>@,;:\"/?={}';
+                .'Control characters (0-31;127), space, tab and the '
+                .'following characters: ()<>@,;:\"/?={}';
         }
 
         // Value must not be null. 0 and empty string are valid. Empty strings
diff --git a/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
index a80956c..ba67ad4 100644
--- a/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
+++ b/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
@@ -14,7 +14,7 @@ class BadResponseException extends RequestException
         string $message,
         RequestInterface $request,
         ResponseInterface $response,
-        \Throwable $previous = null,
+        ?\Throwable $previous = null,
         array $handlerContext = []
     ) {
         parent::__construct($message, $request, $response, $previous, $handlerContext);
diff --git a/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php b/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php
index e1a3151..eab51ca 100644
--- a/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php
+++ b/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php
@@ -25,7 +25,7 @@ class ConnectException extends TransferException implements NetworkExceptionInte
     public function __construct(
         string $message,
         RequestInterface $request,
-        \Throwable $previous = null,
+        ?\Throwable $previous = null,
         array $handlerContext = []
     ) {
         parent::__construct($message, 0, $previous);
diff --git a/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php
index c2d0a9c..b42c88a 100644
--- a/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php
+++ b/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php
@@ -7,7 +7,6 @@ use GuzzleHttp\BodySummarizerInterface;
 use Psr\Http\Client\RequestExceptionInterface;
 use Psr\Http\Message\RequestInterface;
 use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\UriInterface;
 
 /**
  * HTTP Request exception
@@ -32,8 +31,8 @@ class RequestException extends TransferException implements RequestExceptionInte
     public function __construct(
         string $message,
         RequestInterface $request,
-        ResponseInterface $response = null,
-        \Throwable $previous = null,
+        ?ResponseInterface $response = null,
+        ?\Throwable $previous = null,
         array $handlerContext = []
     ) {
         // Set the code of the exception if the response is set and not future.
@@ -63,10 +62,10 @@ class RequestException extends TransferException implements RequestExceptionInte
      */
     public static function create(
         RequestInterface $request,
-        ResponseInterface $response = null,
-        \Throwable $previous = null,
+        ?ResponseInterface $response = null,
+        ?\Throwable $previous = null,
         array $handlerContext = [],
-        BodySummarizerInterface $bodySummarizer = null
+        ?BodySummarizerInterface $bodySummarizer = null
     ): self {
         if (!$response) {
             return new self(
@@ -90,8 +89,7 @@ class RequestException extends TransferException implements RequestExceptionInte
             $className = __CLASS__;
         }
 
-        $uri = $request->getUri();
-        $uri = static::obfuscateUri($uri);
+        $uri = \GuzzleHttp\Psr7\Utils::redactUserInfo($request->getUri());
 
         // Client Error: `GET /` resulted in a `404 Not Found` response:
         // <html> ... (truncated)
@@ -113,20 +111,6 @@ class RequestException extends TransferException implements RequestExceptionInte
         return new $className($message, $request, $response, $previous, $handlerContext);
     }
 
-    /**
-     * Obfuscates URI if there is a username and a password present
-     */
-    private static function obfuscateUri(UriInterface $uri): UriInterface
-    {
-        $userInfo = $uri->getUserInfo();
-
-        if (false !== ($pos = \strpos($userInfo, ':'))) {
-            return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***');
-        }
-
-        return $uri;
-    }
-
     /**
      * Get the request that caused the exception
      */
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
index 0c45089..fe36137 100644
--- a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
+++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
@@ -11,6 +11,7 @@ use GuzzleHttp\Psr7\LazyOpenStream;
 use GuzzleHttp\TransferStats;
 use GuzzleHttp\Utils;
 use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\UriInterface;
 
 /**
  * Creates curl resources from a request
@@ -46,12 +47,22 @@ class CurlFactory implements CurlFactoryInterface
 
     public function create(RequestInterface $request, array $options): EasyHandle
     {
+        $protocolVersion = $request->getProtocolVersion();
+
+        if ('2' === $protocolVersion || '2.0' === $protocolVersion) {
+            if (!self::supportsHttp2()) {
+                throw new ConnectException('HTTP/2 is supported by the cURL handler, however libcurl is built without HTTP/2 support.', $request);
+            }
+        } elseif ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
+            throw new ConnectException(sprintf('HTTP/%s is not supported by the cURL handler.', $protocolVersion), $request);
+        }
+
         if (isset($options['curl']['body_as_string'])) {
             $options['_body_as_string'] = $options['curl']['body_as_string'];
             unset($options['curl']['body_as_string']);
         }
 
-        $easy = new EasyHandle;
+        $easy = new EasyHandle();
         $easy->request = $request;
         $easy->options = $options;
         $conf = $this->getDefaultConf($easy);
@@ -72,6 +83,42 @@ class CurlFactory implements CurlFactoryInterface
         return $easy;
     }
 
+    private static function supportsHttp2(): bool
+    {
+        static $supportsHttp2 = null;
+
+        if (null === $supportsHttp2) {
+            $supportsHttp2 = self::supportsTls12()
+                && defined('CURL_VERSION_HTTP2')
+                && (\CURL_VERSION_HTTP2 & \curl_version()['features']);
+        }
+
+        return $supportsHttp2;
+    }
+
+    private static function supportsTls12(): bool
+    {
+        static $supportsTls12 = null;
+
+        if (null === $supportsTls12) {
+            $supportsTls12 = \CURL_SSLVERSION_TLSv1_2 & \curl_version()['features'];
+        }
+
+        return $supportsTls12;
+    }
+
+    private static function supportsTls13(): bool
+    {
+        static $supportsTls13 = null;
+
+        if (null === $supportsTls13) {
+            $supportsTls13 = defined('CURL_SSLVERSION_TLSv1_3')
+                && (\CURL_SSLVERSION_TLSv1_3 & \curl_version()['features']);
+        }
+
+        return $supportsTls13;
+    }
+
     public function release(EasyHandle $easy): void
     {
         $resource = $easy->handle;
@@ -147,7 +194,7 @@ class CurlFactory implements CurlFactoryInterface
             'error' => \curl_error($easy->handle),
             'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME),
         ] + \curl_getinfo($easy->handle);
-        $ctx[self::CURL_VERSION_STR] = \curl_version()['version'];
+        $ctx[self::CURL_VERSION_STR] = self::getCurlVersion();
         $factory->release($easy);
 
         // Retry when nothing is present or when curl failed to rewind.
@@ -158,14 +205,25 @@ class CurlFactory implements CurlFactoryInterface
         return self::createRejection($easy, $ctx);
     }
 
+    private static function getCurlVersion(): string
+    {
+        static $curlVersion = null;
+
+        if (null === $curlVersion) {
+            $curlVersion = \curl_version()['version'];
+        }
+
+        return $curlVersion;
+    }
+
     private static function createRejection(EasyHandle $easy, array $ctx): PromiseInterface
     {
         static $connectionErrors = [
-            \CURLE_OPERATION_TIMEOUTED  => true,
+            \CURLE_OPERATION_TIMEOUTED => true,
             \CURLE_COULDNT_RESOLVE_HOST => true,
-            \CURLE_COULDNT_CONNECT      => true,
-            \CURLE_SSL_CONNECT_ERROR    => true,
-            \CURLE_GOT_NOTHING          => true,
+            \CURLE_COULDNT_CONNECT => true,
+            \CURLE_SSL_CONNECT_ERROR => true,
+            \CURLE_GOT_NOTHING => true,
         ];
 
         if ($easy->createResponseException) {
@@ -194,15 +252,22 @@ class CurlFactory implements CurlFactoryInterface
             );
         }
 
+        $uri = $easy->request->getUri();
+
+        $sanitizedError = self::sanitizeCurlError($ctx['error'] ?? '', $uri);
+
         $message = \sprintf(
             'cURL error %s: %s (%s)',
             $ctx['errno'],
-            $ctx['error'],
+            $sanitizedError,
             'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'
         );
-        $uriString = (string) $easy->request->getUri();
-        if ($uriString !== '' && false === \strpos($ctx['error'], $uriString)) {
-            $message .= \sprintf(' for %s', $uriString);
+
+        if ('' !== $sanitizedError) {
+            $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($uri)->__toString();
+            if ($redactedUriString !== '' && false === \strpos($sanitizedError, $redactedUriString)) {
+                $message .= \sprintf(' for %s', $redactedUriString);
+            }
         }
 
         // Create a connection exception if it was a specific error code.
@@ -213,18 +278,36 @@ class CurlFactory implements CurlFactoryInterface
         return P\Create::rejectionFor($error);
     }
 
+    private static function sanitizeCurlError(string $error, UriInterface $uri): string
+    {
+        if ('' === $error) {
+            return $error;
+        }
+
+        $baseUri = $uri->withQuery('')->withFragment('');
+        $baseUriString = $baseUri->__toString();
+
+        if ('' === $baseUriString) {
+            return $error;
+        }
+
+        $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($baseUri)->__toString();
+
+        return str_replace($baseUriString, $redactedUriString, $error);
+    }
+
     /**
      * @return array<int|string, mixed>
      */
     private function getDefaultConf(EasyHandle $easy): array
     {
         $conf = [
-            '_headers'              => $easy->request->getHeaders(),
-            \CURLOPT_CUSTOMREQUEST  => $easy->request->getMethod(),
-            \CURLOPT_URL            => (string) $easy->request->getUri()->withFragment(''),
+            '_headers' => $easy->request->getHeaders(),
+            \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
+            \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
             \CURLOPT_RETURNTRANSFER => false,
-            \CURLOPT_HEADER         => false,
-            \CURLOPT_CONNECTTIMEOUT => 150,
+            \CURLOPT_HEADER => false,
+            \CURLOPT_CONNECTTIMEOUT => 300,
         ];
 
         if (\defined('CURLOPT_PROTOCOLS')) {
@@ -232,10 +315,11 @@ class CurlFactory implements CurlFactoryInterface
         }
 
         $version = $easy->request->getProtocolVersion();
-        if ($version == 1.1) {
-            $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
-        } elseif ($version == 2.0) {
+
+        if ('2' === $version || '2.0' === $version) {
             $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0;
+        } elseif ('1.1' === $version) {
+            $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1;
         } else {
             $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0;
         }
@@ -250,12 +334,13 @@ class CurlFactory implements CurlFactoryInterface
 
         if ($size === null || $size > 0) {
             $this->applyBody($easy->request, $easy->options, $conf);
+
             return;
         }
 
         $method = $easy->request->getMethod();
         if ($method === 'PUT' || $method === 'POST') {
-            // See https://tools.ietf.org/html/rfc7230#section-3.3.2
+            // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
             if (!$easy->request->hasHeader('Content-Length')) {
                 $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
             }
@@ -341,6 +426,7 @@ class CurlFactory implements CurlFactoryInterface
         foreach (\array_keys($options['_headers']) as $key) {
             if (!\strcasecmp($key, $name)) {
                 unset($options['_headers'][$key]);
+
                 return;
             }
         }
@@ -365,11 +451,11 @@ class CurlFactory implements CurlFactoryInterface
                     // If it's a directory or a link to a directory use CURLOPT_CAPATH.
                     // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
                     if (
-                        \is_dir($options['verify']) ||
-                        (
-                            \is_link($options['verify']) === true &&
-                            ($verifyLink = \readlink($options['verify'])) !== false &&
-                            \is_dir($verifyLink)
+                        \is_dir($options['verify'])
+                        || (
+                            \is_link($options['verify']) === true
+                            && ($verifyLink = \readlink($options['verify'])) !== false
+                            && \is_dir($verifyLink)
                         )
                     ) {
                         $conf[\CURLOPT_CAPATH] = $options['verify'];
@@ -388,8 +474,10 @@ class CurlFactory implements CurlFactoryInterface
                 // The empty string enables all available decoders and implicitly
                 // sets a matching 'Accept-Encoding' header.
                 $conf[\CURLOPT_ENCODING] = '';
-                // But as the user did not specify any acceptable encodings we need
-                // to overwrite this implicit header with an empty one.
+                // But as the user did not specify any encoding preference,
+                // let's leave it up to server by preventing curl from sending
+                // the header, which will be interpreted as 'Accept-Encoding: *'.
+                // https://www.rfc-editor.org/rfc/rfc9110#field.accept-encoding
                 $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
             }
         }
@@ -443,13 +531,53 @@ class CurlFactory implements CurlFactoryInterface
                 $scheme = $easy->request->getUri()->getScheme();
                 if (isset($options['proxy'][$scheme])) {
                     $host = $easy->request->getUri()->getHost();
-                    if (!isset($options['proxy']['no']) || !Utils::isHostInNoProxy($host, $options['proxy']['no'])) {
+                    if (isset($options['proxy']['no']) && Utils::isHostInNoProxy($host, $options['proxy']['no'])) {
+                        unset($conf[\CURLOPT_PROXY]);
+                    } else {
                         $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme];
                     }
                 }
             }
         }
 
+        if (isset($options['crypto_method'])) {
+            $protocolVersion = $easy->request->getProtocolVersion();
+
+            // If HTTP/2, upgrade TLS 1.0 and 1.1 to 1.2
+            if ('2' === $protocolVersion || '2.0' === $protocolVersion) {
+                if (
+                    \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']
+                    || \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']
+                    || \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']
+                ) {
+                    $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2;
+                } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) {
+                    if (!self::supportsTls13()) {
+                        throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL');
+                    }
+                    $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3;
+                } else {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+                }
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) {
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0;
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) {
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1;
+            } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) {
+                if (!self::supportsTls12()) {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL');
+                }
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2;
+            } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) {
+                if (!self::supportsTls13()) {
+                    throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL');
+                }
+                $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3;
+            } else {
+                throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+            }
+        }
+
         if (isset($options['cert'])) {
             $cert = $options['cert'];
             if (\is_array($cert)) {
@@ -459,8 +587,8 @@ class CurlFactory implements CurlFactoryInterface
             if (!\file_exists($cert)) {
                 throw new \InvalidArgumentException("SSL certificate not found: {$cert}");
             }
-            # OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files.
-            # see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
+            // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files.
+            // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
             $ext = pathinfo($cert, \PATHINFO_EXTENSION);
             if (preg_match('#^(der|p12)$#i', $ext)) {
                 $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext);
@@ -523,9 +651,10 @@ class CurlFactory implements CurlFactoryInterface
             }
         } catch (\RuntimeException $e) {
             $ctx['error'] = 'The connection unexpectedly failed without '
-                . 'providing an error. The request would have been retried, '
-                . 'but attempting to rewind the request body failed. '
-                . 'Exception: ' . $e;
+                .'providing an error. The request would have been retried, '
+                .'but attempting to rewind the request body failed. '
+                .'Exception: '.$e;
+
             return self::createRejection($easy, $ctx);
         }
 
@@ -534,14 +663,15 @@ class CurlFactory implements CurlFactoryInterface
             $easy->options['_curl_retries'] = 1;
         } elseif ($easy->options['_curl_retries'] == 2) {
             $ctx['error'] = 'The cURL request was retried 3 times '
-                . 'and did not succeed. The most likely reason for the failure '
-                . 'is that cURL was unable to rewind the body of the request '
-                . 'and subsequent retries resulted in the same error. Turn on '
-                . 'the debug option to see what went wrong. See '
-                . 'https://bugs.php.net/bug.php?id=47204 for more information.';
+                .'and did not succeed. The most likely reason for the failure '
+                .'is that cURL was unable to rewind the body of the request '
+                .'and subsequent retries resulted in the same error. Turn on '
+                .'the debug option to see what went wrong. See '
+                .'https://bugs.php.net/bug.php?id=47204 for more information.';
+
             return self::createRejection($easy, $ctx);
         } else {
-            $easy->options['_curl_retries']++;
+            ++$easy->options['_curl_retries'];
         }
 
         return $handler($easy->request, $easy->options);
@@ -571,6 +701,7 @@ class CurlFactory implements CurlFactoryInterface
                     $easy->createResponse();
                 } catch (\Exception $e) {
                     $easy->createResponseException = $e;
+
                     return -1;
                 }
                 if ($onHeaders !== null) {
@@ -580,6 +711,7 @@ class CurlFactory implements CurlFactoryInterface
                         // Associate the exception with the handle and trigger
                         // a curl header write error by returning 0.
                         $easy->onHeadersException = $e;
+
                         return -1;
                     }
                 }
@@ -589,7 +721,16 @@ class CurlFactory implements CurlFactoryInterface
             } else {
                 $easy->headers[] = $value;
             }
+
             return \strlen($h);
         };
     }
+
+    public function __destruct()
+    {
+        foreach ($this->handles as $id => $handle) {
+            \curl_close($handle);
+            unset($this->handles[$id]);
+        }
+    }
 }
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
index 4356d02..73a6abe 100644
--- a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
+++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
@@ -2,6 +2,7 @@
 
 namespace GuzzleHttp\Handler;
 
+use Closure;
 use GuzzleHttp\Promise as P;
 use GuzzleHttp\Promise\Promise;
 use GuzzleHttp\Promise\PromiseInterface;
@@ -15,11 +16,8 @@ use Psr\Http\Message\RequestInterface;
  * associative array of curl option constants mapping to values in the
  * **curl** key of the provided request options.
  *
- * @property resource|\CurlMultiHandle $_mh Internal use only. Lazy loaded multi-handle.
- *
  * @final
  */
-#[\AllowDynamicProperties]
 class CurlMultiHandler
 {
     /**
@@ -56,6 +54,9 @@ class CurlMultiHandler
      */
     private $options = [];
 
+    /** @var resource|\CurlMultiHandle */
+    private $_mh;
+
     /**
      * This handler accepts the following options:
      *
@@ -79,6 +80,10 @@ class CurlMultiHandler
         }
 
         $this->options = $options['options'] ?? [];
+
+        // unsetting the property forces the first access to go through
+        // __get().
+        unset($this->_mh);
     }
 
     /**
@@ -155,6 +160,9 @@ class CurlMultiHandler
             }
         }
 
+        // Run curl_multi_exec in the queue to enable other async tasks to run
+        P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue']));
+
         // Step through the task queue which may add additional requests.
         P\Utils::queue()->run();
 
@@ -164,11 +172,25 @@ class CurlMultiHandler
             \usleep(250);
         }
 
-        while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM);
+        while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
+            // Prevent busy looping for slow HTTP requests.
+            \curl_multi_select($this->_mh, $this->selectTimeout);
+        }
 
         $this->processMessages();
     }
 
+    /**
+     * Runs \curl_multi_exec() inside the event loop, to prevent busy looping
+     */
+    private function tickInQueue(): void
+    {
+        if (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) {
+            \curl_multi_select($this->_mh, 0);
+            P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue']));
+        }
+    }
+
     /**
      * Runs until all outstanding connections have completed.
      */
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
index 224344d..1bc39f4 100644
--- a/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
+++ b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
@@ -106,7 +106,7 @@ final class EasyHandle
      */
     public function __get($name)
     {
-        $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: ' . $name;
+        $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: '.$name;
         throw new \BadMethodCallException($msg);
     }
 }
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php b/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php
index a098884..5554b8f 100644
--- a/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php
+++ b/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php
@@ -14,9 +14,9 @@ final class HeaderProcessor
      *
      * @param string[] $headers
      *
-     * @throws \RuntimeException
-     *
      * @return array{0:string, 1:int, 2:?string, 3:array}
+     *
+     * @throws \RuntimeException
      */
     public static function parseHeaders(array $headers): array
     {
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php
index 79664e2..3ecd596 100644
--- a/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php
+++ b/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php
@@ -52,21 +52,21 @@ class MockHandler implements \Countable
      * @param callable|null $onFulfilled Callback to invoke when the return value is fulfilled.
      * @param callable|null $onRejected  Callback to invoke when the return value is rejected.
      */
-    public static function createWithMiddleware(array $queue = null, callable $onFulfilled = null, callable $onRejected = null): HandlerStack
+    public static function createWithMiddleware(?array $queue = null, ?callable $onFulfilled = null, ?callable $onRejected = null): HandlerStack
     {
         return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
     }
 
     /**
      * The passed in value must be an array of
-     * {@see \Psr\Http\Message\ResponseInterface} objects, Exceptions,
+     * {@see ResponseInterface} objects, Exceptions,
      * callables, or Promises.
      *
      * @param array<int, mixed>|null $queue       The parameters to be passed to the append function, as an indexed array.
      * @param callable|null          $onFulfilled Callback to invoke when the return value is fulfilled.
      * @param callable|null          $onRejected  Callback to invoke when the return value is rejected.
      */
-    public function __construct(array $queue = null, callable $onFulfilled = null, callable $onRejected = null)
+    public function __construct(?array $queue = null, ?callable $onFulfilled = null, ?callable $onRejected = null)
     {
         $this->onFulfilled = $onFulfilled;
         $this->onRejected = $onRejected;
@@ -138,6 +138,7 @@ class MockHandler implements \Countable
                 if ($this->onRejected) {
                     ($this->onRejected)($reason);
                 }
+
                 return P\Create::rejectionFor($reason);
             }
         );
@@ -159,7 +160,7 @@ class MockHandler implements \Countable
             ) {
                 $this->queue[] = $value;
             } else {
-                throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found ' . Utils::describeType($value));
+                throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found '.Utils::describeType($value));
             }
         }
     }
@@ -199,7 +200,7 @@ class MockHandler implements \Countable
     private function invokeStats(
         RequestInterface $request,
         array $options,
-        ResponseInterface $response = null,
+        ?ResponseInterface $response = null,
         $reason = null
     ): void {
         if (isset($options['on_stats'])) {
diff --git a/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
index 543f825..1d89a8f 100644
--- a/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
+++ b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
@@ -40,6 +40,12 @@ class StreamHandler
             \usleep($options['delay'] * 1000);
         }
 
+        $protocolVersion = $request->getProtocolVersion();
+
+        if ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
+            throw new ConnectException(sprintf('HTTP/%s is not supported by the stream handler.', $protocolVersion), $request);
+        }
+
         $startTime = isset($options['on_stats']) ? Utils::currentTime() : null;
 
         try {
@@ -67,7 +73,7 @@ class StreamHandler
             if (false !== \strpos($message, 'getaddrinfo') // DNS lookup failed
                 || false !== \strpos($message, 'Connection refused')
                 || false !== \strpos($message, "couldn't connect to host") // error on HHVM
-                || false !== \strpos($message, "connection attempt failed")
+                || false !== \strpos($message, 'connection attempt failed')
             ) {
                 $e = new ConnectException($e->getMessage(), $request, $e);
             } else {
@@ -83,8 +89,8 @@ class StreamHandler
         array $options,
         RequestInterface $request,
         ?float $startTime,
-        ResponseInterface $response = null,
-        \Throwable $error = null
+        ?ResponseInterface $response = null,
+        ?\Throwable $error = null
     ): void {
         if (isset($options['on_stats'])) {
             $stats = new TransferStats($request, $response, Utils::currentTime() - $startTime, $error, []);
@@ -231,9 +237,10 @@ class StreamHandler
         \set_error_handler(static function ($_, $msg, $file, $line) use (&$errors): bool {
             $errors[] = [
                 'message' => $msg,
-                'file'    => $file,
-                'line'    => $line
+                'file' => $file,
+                'line' => $line,
             ];
+
             return true;
         });
 
@@ -247,7 +254,7 @@ class StreamHandler
             $message = 'Error creating resource: ';
             foreach ($errors as $err) {
                 foreach ($err as $key => $value) {
-                    $message .= "[$key] $value" . \PHP_EOL;
+                    $message .= "[$key] $value".\PHP_EOL;
                 }
             }
             throw new \RuntimeException(\trim($message));
@@ -272,7 +279,7 @@ class StreamHandler
 
         // HTTP/1.1 streams using the PHP stream wrapper require a
         // Connection: close header
-        if ($request->getProtocolVersion() == '1.1'
+        if ($request->getProtocolVersion() === '1.1'
             && !$request->hasHeader('Connection')
         ) {
             $request = $request->withHeader('Connection', 'close');
@@ -350,6 +357,7 @@ class StreamHandler
                 if (false === $records || !isset($records[0]['ip'])) {
                     throw new ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
                 }
+
                 return $uri->withHost($records[0]['ip']);
             }
             if ('v6' === $options['force_ip_resolve']) {
@@ -357,7 +365,8 @@ class StreamHandler
                 if (false === $records || !isset($records[0]['ipv6'])) {
                     throw new ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
                 }
-                return $uri->withHost('[' . $records[0]['ipv6'] . ']');
+
+                return $uri->withHost('['.$records[0]['ipv6'].']');
             }
         }
 
@@ -375,11 +384,11 @@ class StreamHandler
 
         $context = [
             'http' => [
-                'method'           => $request->getMethod(),
-                'header'           => $headers,
+                'method' => $request->getMethod(),
+                'header' => $headers,
                 'protocol_version' => $request->getProtocolVersion(),
-                'ignore_errors'    => true,
-                'follow_location'  => 0,
+                'ignore_errors' => true,
+                'follow_location' => 0,
             ],
             'ssl' => [
                 'peer_name' => $request->getUri()->getHost(),
@@ -388,7 +397,7 @@ class StreamHandler
 
         $body = (string) $request->getBody();
 
-        if (!empty($body)) {
+        if ('' !== $body) {
             $context['http']['content'] = $body;
             // Prevent the HTTP handler from adding a Content-Type header.
             if (!$request->hasHeader('Content-Type')) {
@@ -472,6 +481,25 @@ class StreamHandler
         }
     }
 
+    /**
+     * @param mixed $value as passed via Request transfer options.
+     */
+    private function add_crypto_method(RequestInterface $request, array &$options, $value, array &$params): void
+    {
+        if (
+            $value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
+            || $value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
+            || $value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
+            || (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT)
+        ) {
+            $options['http']['crypto_method'] = $value;
+
+            return;
+        }
+
+        throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided');
+    }
+
     /**
      * @param mixed $value as passed via Request transfer options.
      */
@@ -542,27 +570,27 @@ class StreamHandler
         }
 
         static $map = [
-            \STREAM_NOTIFY_CONNECT       => 'CONNECT',
+            \STREAM_NOTIFY_CONNECT => 'CONNECT',
             \STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
-            \STREAM_NOTIFY_AUTH_RESULT   => 'AUTH_RESULT',
-            \STREAM_NOTIFY_MIME_TYPE_IS  => 'MIME_TYPE_IS',
-            \STREAM_NOTIFY_FILE_SIZE_IS  => 'FILE_SIZE_IS',
-            \STREAM_NOTIFY_REDIRECTED    => 'REDIRECTED',
-            \STREAM_NOTIFY_PROGRESS      => 'PROGRESS',
-            \STREAM_NOTIFY_FAILURE       => 'FAILURE',
-            \STREAM_NOTIFY_COMPLETED     => 'COMPLETED',
-            \STREAM_NOTIFY_RESOLVE       => 'RESOLVE',
+            \STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
+            \STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
+            \STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
+            \STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
+            \STREAM_NOTIFY_PROGRESS => 'PROGRESS',
+            \STREAM_NOTIFY_FAILURE => 'FAILURE',
+            \STREAM_NOTIFY_COMPLETED => 'COMPLETED',
+            \STREAM_NOTIFY_RESOLVE => 'RESOLVE',
         ];
         static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max'];
 
         $value = Utils::debugResource($value);
-        $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
+        $ident = $request->getMethod().' '.$request->getUri()->withFragment('');
         self::addNotification(
             $params,
             static function (int $code, ...$passed) use ($ident, $value, $map, $args): void {
                 \fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
                 foreach (\array_filter($passed) as $i => $v) {
-                    \fwrite($value, $args[$i] . ': "' . $v . '" ');
+                    \fwrite($value, $args[$i].': "'.$v.'" ');
                 }
                 \fwrite($value, "\n");
             }
@@ -577,7 +605,7 @@ class StreamHandler
         } else {
             $params['notification'] = self::callArray([
                 $params['notification'],
-                $notify
+                $notify,
             ]);
         }
     }
diff --git a/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/vendor/guzzlehttp/guzzle/src/HandlerStack.php
index e0a1d11..03f9a18 100644
--- a/vendor/guzzlehttp/guzzle/src/HandlerStack.php
+++ b/vendor/guzzlehttp/guzzle/src/HandlerStack.php
@@ -58,7 +58,7 @@ class HandlerStack
     /**
      * @param (callable(RequestInterface, array): PromiseInterface)|null $handler Underlying HTTP handler.
      */
-    public function __construct(callable $handler = null)
+    public function __construct(?callable $handler = null)
     {
         $this->handler = $handler;
     }
@@ -86,14 +86,14 @@ class HandlerStack
         $stack = [];
 
         if ($this->handler !== null) {
-            $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
+            $stack[] = '0) Handler: '.$this->debugCallable($this->handler);
         }
 
         $result = '';
         foreach (\array_reverse($this->stack) as $tuple) {
-            $depth++;
+            ++$depth;
             $str = "{$depth}) Name: '{$tuple[1]}', ";
-            $str .= "Function: " . $this->debugCallable($tuple[0]);
+            $str .= 'Function: '.$this->debugCallable($tuple[0]);
             $result = "> {$str}\n{$result}";
             $stack[] = $str;
         }
@@ -122,7 +122,7 @@ class HandlerStack
      */
     public function hasHandler(): bool
     {
-        return $this->handler !== null ;
+        return $this->handler !== null;
     }
 
     /**
@@ -266,10 +266,10 @@ class HandlerStack
         if (\is_array($fn)) {
             return \is_string($fn[0])
                 ? "callable({$fn[0]}::{$fn[1]})"
-                : "callable(['" . \get_class($fn[0]) . "', '{$fn[1]}'])";
+                : "callable(['".\get_class($fn[0])."', '{$fn[1]}'])";
         }
 
         /** @var object $fn */
-        return 'callable(' . \spl_object_hash($fn) . ')';
+        return 'callable('.\spl_object_hash($fn).')';
     }
 }
diff --git a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
index da49954..9b77eee 100644
--- a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
+++ b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
@@ -40,11 +40,11 @@ class MessageFormatter implements MessageFormatterInterface
     /**
      * Apache Common Log Format.
      *
-     * @link https://httpd.apache.org/docs/2.4/logs.html#common
+     * @see https://httpd.apache.org/docs/2.4/logs.html#common
      *
      * @var string
      */
-    public const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
+    public const CLF = '{hostname} {req_header_User-Agent} - [{date_common_log}] "{method} {target} HTTP/{version}" {code} {res_header_Content-Length}';
     public const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
     public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
 
@@ -90,9 +90,9 @@ class MessageFormatter implements MessageFormatterInterface
                         break;
                     case 'req_headers':
                         $result = \trim($request->getMethod()
-                                . ' ' . $request->getRequestTarget())
-                            . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
-                            . $this->headers($request);
+                                .' '.$request->getRequestTarget())
+                            .' HTTP/'.$request->getProtocolVersion()."\r\n"
+                            .$this->headers($request);
                         break;
                     case 'res_headers':
                         $result = $response ?
@@ -101,7 +101,7 @@ class MessageFormatter implements MessageFormatterInterface
                                 $response->getProtocolVersion(),
                                 $response->getStatusCode(),
                                 $response->getReasonPhrase()
-                            ) . "\r\n" . $this->headers($response)
+                            )."\r\n".$this->headers($response)
                             : 'NULL';
                         break;
                     case 'req_body':
@@ -177,6 +177,7 @@ class MessageFormatter implements MessageFormatterInterface
                 }
 
                 $cache[$matches[1]] = $result;
+
                 return $result;
             },
             $this->template
@@ -190,7 +191,7 @@ class MessageFormatter implements MessageFormatterInterface
     {
         $result = '';
         foreach ($message->getHeaders() as $name => $values) {
-            $result .= $name . ': ' . \implode(', ', $values) . "\r\n";
+            $result .= $name.': '.\implode(', ', $values)."\r\n";
         }
 
         return \trim($result);
diff --git a/vendor/guzzlehttp/guzzle/src/Middleware.php b/vendor/guzzlehttp/guzzle/src/Middleware.php
index 7035c77..6edbb3f 100644
--- a/vendor/guzzlehttp/guzzle/src/Middleware.php
+++ b/vendor/guzzlehttp/guzzle/src/Middleware.php
@@ -34,10 +34,12 @@ final class Middleware
                 }
                 $cookieJar = $options['cookies'];
                 $request = $cookieJar->withCookieHeader($request);
+
                 return $handler($request, $options)
                     ->then(
                         static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface {
                             $cookieJar->extractCookies($request, $response);
+
                             return $response;
                         }
                     );
@@ -53,13 +55,14 @@ final class Middleware
      *
      * @return callable(callable): callable Returns a function that accepts the next handler.
      */
-    public static function httpErrors(BodySummarizerInterface $bodySummarizer = null): callable
+    public static function httpErrors(?BodySummarizerInterface $bodySummarizer = null): callable
     {
         return static function (callable $handler) use ($bodySummarizer): callable {
             return static function ($request, array $options) use ($handler, $bodySummarizer) {
                 if (empty($options['http_errors'])) {
                     return $handler($request, $options);
                 }
+
                 return $handler($request, $options)->then(
                     static function (ResponseInterface $response) use ($request, $bodySummarizer) {
                         $code = $response->getStatusCode();
@@ -93,20 +96,22 @@ final class Middleware
                 return $handler($request, $options)->then(
                     static function ($value) use ($request, &$container, $options) {
                         $container[] = [
-                            'request'  => $request,
+                            'request' => $request,
                             'response' => $value,
-                            'error'    => null,
-                            'options'  => $options
+                            'error' => null,
+                            'options' => $options,
                         ];
+
                         return $value;
                     },
                     static function ($reason) use ($request, &$container, $options) {
                         $container[] = [
-                            'request'  => $request,
+                            'request' => $request,
                             'response' => null,
-                            'error'    => $reason,
-                            'options'  => $options
+                            'error' => $reason,
+                            'options' => $options,
                         ];
+
                         return P\Create::rejectionFor($reason);
                     }
                 );
@@ -127,7 +132,7 @@ final class Middleware
      *
      * @return callable Returns a function that accepts the next handler.
      */
-    public static function tap(callable $before = null, callable $after = null): callable
+    public static function tap(?callable $before = null, ?callable $after = null): callable
     {
         return static function (callable $handler) use ($before, $after): callable {
             return static function (RequestInterface $request, array $options) use ($handler, $before, $after) {
@@ -138,6 +143,7 @@ final class Middleware
                 if ($after) {
                     $after($request, $options, $response);
                 }
+
                 return $response;
             };
         };
@@ -170,7 +176,7 @@ final class Middleware
      *
      * @return callable Returns a function that accepts the next handler.
      */
-    public static function retry(callable $decider, callable $delay = null): callable
+    public static function retry(callable $decider, ?callable $delay = null): callable
     {
         return static function (callable $handler) use ($decider, $delay): RetryMiddleware {
             return new RetryMiddleware($decider, $handler, $delay);
@@ -202,12 +208,14 @@ final class Middleware
                     static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface {
                         $message = $formatter->format($request, $response);
                         $logger->log($logLevel, $message);
+
                         return $response;
                     },
                     static function ($reason) use ($logger, $request, $formatter): PromiseInterface {
                         $response = $reason instanceof RequestException ? $reason->getResponse() : null;
                         $message = $formatter->format($request, $response, P\Create::exceptionFor($reason));
                         $logger->error($message);
+
                         return P\Create::rejectionFor($reason);
                     }
                 );
diff --git a/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
index 7ca6283..7dde6c5 100644
--- a/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
+++ b/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
@@ -76,14 +76,15 @@ class PrepareBodyMiddleware
 
         $expect = $options['expect'] ?? null;
 
-        // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
-        if ($expect === false || $request->getProtocolVersion() < 1.1) {
+        // Return if disabled or using HTTP/1.0
+        if ($expect === false || $request->getProtocolVersion() === '1.0') {
             return;
         }
 
         // The expect header is unconditionally enabled
         if ($expect === true) {
             $modify['set_headers']['Expect'] = '100-Continue';
+
             return;
         }
 
diff --git a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
index f67d448..7aa21a6 100644
--- a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
+++ b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
@@ -27,10 +27,10 @@ class RedirectMiddleware
      * @var array
      */
     public static $defaultSettings = [
-        'max'             => 5,
-        'protocols'       => ['http', 'https'],
-        'strict'          => false,
-        'referer'         => false,
+        'max' => 5,
+        'protocols' => ['http', 'https'],
+        'strict' => false,
+        'referer' => false,
         'track_redirects' => false,
     ];
 
@@ -166,8 +166,8 @@ class RedirectMiddleware
         // not forcing RFC compliance, but rather emulating what all browsers
         // would do.
         $statusCode = $response->getStatusCode();
-        if ($statusCode == 303 ||
-            ($statusCode <= 302 && !$options['allow_redirects']['strict'])
+        if ($statusCode == 303
+            || ($statusCode <= 302 && !$options['allow_redirects']['strict'])
         ) {
             $safeMethods = ['GET', 'HEAD', 'OPTIONS'];
             $requestMethod = $request->getMethod();
diff --git a/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/vendor/guzzlehttp/guzzle/src/RequestOptions.php
index 20b31bc..84a3500 100644
--- a/vendor/guzzlehttp/guzzle/src/RequestOptions.php
+++ b/vendor/guzzlehttp/guzzle/src/RequestOptions.php
@@ -5,9 +5,7 @@ namespace GuzzleHttp;
 /**
  * This class contains a list of built-in Guzzle request options.
  *
- * More documentation for each option can be found at http://guzzlephp.org/.
- *
- * @link http://docs.guzzlephp.org/en/v6/request-options.html
+ * @see https://docs.guzzlephp.org/en/latest/request-options.html
  */
 final class RequestOptions
 {
@@ -63,17 +61,29 @@ final class RequestOptions
      * Specifies whether or not cookies are used in a request or what cookie
      * jar to use or what cookies to send. This option only works if your
      * handler has the `cookie` middleware. Valid values are `false` and
-     * an instance of {@see \GuzzleHttp\Cookie\CookieJarInterface}.
+     * an instance of {@see Cookie\CookieJarInterface}.
      */
     public const COOKIES = 'cookies';
 
     /**
      * connect_timeout: (float, default=0) Float describing the number of
      * seconds to wait while trying to connect to a server. Use 0 to wait
-     * indefinitely (the default behavior).
+     * 300 seconds (the default behavior).
      */
     public const CONNECT_TIMEOUT = 'connect_timeout';
 
+    /**
+     * crypto_method: (int) A value describing the minimum TLS protocol
+     * version to use.
+     *
+     * This setting must be set to one of the
+     * ``STREAM_CRYPTO_METHOD_TLS*_CLIENT`` constants. PHP 7.4 or higher is
+     * required in order to use TLS 1.3, and cURL 7.34.0 or higher is required
+     * in order to specify a crypto method, with cURL 7.52.0 or higher being
+     * required to use TLS 1.3.
+     */
+    public const CRYPTO_METHOD = 'crypto_method';
+
     /**
      * debug: (bool|resource) Set to true or set to a PHP stream returned by
      * fopen()  enable debug output with the HTTP handler used to send a
diff --git a/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php b/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php
index 0236a9d..65f49cb 100644
--- a/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php
+++ b/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php
@@ -40,11 +40,11 @@ class RetryMiddleware
      *                                                                         and returns the number of
      *                                                                         milliseconds to delay.
      */
-    public function __construct(callable $decider, callable $nextHandler, callable $delay = null)
+    public function __construct(callable $decider, callable $nextHandler, ?callable $delay = null)
     {
         $this->decider = $decider;
         $this->nextHandler = $nextHandler;
-        $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
+        $this->delay = $delay ?: __CLASS__.'::exponentialDelay';
     }
 
     /**
@@ -54,7 +54,7 @@ class RetryMiddleware
      */
     public static function exponentialDelay(int $retries): int
     {
-        return (int) \pow(2, $retries - 1) * 1000;
+        return (int) 2 ** ($retries - 1) * 1000;
     }
 
     public function __invoke(RequestInterface $request, array $options): PromiseInterface
@@ -64,6 +64,7 @@ class RetryMiddleware
         }
 
         $fn = $this->nextHandler;
+
         return $fn($request, $options)
             ->then(
                 $this->onFulfilled($request, $options),
@@ -85,6 +86,7 @@ class RetryMiddleware
             )) {
                 return $value;
             }
+
             return $this->doRetry($request, $options, $value);
         };
     }
@@ -103,11 +105,12 @@ class RetryMiddleware
             )) {
                 return P\Create::rejectionFor($reason);
             }
+
             return $this->doRetry($req, $options);
         };
     }
 
-    private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null): PromiseInterface
+    private function doRetry(RequestInterface $request, array $options, ?ResponseInterface $response = null): PromiseInterface
     {
         $options['delay'] = ($this->delay)(++$options['retries'], $response, $request);
 
diff --git a/vendor/guzzlehttp/guzzle/src/Utils.php b/vendor/guzzlehttp/guzzle/src/Utils.php
index e355f32..df52927 100644
--- a/vendor/guzzlehttp/guzzle/src/Utils.php
+++ b/vendor/guzzlehttp/guzzle/src/Utils.php
@@ -23,9 +23,9 @@ final class Utils
     {
         switch (\gettype($input)) {
             case 'object':
-                return 'object(' . \get_class($input) . ')';
+                return 'object('.\get_class($input).')';
             case 'array':
-                return 'array(' . \count($input) . ')';
+                return 'array('.\count($input).')';
             default:
                 \ob_start();
                 \var_dump($input);
@@ -71,7 +71,7 @@ final class Utils
             return \STDOUT;
         }
 
-        return \GuzzleHttp\Psr7\Utils::tryFopen('php://output', 'w');
+        return Psr7\Utils::tryFopen('php://output', 'w');
     }
 
     /**
@@ -79,15 +79,15 @@ final class Utils
      *
      * The returned handler is not wrapped by any default middlewares.
      *
-     * @throws \RuntimeException if no viable Handler is available.
-     *
      * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
+     *
+     * @throws \RuntimeException if no viable Handler is available.
      */
     public static function chooseHandler(): callable
     {
         $handler = null;
 
-        if (\defined('CURLOPT_CUSTOMREQUEST')) {
+        if (\defined('CURLOPT_CUSTOMREQUEST') && \function_exists('curl_version') && version_compare(curl_version()['version'], '7.21.2') >= 0) {
             if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) {
                 $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
             } elseif (\function_exists('curl_exec')) {
@@ -176,14 +176,13 @@ No system CA bundle could be found in any of the the common system locations.
 PHP versions earlier than 5.6 are not properly configured to use the system's
 CA bundle by default. In order to verify peer certificates, you will need to
 supply the path on disk to a certificate bundle to the 'verify' request
-option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
-need a specific certificate bundle, then Mozilla provides a commonly used CA
-bundle which can be downloaded here (provided by the maintainer of cURL):
-https://curl.haxx.se/ca/cacert.pem. Once
-you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
-ini setting to point to the path to the file, allowing you to omit the 'verify'
-request option. See https://curl.haxx.se/docs/sslcerts.html for more
-information.
+option: https://docs.guzzlephp.org/en/latest/request-options.html#verify. If
+you do not need a specific certificate bundle, then Mozilla provides a commonly
+used CA bundle which can be downloaded here (provided by the maintainer of
+cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available
+on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path
+to the file, allowing you to omit the 'verify' request option. See
+https://curl.haxx.se/docs/sslcerts.html for more information.
 EOT
         );
     }
@@ -247,8 +246,8 @@ EOT
             }
             // Special match if the area when prefixed with ".". Remove any
             // existing leading "." and add a new leading ".".
-            $area = '.' . \ltrim($area, '.');
-            if (\substr($host, -(\strlen($area))) === $area) {
+            $area = '.'.\ltrim($area, '.');
+            if (\substr($host, -\strlen($area)) === $area) {
                 return true;
             }
         }
@@ -269,13 +268,13 @@ EOT
      *
      * @throws InvalidArgumentException if the JSON cannot be decoded.
      *
-     * @link https://www.php.net/manual/en/function.json-decode.php
+     * @see https://www.php.net/manual/en/function.json-decode.php
      */
     public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
     {
         $data = \json_decode($json, $assoc, $depth, $options);
         if (\JSON_ERROR_NONE !== \json_last_error()) {
-            throw new InvalidArgumentException('json_decode error: ' . \json_last_error_msg());
+            throw new InvalidArgumentException('json_decode error: '.\json_last_error_msg());
         }
 
         return $data;
@@ -290,13 +289,13 @@ EOT
      *
      * @throws InvalidArgumentException if the JSON cannot be encoded.
      *
-     * @link https://www.php.net/manual/en/function.json-encode.php
+     * @see https://www.php.net/manual/en/function.json-encode.php
      */
     public static function jsonEncode($value, int $options = 0, int $depth = 512): string
     {
         $json = \json_encode($value, $options, $depth);
         if (\JSON_ERROR_NONE !== \json_last_error()) {
-            throw new InvalidArgumentException('json_encode error: ' . \json_last_error_msg());
+            throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg());
         }
 
         /** @var string */
@@ -341,7 +340,7 @@ EOT
 
                 $errorMessage = 'IDN conversion failed';
                 if ($errors) {
-                    $errorMessage .= ' (errors: ' . implode(', ', $errors) . ')';
+                    $errorMessage .= ' (errors: '.implode(', ', $errors).')';
                 }
 
                 throw new InvalidArgumentException($errorMessage);
diff --git a/vendor/guzzlehttp/guzzle/src/functions.php b/vendor/guzzlehttp/guzzle/src/functions.php
index a70d2cb..5edc66a 100644
--- a/vendor/guzzlehttp/guzzle/src/functions.php
+++ b/vendor/guzzlehttp/guzzle/src/functions.php
@@ -50,10 +50,10 @@ function debug_resource($value = null)
  *
  * The returned handler is not wrapped by any default middlewares.
  *
- * @throws \RuntimeException if no viable Handler is available.
- *
  * @return callable(\Psr\Http\Message\RequestInterface, array): \GuzzleHttp\Promise\PromiseInterface Returns the best handler for the given system.
  *
+ * @throws \RuntimeException if no viable Handler is available.
+ *
  * @deprecated choose_handler will be removed in guzzlehttp/guzzle:8.0. Use Utils::chooseHandler instead.
  */
 function choose_handler(): callable
@@ -141,7 +141,7 @@ function is_host_in_noproxy(string $host, array $noProxyArray): bool
  *
  * @throws Exception\InvalidArgumentException if the JSON cannot be decoded.
  *
- * @link https://www.php.net/manual/en/function.json-decode.php
+ * @see https://www.php.net/manual/en/function.json-decode.php
  * @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonDecode instead.
  */
 function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0)
@@ -158,7 +158,7 @@ function json_decode(string $json, bool $assoc = false, int $depth = 512, int $o
  *
  * @throws Exception\InvalidArgumentException if the JSON cannot be encoded.
  *
- * @link https://www.php.net/manual/en/function.json-encode.php
+ * @see https://www.php.net/manual/en/function.json-encode.php
  * @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonEncode instead.
  */
 function json_encode($value, int $options = 0, int $depth = 512): string
diff --git a/vendor/guzzlehttp/guzzle/src/functions_include.php b/vendor/guzzlehttp/guzzle/src/functions_include.php
index 6636a42..394f953 100644
--- a/vendor/guzzlehttp/guzzle/src/functions_include.php
+++ b/vendor/guzzlehttp/guzzle/src/functions_include.php
@@ -2,5 +2,5 @@
 
 // Don't redefine the functions if included multiple times.
 if (!\function_exists('GuzzleHttp\describe_type')) {
-    require __DIR__ . '/functions.php';
+    require __DIR__.'/functions.php';
 }
diff --git a/vendor/guzzlehttp/promises/CHANGELOG.md b/vendor/guzzlehttp/promises/CHANGELOG.md
index 253282e..707925a 100644
--- a/vendor/guzzlehttp/promises/CHANGELOG.md
+++ b/vendor/guzzlehttp/promises/CHANGELOG.md
@@ -1,11 +1,57 @@
 # CHANGELOG
 
+
+## 2.0.3 - 2024-07-18
+
+### Changed
+
+- PHP 8.4 support
+
+
+## 2.0.2 - 2023-12-03
+
+### Changed
+
+- Replaced `call_user_func*` with native calls
+
+
+## 2.0.1 - 2023-08-03
+
+### Changed
+
+- PHP 8.3 support
+
+
+## 2.0.0 - 2023-05-21
+
+### Added
+
+- Added PHP 7 type hints
+
+### Changed
+
+- All previously non-final non-exception classes have been marked as soft-final
+
+### Removed
+
+- Dropped PHP < 7.2 support
+- All functions in the `GuzzleHttp\Promise` namespace
+
+
+## 1.5.3 - 2023-05-21
+
+### Changed
+
+- Removed remaining usage of deprecated functions
+
+
 ## 1.5.2 - 2022-08-07
 
 ### Changed
 
 - Officially support PHP 8.2
 
+
 ## 1.5.1 - 2021-10-22
 
 ### Fixed
@@ -13,6 +59,7 @@
 - Revert "Call handler when waiting on fulfilled/rejected Promise"
 - Fix pool memory leak when empty array of promises provided
 
+
 ## 1.5.0 - 2021-10-07
 
 ### Changed
@@ -24,12 +71,14 @@
 
 - Fix manually settle promises generated with `Utils::task`
 
+
 ## 1.4.1 - 2021-02-18
 
 ### Fixed
 
 - Fixed `each_limit` skipping promises and failing
 
+
 ## 1.4.0 - 2020-09-30
 
 ### Added
diff --git a/vendor/guzzlehttp/promises/README.md b/vendor/guzzlehttp/promises/README.md
index 1ea667a..d1c814f 100644
--- a/vendor/guzzlehttp/promises/README.md
+++ b/vendor/guzzlehttp/promises/README.md
@@ -29,6 +29,21 @@ for a general introduction to promises.
   `GuzzleHttp\Promise\Coroutine::of()`.
 
 
+## Installation
+
+```shell
+composer require guzzlehttp/promises
+```
+
+
+## Version Guidance
+
+| Version | Status              | PHP Version  |
+|---------|---------------------|--------------|
+| 1.x     | Security fixes only | >=5.5,<8.3   |
+| 2.x     | Latest              | >=7.2.5,<8.5 |
+
+
 ## Quick Start
 
 A *promise* represents the eventual result of an asynchronous operation. The
@@ -430,8 +445,6 @@ $loop = React\EventLoop\Factory::create();
 $loop->addPeriodicTimer(0, [$queue, 'run']);
 ```
 
-*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
-
 
 ## Implementation Notes
 
@@ -501,8 +514,8 @@ $promise->resolve('foo');
 
 A static API was first introduced in 1.4.0, in order to mitigate problems with
 functions conflicting between global and local copies of the package. The
-function API will be removed in 2.0.0. A migration table has been provided here
-for your convenience:
+function API was removed in 2.0.0. A migration table has been provided here for
+your convenience:
 
 | Original Function | Replacement Method |
 |----------------|----------------|
diff --git a/vendor/guzzlehttp/promises/composer.json b/vendor/guzzlehttp/promises/composer.json
index c959fb3..f64ed77 100644
--- a/vendor/guzzlehttp/promises/composer.json
+++ b/vendor/guzzlehttp/promises/composer.json
@@ -26,32 +26,32 @@
         }
     ],
     "require": {
-        "php": ">=5.5"
+        "php": "^7.2.5 || ^8.0"
     },
     "require-dev": {
-        "symfony/phpunit-bridge": "^4.4 || ^5.1"
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20"
     },
     "autoload": {
         "psr-4": {
             "GuzzleHttp\\Promise\\": "src/"
-        },
-        "files": ["src/functions_include.php"]
+        }
     },
     "autoload-dev": {
         "psr-4": {
             "GuzzleHttp\\Promise\\Tests\\": "tests/"
         }
     },
-    "scripts": {
-        "test": "vendor/bin/simple-phpunit",
-        "test-ci": "vendor/bin/simple-phpunit --coverage-text"
-    },
     "extra": {
-        "branch-alias": {
-            "dev-master": "1.5-dev"
+        "bamarni-bin": {
+            "bin-links": true,
+            "forward-command": false
         }
     },
     "config": {
+        "allow-plugins": {
+            "bamarni/composer-bin-plugin": true
+        },
         "preferred-install": "dist",
         "sort-packages": true
     }
diff --git a/vendor/guzzlehttp/promises/src/AggregateException.php b/vendor/guzzlehttp/promises/src/AggregateException.php
index d2b5712..40ffdbc 100644
--- a/vendor/guzzlehttp/promises/src/AggregateException.php
+++ b/vendor/guzzlehttp/promises/src/AggregateException.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -7,7 +9,7 @@ namespace GuzzleHttp\Promise;
  */
 class AggregateException extends RejectionException
 {
-    public function __construct($msg, array $reasons)
+    public function __construct(string $msg, array $reasons)
     {
         parent::__construct(
             $reasons,
diff --git a/vendor/guzzlehttp/promises/src/CancellationException.php b/vendor/guzzlehttp/promises/src/CancellationException.php
index 56a1ed6..ad8f51d 100644
--- a/vendor/guzzlehttp/promises/src/CancellationException.php
+++ b/vendor/guzzlehttp/promises/src/CancellationException.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
diff --git a/vendor/guzzlehttp/promises/src/Coroutine.php b/vendor/guzzlehttp/promises/src/Coroutine.php
index 670da47..0da0228 100644
--- a/vendor/guzzlehttp/promises/src/Coroutine.php
+++ b/vendor/guzzlehttp/promises/src/Coroutine.php
@@ -1,8 +1,9 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
-use Exception;
 use Generator;
 use Throwable;
 
@@ -27,7 +28,7 @@ use Throwable;
  *         $value = (yield createPromise('a'));
  *         try {
  *             $value = (yield createPromise($value . 'b'));
- *         } catch (\Exception $e) {
+ *         } catch (\Throwable $e) {
  *             // The promise was rejected.
  *         }
  *         yield $value . 'c';
@@ -40,7 +41,7 @@ use Throwable;
  *
  * @return Promise
  *
- * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ * @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
  */
 final class Coroutine implements PromiseInterface
 {
@@ -62,15 +63,13 @@ final class Coroutine implements PromiseInterface
     public function __construct(callable $generatorFn)
     {
         $this->generator = $generatorFn();
-        $this->result = new Promise(function () {
+        $this->result = new Promise(function (): void {
             while (isset($this->currentPromise)) {
                 $this->currentPromise->wait();
             }
         });
         try {
             $this->nextCoroutine($this->generator->current());
-        } catch (\Exception $exception) {
-            $this->result->reject($exception);
         } catch (Throwable $throwable) {
             $this->result->reject($throwable);
         }
@@ -78,53 +77,51 @@ final class Coroutine implements PromiseInterface
 
     /**
      * Create a new coroutine.
-     *
-     * @return self
      */
-    public static function of(callable $generatorFn)
+    public static function of(callable $generatorFn): self
     {
         return new self($generatorFn);
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         return $this->result->then($onFulfilled, $onRejected);
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->result->otherwise($onRejected);
     }
 
-    public function wait($unwrap = true)
+    public function wait(bool $unwrap = true)
     {
         return $this->result->wait($unwrap);
     }
 
-    public function getState()
+    public function getState(): string
     {
         return $this->result->getState();
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
         $this->result->resolve($value);
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
         $this->result->reject($reason);
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         $this->currentPromise->cancel();
         $this->result->cancel();
     }
 
-    private function nextCoroutine($yielded)
+    private function nextCoroutine($yielded): void
     {
         $this->currentPromise = Create::promiseFor($yielded)
             ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
@@ -133,7 +130,7 @@ final class Coroutine implements PromiseInterface
     /**
      * @internal
      */
-    public function _handleSuccess($value)
+    public function _handleSuccess($value): void
     {
         unset($this->currentPromise);
         try {
@@ -143,8 +140,6 @@ final class Coroutine implements PromiseInterface
             } else {
                 $this->result->resolve($value);
             }
-        } catch (Exception $exception) {
-            $this->result->reject($exception);
         } catch (Throwable $throwable) {
             $this->result->reject($throwable);
         }
@@ -153,15 +148,13 @@ final class Coroutine implements PromiseInterface
     /**
      * @internal
      */
-    public function _handleFailure($reason)
+    public function _handleFailure($reason): void
     {
         unset($this->currentPromise);
         try {
             $nextYield = $this->generator->throw(Create::exceptionFor($reason));
             // The throw was caught, so keep iterating on the coroutine
             $this->nextCoroutine($nextYield);
-        } catch (Exception $exception) {
-            $this->result->reject($exception);
         } catch (Throwable $throwable) {
             $this->result->reject($throwable);
         }
diff --git a/vendor/guzzlehttp/promises/src/Create.php b/vendor/guzzlehttp/promises/src/Create.php
index 8d038e9..9d3fc4a 100644
--- a/vendor/guzzlehttp/promises/src/Create.php
+++ b/vendor/guzzlehttp/promises/src/Create.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Create
@@ -8,10 +10,8 @@ final class Create
      * Creates a promise for a value if the value is not a promise.
      *
      * @param mixed $value Promise or value.
-     *
-     * @return PromiseInterface
      */
-    public static function promiseFor($value)
+    public static function promiseFor($value): PromiseInterface
     {
         if ($value instanceof PromiseInterface) {
             return $value;
@@ -23,6 +23,7 @@ final class Create
             $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
             $promise = new Promise($wfn, $cfn);
             $value->then([$promise, 'resolve'], [$promise, 'reject']);
+
             return $promise;
         }
 
@@ -34,10 +35,8 @@ final class Create
      * If the provided reason is a promise, then it is returned as-is.
      *
      * @param mixed $reason Promise or reason.
-     *
-     * @return PromiseInterface
      */
-    public static function rejectionFor($reason)
+    public static function rejectionFor($reason): PromiseInterface
     {
         if ($reason instanceof PromiseInterface) {
             return $reason;
@@ -50,12 +49,10 @@ final class Create
      * Create an exception for a rejected promise value.
      *
      * @param mixed $reason
-     *
-     * @return \Exception|\Throwable
      */
-    public static function exceptionFor($reason)
+    public static function exceptionFor($reason): \Throwable
     {
-        if ($reason instanceof \Exception || $reason instanceof \Throwable) {
+        if ($reason instanceof \Throwable) {
             return $reason;
         }
 
@@ -66,10 +63,8 @@ final class Create
      * Returns an iterator for the given value.
      *
      * @param mixed $value
-     *
-     * @return \Iterator
      */
-    public static function iterFor($value)
+    public static function iterFor($value): \Iterator
     {
         if ($value instanceof \Iterator) {
             return $value;
diff --git a/vendor/guzzlehttp/promises/src/Each.php b/vendor/guzzlehttp/promises/src/Each.php
index 1dda354..dd72c83 100644
--- a/vendor/guzzlehttp/promises/src/Each.php
+++ b/vendor/guzzlehttp/promises/src/Each.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Each
@@ -17,20 +19,16 @@ final class Each
      * index, and the aggregate promise. The callback can invoke any necessary
      * side effects and choose to resolve or reject the aggregate if needed.
      *
-     * @param mixed    $iterable    Iterator or array to iterate over.
-     * @param callable $onFulfilled
-     * @param callable $onRejected
-     *
-     * @return PromiseInterface
+     * @param mixed $iterable Iterator or array to iterate over.
      */
     public static function of(
         $iterable,
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         return (new EachPromise($iterable, [
             'fulfilled' => $onFulfilled,
-            'rejected'  => $onRejected
+            'rejected' => $onRejected,
         ]))->promise();
     }
 
@@ -44,21 +42,17 @@ final class Each
      *
      * @param mixed        $iterable
      * @param int|callable $concurrency
-     * @param callable     $onFulfilled
-     * @param callable     $onRejected
-     *
-     * @return PromiseInterface
      */
     public static function ofLimit(
         $iterable,
         $concurrency,
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         return (new EachPromise($iterable, [
-            'fulfilled'   => $onFulfilled,
-            'rejected'    => $onRejected,
-            'concurrency' => $concurrency
+            'fulfilled' => $onFulfilled,
+            'rejected' => $onRejected,
+            'concurrency' => $concurrency,
         ]))->promise();
     }
 
@@ -69,20 +63,17 @@ final class Each
      *
      * @param mixed        $iterable
      * @param int|callable $concurrency
-     * @param callable     $onFulfilled
-     *
-     * @return PromiseInterface
      */
     public static function ofLimitAll(
         $iterable,
         $concurrency,
-        callable $onFulfilled = null
-    ) {
-        return each_limit(
+        ?callable $onFulfilled = null
+    ): PromiseInterface {
+        return self::ofLimit(
             $iterable,
             $concurrency,
             $onFulfilled,
-            function ($reason, $idx, PromiseInterface $aggregate) {
+            function ($reason, $idx, PromiseInterface $aggregate): void {
                 $aggregate->reject($reason);
             }
         );
diff --git a/vendor/guzzlehttp/promises/src/EachPromise.php b/vendor/guzzlehttp/promises/src/EachPromise.php
index 280d799..e123898 100644
--- a/vendor/guzzlehttp/promises/src/EachPromise.php
+++ b/vendor/guzzlehttp/promises/src/EachPromise.php
@@ -1,10 +1,14 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
  * Represents a promise that iterates over many promises and invokes
  * side-effect functions in the process.
+ *
+ * @final
  */
 class EachPromise implements PromisorInterface
 {
@@ -69,7 +73,7 @@ class EachPromise implements PromisorInterface
     }
 
     /** @psalm-suppress InvalidNullableReturnType */
-    public function promise()
+    public function promise(): PromiseInterface
     {
         if ($this->aggregate) {
             return $this->aggregate;
@@ -82,21 +86,18 @@ class EachPromise implements PromisorInterface
             $this->refillPending();
         } catch (\Throwable $e) {
             $this->aggregate->reject($e);
-        } catch (\Exception $e) {
-            $this->aggregate->reject($e);
         }
 
         /**
          * @psalm-suppress NullableReturnStatement
-         * @phpstan-ignore-next-line
          */
         return $this->aggregate;
     }
 
-    private function createPromise()
+    private function createPromise(): void
     {
         $this->mutex = false;
-        $this->aggregate = new Promise(function () {
+        $this->aggregate = new Promise(function (): void {
             if ($this->checkIfFinished()) {
                 return;
             }
@@ -113,7 +114,7 @@ class EachPromise implements PromisorInterface
         });
 
         // Clear the references when the promise is resolved.
-        $clearFn = function () {
+        $clearFn = function (): void {
             $this->iterable = $this->concurrency = $this->pending = null;
             $this->onFulfilled = $this->onRejected = null;
             $this->nextPendingIndex = 0;
@@ -122,17 +123,19 @@ class EachPromise implements PromisorInterface
         $this->aggregate->then($clearFn, $clearFn);
     }
 
-    private function refillPending()
+    private function refillPending(): void
     {
         if (!$this->concurrency) {
             // Add all pending promises.
-            while ($this->addPending() && $this->advanceIterator());
+            while ($this->addPending() && $this->advanceIterator()) {
+            }
+
             return;
         }
 
         // Add only up to N pending promises.
         $concurrency = is_callable($this->concurrency)
-            ? call_user_func($this->concurrency, count($this->pending))
+            ? ($this->concurrency)(count($this->pending))
             : $this->concurrency;
         $concurrency = max($concurrency - count($this->pending), 0);
         // Concurrency may be set to 0 to disallow new promises.
@@ -147,10 +150,11 @@ class EachPromise implements PromisorInterface
         // next value to yield until promise callbacks are called.
         while (--$concurrency
             && $this->advanceIterator()
-            && $this->addPending());
+            && $this->addPending()) {
+        }
     }
 
-    private function addPending()
+    private function addPending(): bool
     {
         if (!$this->iterable || !$this->iterable->valid()) {
             return false;
@@ -164,10 +168,9 @@ class EachPromise implements PromisorInterface
         $idx = $this->nextPendingIndex++;
 
         $this->pending[$idx] = $promise->then(
-            function ($value) use ($idx, $key) {
+            function ($value) use ($idx, $key): void {
                 if ($this->onFulfilled) {
-                    call_user_func(
-                        $this->onFulfilled,
+                    ($this->onFulfilled)(
                         $value,
                         $key,
                         $this->aggregate
@@ -175,10 +178,9 @@ class EachPromise implements PromisorInterface
                 }
                 $this->step($idx);
             },
-            function ($reason) use ($idx, $key) {
+            function ($reason) use ($idx, $key): void {
                 if ($this->onRejected) {
-                    call_user_func(
-                        $this->onRejected,
+                    ($this->onRejected)(
                         $reason,
                         $key,
                         $this->aggregate
@@ -191,7 +193,7 @@ class EachPromise implements PromisorInterface
         return true;
     }
 
-    private function advanceIterator()
+    private function advanceIterator(): bool
     {
         // Place a lock on the iterator so that we ensure to not recurse,
         // preventing fatal generator errors.
@@ -204,19 +206,17 @@ class EachPromise implements PromisorInterface
         try {
             $this->iterable->next();
             $this->mutex = false;
+
             return true;
         } catch (\Throwable $e) {
             $this->aggregate->reject($e);
             $this->mutex = false;
-            return false;
-        } catch (\Exception $e) {
-            $this->aggregate->reject($e);
-            $this->mutex = false;
+
             return false;
         }
     }
 
-    private function step($idx)
+    private function step(int $idx): void
     {
         // If the promise was already resolved, then ignore this step.
         if (Is::settled($this->aggregate)) {
@@ -234,11 +234,12 @@ class EachPromise implements PromisorInterface
         }
     }
 
-    private function checkIfFinished()
+    private function checkIfFinished(): bool
     {
         if (!$this->pending && !$this->iterable->valid()) {
             // Resolve the promise if there's nothing left to do.
             $this->aggregate->resolve(null);
+
             return true;
         }
 
diff --git a/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/vendor/guzzlehttp/promises/src/FulfilledPromise.php
index 98f72a6..727ec31 100644
--- a/vendor/guzzlehttp/promises/src/FulfilledPromise.php
+++ b/vendor/guzzlehttp/promises/src/FulfilledPromise.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -7,11 +9,16 @@ namespace GuzzleHttp\Promise;
  *
  * Thenning off of this promise will invoke the onFulfilled callback
  * immediately and ignore other callbacks.
+ *
+ * @final
  */
 class FulfilledPromise implements PromiseInterface
 {
     private $value;
 
+    /**
+     * @param mixed $value
+     */
     public function __construct($value)
     {
         if (is_object($value) && method_exists($value, 'then')) {
@@ -24,9 +31,9 @@ class FulfilledPromise implements PromiseInterface
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         // Return itself if there is no onFulfilled function.
         if (!$onFulfilled) {
             return $this;
@@ -35,14 +42,12 @@ class FulfilledPromise implements PromiseInterface
         $queue = Utils::queue();
         $p = new Promise([$queue, 'run']);
         $value = $this->value;
-        $queue->add(static function () use ($p, $value, $onFulfilled) {
+        $queue->add(static function () use ($p, $value, $onFulfilled): void {
             if (Is::pending($p)) {
                 try {
                     $p->resolve($onFulfilled($value));
                 } catch (\Throwable $e) {
                     $p->reject($e);
-                } catch (\Exception $e) {
-                    $p->reject($e);
                 }
             }
         });
@@ -50,34 +55,34 @@ class FulfilledPromise implements PromiseInterface
         return $p;
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->then(null, $onRejected);
     }
 
-    public function wait($unwrap = true, $defaultDelivery = null)
+    public function wait(bool $unwrap = true)
     {
         return $unwrap ? $this->value : null;
     }
 
-    public function getState()
+    public function getState(): string
     {
         return self::FULFILLED;
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
         if ($value !== $this->value) {
-            throw new \LogicException("Cannot resolve a fulfilled promise");
+            throw new \LogicException('Cannot resolve a fulfilled promise');
         }
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
-        throw new \LogicException("Cannot reject a fulfilled promise");
+        throw new \LogicException('Cannot reject a fulfilled promise');
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         // pass
     }
diff --git a/vendor/guzzlehttp/promises/src/Is.php b/vendor/guzzlehttp/promises/src/Is.php
index c3ed8d0..f3f0503 100644
--- a/vendor/guzzlehttp/promises/src/Is.php
+++ b/vendor/guzzlehttp/promises/src/Is.php
@@ -1,45 +1,39 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Is
 {
     /**
      * Returns true if a promise is pending.
-     *
-     * @return bool
      */
-    public static function pending(PromiseInterface $promise)
+    public static function pending(PromiseInterface $promise): bool
     {
         return $promise->getState() === PromiseInterface::PENDING;
     }
 
     /**
      * Returns true if a promise is fulfilled or rejected.
-     *
-     * @return bool
      */
-    public static function settled(PromiseInterface $promise)
+    public static function settled(PromiseInterface $promise): bool
     {
         return $promise->getState() !== PromiseInterface::PENDING;
     }
 
     /**
      * Returns true if a promise is fulfilled.
-     *
-     * @return bool
      */
-    public static function fulfilled(PromiseInterface $promise)
+    public static function fulfilled(PromiseInterface $promise): bool
     {
         return $promise->getState() === PromiseInterface::FULFILLED;
     }
 
     /**
      * Returns true if a promise is rejected.
-     *
-     * @return bool
      */
-    public static function rejected(PromiseInterface $promise)
+    public static function rejected(PromiseInterface $promise): bool
     {
         return $promise->getState() === PromiseInterface::REJECTED;
     }
diff --git a/vendor/guzzlehttp/promises/src/Promise.php b/vendor/guzzlehttp/promises/src/Promise.php
index 7593905..c0c5be2 100644
--- a/vendor/guzzlehttp/promises/src/Promise.php
+++ b/vendor/guzzlehttp/promises/src/Promise.php
@@ -1,11 +1,15 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
  * Promises/A+ implementation that avoids recursion when possible.
  *
- * @link https://promisesaplus.com/
+ * @see https://promisesaplus.com/
+ *
+ * @final
  */
 class Promise implements PromiseInterface
 {
@@ -21,43 +25,46 @@ class Promise implements PromiseInterface
      * @param callable $cancelFn Fn that when invoked cancels the promise.
      */
     public function __construct(
-        callable $waitFn = null,
-        callable $cancelFn = null
+        ?callable $waitFn = null,
+        ?callable $cancelFn = null
     ) {
         $this->waitFn = $waitFn;
         $this->cancelFn = $cancelFn;
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         if ($this->state === self::PENDING) {
             $p = new Promise(null, [$this, 'cancel']);
             $this->handlers[] = [$p, $onFulfilled, $onRejected];
             $p->waitList = $this->waitList;
             $p->waitList[] = $this;
+
             return $p;
         }
 
         // Return a fulfilled promise and immediately invoke any callbacks.
         if ($this->state === self::FULFILLED) {
             $promise = Create::promiseFor($this->result);
+
             return $onFulfilled ? $promise->then($onFulfilled) : $promise;
         }
 
         // It's either cancelled or rejected, so return a rejected promise
         // and immediately invoke any callbacks.
         $rejection = Create::rejectionFor($this->result);
+
         return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->then(null, $onRejected);
     }
 
-    public function wait($unwrap = true)
+    public function wait(bool $unwrap = true)
     {
         $this->waitIfPending();
 
@@ -73,12 +80,12 @@ class Promise implements PromiseInterface
         }
     }
 
-    public function getState()
+    public function getState(): string
     {
         return $this->state;
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         if ($this->state !== self::PENDING) {
             return;
@@ -93,8 +100,6 @@ class Promise implements PromiseInterface
                 $fn();
             } catch (\Throwable $e) {
                 $this->reject($e);
-            } catch (\Exception $e) {
-                $this->reject($e);
             }
         }
 
@@ -105,17 +110,17 @@ class Promise implements PromiseInterface
         }
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
         $this->settle(self::FULFILLED, $value);
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
         $this->settle(self::REJECTED, $reason);
     }
 
-    private function settle($state, $value)
+    private function settle(string $state, $value): void
     {
         if ($this->state !== self::PENDING) {
             // Ignore calls with the same resolution.
@@ -148,7 +153,7 @@ class Promise implements PromiseInterface
         if (!is_object($value) || !method_exists($value, 'then')) {
             $id = $state === self::FULFILLED ? 1 : 2;
             // It's a success, so resolve the handlers in the queue.
-            Utils::queue()->add(static function () use ($id, $value, $handlers) {
+            Utils::queue()->add(static function () use ($id, $value, $handlers): void {
                 foreach ($handlers as $handler) {
                     self::callHandler($id, $value, $handler);
                 }
@@ -159,12 +164,12 @@ class Promise implements PromiseInterface
         } else {
             // Resolve the handlers when the forwarded promise is resolved.
             $value->then(
-                static function ($value) use ($handlers) {
+                static function ($value) use ($handlers): void {
                     foreach ($handlers as $handler) {
                         self::callHandler(1, $value, $handler);
                     }
                 },
-                static function ($reason) use ($handlers) {
+                static function ($reason) use ($handlers): void {
                     foreach ($handlers as $handler) {
                         self::callHandler(2, $reason, $handler);
                     }
@@ -180,7 +185,7 @@ class Promise implements PromiseInterface
      * @param mixed $value   Value to pass to the callback.
      * @param array $handler Array of handler data (promise and callbacks).
      */
-    private static function callHandler($index, $value, array $handler)
+    private static function callHandler(int $index, $value, array $handler): void
     {
         /** @var PromiseInterface $promise */
         $promise = $handler[0];
@@ -211,12 +216,10 @@ class Promise implements PromiseInterface
             }
         } catch (\Throwable $reason) {
             $promise->reject($reason);
-        } catch (\Exception $reason) {
-            $promise->reject($reason);
         }
     }
 
-    private function waitIfPending()
+    private function waitIfPending(): void
     {
         if ($this->state !== self::PENDING) {
             return;
@@ -227,9 +230,9 @@ class Promise implements PromiseInterface
         } else {
             // If there's no wait function, then reject the promise.
             $this->reject('Cannot wait on a promise that has '
-                . 'no internal wait function. You must provide a wait '
-                . 'function when constructing the promise to be able to '
-                . 'wait on a promise.');
+                .'no internal wait function. You must provide a wait '
+                .'function when constructing the promise to be able to '
+                .'wait on a promise.');
         }
 
         Utils::queue()->run();
@@ -240,13 +243,13 @@ class Promise implements PromiseInterface
         }
     }
 
-    private function invokeWaitFn()
+    private function invokeWaitFn(): void
     {
         try {
             $wfn = $this->waitFn;
             $this->waitFn = null;
             $wfn(true);
-        } catch (\Exception $reason) {
+        } catch (\Throwable $reason) {
             if ($this->state === self::PENDING) {
                 // The promise has not been resolved yet, so reject the promise
                 // with the exception.
@@ -259,7 +262,7 @@ class Promise implements PromiseInterface
         }
     }
 
-    private function invokeWaitList()
+    private function invokeWaitList(): void
     {
         $waitList = $this->waitList;
         $this->waitList = null;
diff --git a/vendor/guzzlehttp/promises/src/PromiseInterface.php b/vendor/guzzlehttp/promises/src/PromiseInterface.php
index e598331..c11721e 100644
--- a/vendor/guzzlehttp/promises/src/PromiseInterface.php
+++ b/vendor/guzzlehttp/promises/src/PromiseInterface.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -9,13 +11,13 @@ namespace GuzzleHttp\Promise;
  * which registers callbacks to receive either a promise’s eventual value or
  * the reason why the promise cannot be fulfilled.
  *
- * @link https://promisesaplus.com/
+ * @see https://promisesaplus.com/
  */
 interface PromiseInterface
 {
-    const PENDING = 'pending';
-    const FULFILLED = 'fulfilled';
-    const REJECTED = 'rejected';
+    public const PENDING = 'pending';
+    public const FULFILLED = 'fulfilled';
+    public const REJECTED = 'rejected';
 
     /**
      * Appends fulfillment and rejection handlers to the promise, and returns
@@ -23,13 +25,11 @@ interface PromiseInterface
      *
      * @param callable $onFulfilled Invoked when the promise fulfills.
      * @param callable $onRejected  Invoked when the promise is rejected.
-     *
-     * @return PromiseInterface
      */
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    );
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface;
 
     /**
      * Appends a rejection handler callback to the promise, and returns a new
@@ -38,20 +38,16 @@ interface PromiseInterface
      * fulfilled.
      *
      * @param callable $onRejected Invoked when the promise is rejected.
-     *
-     * @return PromiseInterface
      */
-    public function otherwise(callable $onRejected);
+    public function otherwise(callable $onRejected): PromiseInterface;
 
     /**
      * Get the state of the promise ("pending", "rejected", or "fulfilled").
      *
      * The three states can be checked against the constants defined on
      * PromiseInterface: PENDING, FULFILLED, and REJECTED.
-     *
-     * @return string
      */
-    public function getState();
+    public function getState(): string;
 
     /**
      * Resolve the promise with the given value.
@@ -60,7 +56,7 @@ interface PromiseInterface
      *
      * @throws \RuntimeException if the promise is already resolved.
      */
-    public function resolve($value);
+    public function resolve($value): void;
 
     /**
      * Reject the promise with the given reason.
@@ -69,14 +65,14 @@ interface PromiseInterface
      *
      * @throws \RuntimeException if the promise is already resolved.
      */
-    public function reject($reason);
+    public function reject($reason): void;
 
     /**
      * Cancels the promise if possible.
      *
-     * @link https://github.com/promises-aplus/cancellation-spec/issues/7
+     * @see https://github.com/promises-aplus/cancellation-spec/issues/7
      */
-    public function cancel();
+    public function cancel(): void;
 
     /**
      * Waits until the promise completes if possible.
@@ -86,12 +82,10 @@ interface PromiseInterface
      *
      * If the promise cannot be waited on, then the promise will be rejected.
      *
-     * @param bool $unwrap
-     *
      * @return mixed
      *
      * @throws \LogicException if the promise has no wait function or if the
      *                         promise does not settle after waiting.
      */
-    public function wait($unwrap = true);
+    public function wait(bool $unwrap = true);
 }
diff --git a/vendor/guzzlehttp/promises/src/PromisorInterface.php b/vendor/guzzlehttp/promises/src/PromisorInterface.php
index 2d2e342..5e8c512 100644
--- a/vendor/guzzlehttp/promises/src/PromisorInterface.php
+++ b/vendor/guzzlehttp/promises/src/PromisorInterface.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -9,8 +11,6 @@ interface PromisorInterface
 {
     /**
      * Returns a promise.
-     *
-     * @return PromiseInterface
      */
-    public function promise();
+    public function promise(): PromiseInterface;
 }
diff --git a/vendor/guzzlehttp/promises/src/RejectedPromise.php b/vendor/guzzlehttp/promises/src/RejectedPromise.php
index d291846..1ebf0b2 100644
--- a/vendor/guzzlehttp/promises/src/RejectedPromise.php
+++ b/vendor/guzzlehttp/promises/src/RejectedPromise.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -7,11 +9,16 @@ namespace GuzzleHttp\Promise;
  *
  * Thenning off of this promise will invoke the onRejected callback
  * immediately and ignore other callbacks.
+ *
+ * @final
  */
 class RejectedPromise implements PromiseInterface
 {
     private $reason;
 
+    /**
+     * @param mixed $reason
+     */
     public function __construct($reason)
     {
         if (is_object($reason) && method_exists($reason, 'then')) {
@@ -24,9 +31,9 @@ class RejectedPromise implements PromiseInterface
     }
 
     public function then(
-        callable $onFulfilled = null,
-        callable $onRejected = null
-    ) {
+        ?callable $onFulfilled = null,
+        ?callable $onRejected = null
+    ): PromiseInterface {
         // If there's no onRejected callback then just return self.
         if (!$onRejected) {
             return $this;
@@ -35,7 +42,7 @@ class RejectedPromise implements PromiseInterface
         $queue = Utils::queue();
         $reason = $this->reason;
         $p = new Promise([$queue, 'run']);
-        $queue->add(static function () use ($p, $reason, $onRejected) {
+        $queue->add(static function () use ($p, $reason, $onRejected): void {
             if (Is::pending($p)) {
                 try {
                     // Return a resolved promise if onRejected does not throw.
@@ -43,9 +50,6 @@ class RejectedPromise implements PromiseInterface
                 } catch (\Throwable $e) {
                     // onRejected threw, so return a rejected promise.
                     $p->reject($e);
-                } catch (\Exception $e) {
-                    // onRejected threw, so return a rejected promise.
-                    $p->reject($e);
                 }
             }
         });
@@ -53,12 +57,12 @@ class RejectedPromise implements PromiseInterface
         return $p;
     }
 
-    public function otherwise(callable $onRejected)
+    public function otherwise(callable $onRejected): PromiseInterface
     {
         return $this->then(null, $onRejected);
     }
 
-    public function wait($unwrap = true, $defaultDelivery = null)
+    public function wait(bool $unwrap = true)
     {
         if ($unwrap) {
             throw Create::exceptionFor($this->reason);
@@ -67,24 +71,24 @@ class RejectedPromise implements PromiseInterface
         return null;
     }
 
-    public function getState()
+    public function getState(): string
     {
         return self::REJECTED;
     }
 
-    public function resolve($value)
+    public function resolve($value): void
     {
-        throw new \LogicException("Cannot resolve a rejected promise");
+        throw new \LogicException('Cannot resolve a rejected promise');
     }
 
-    public function reject($reason)
+    public function reject($reason): void
     {
         if ($reason !== $this->reason) {
-            throw new \LogicException("Cannot reject a rejected promise");
+            throw new \LogicException('Cannot reject a rejected promise');
         }
     }
 
-    public function cancel()
+    public function cancel(): void
     {
         // pass
     }
diff --git a/vendor/guzzlehttp/promises/src/RejectionException.php b/vendor/guzzlehttp/promises/src/RejectionException.php
index e2f1377..47dca86 100644
--- a/vendor/guzzlehttp/promises/src/RejectionException.php
+++ b/vendor/guzzlehttp/promises/src/RejectionException.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -13,24 +15,23 @@ class RejectionException extends \RuntimeException
     private $reason;
 
     /**
-     * @param mixed  $reason      Rejection reason.
-     * @param string $description Optional description
+     * @param mixed       $reason      Rejection reason.
+     * @param string|null $description Optional description.
      */
-    public function __construct($reason, $description = null)
+    public function __construct($reason, ?string $description = null)
     {
         $this->reason = $reason;
 
         $message = 'The promise was rejected';
 
         if ($description) {
-            $message .= ' with reason: ' . $description;
+            $message .= ' with reason: '.$description;
         } elseif (is_string($reason)
             || (is_object($reason) && method_exists($reason, '__toString'))
         ) {
-            $message .= ' with reason: ' . $this->reason;
+            $message .= ' with reason: '.$this->reason;
         } elseif ($reason instanceof \JsonSerializable) {
-            $message .= ' with reason: '
-                . json_encode($this->reason, JSON_PRETTY_PRINT);
+            $message .= ' with reason: '.json_encode($this->reason, JSON_PRETTY_PRINT);
         }
 
         parent::__construct($message);
diff --git a/vendor/guzzlehttp/promises/src/TaskQueue.php b/vendor/guzzlehttp/promises/src/TaskQueue.php
index f0fba2c..503e0b2 100644
--- a/vendor/guzzlehttp/promises/src/TaskQueue.php
+++ b/vendor/guzzlehttp/promises/src/TaskQueue.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 /**
@@ -10,16 +12,18 @@ namespace GuzzleHttp\Promise;
  * by calling the `run()` function of the global task queue in an event loop.
  *
  *     GuzzleHttp\Promise\Utils::queue()->run();
+ *
+ * @final
  */
 class TaskQueue implements TaskQueueInterface
 {
     private $enableShutdown = true;
     private $queue = [];
 
-    public function __construct($withShutdown = true)
+    public function __construct(bool $withShutdown = true)
     {
         if ($withShutdown) {
-            register_shutdown_function(function () {
+            register_shutdown_function(function (): void {
                 if ($this->enableShutdown) {
                     // Only run the tasks if an E_ERROR didn't occur.
                     $err = error_get_last();
@@ -31,17 +35,17 @@ class TaskQueue implements TaskQueueInterface
         }
     }
 
-    public function isEmpty()
+    public function isEmpty(): bool
     {
         return !$this->queue;
     }
 
-    public function add(callable $task)
+    public function add(callable $task): void
     {
         $this->queue[] = $task;
     }
 
-    public function run()
+    public function run(): void
     {
         while ($task = array_shift($this->queue)) {
             /** @var callable $task */
@@ -60,7 +64,7 @@ class TaskQueue implements TaskQueueInterface
      *
      * Note: This shutdown will occur before any destructors are triggered.
      */
-    public function disableShutdown()
+    public function disableShutdown(): void
     {
         $this->enableShutdown = false;
     }
diff --git a/vendor/guzzlehttp/promises/src/TaskQueueInterface.php b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
index 723d4d5..34c561a 100644
--- a/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
+++ b/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
@@ -1,24 +1,24 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 interface TaskQueueInterface
 {
     /**
      * Returns true if the queue is empty.
-     *
-     * @return bool
      */
-    public function isEmpty();
+    public function isEmpty(): bool;
 
     /**
      * Adds a task to the queue that will be executed the next time run is
      * called.
      */
-    public function add(callable $task);
+    public function add(callable $task): void;
 
     /**
      * Execute all of the pending task in the queue.
      */
-    public function run();
+    public function run(): void;
 }
diff --git a/vendor/guzzlehttp/promises/src/Utils.php b/vendor/guzzlehttp/promises/src/Utils.php
index 8647126..45b0893 100644
--- a/vendor/guzzlehttp/promises/src/Utils.php
+++ b/vendor/guzzlehttp/promises/src/Utils.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace GuzzleHttp\Promise;
 
 final class Utils
@@ -17,11 +19,9 @@ final class Utils
      * }
      * </code>
      *
-     * @param TaskQueueInterface $assign Optionally specify a new queue instance.
-     *
-     * @return TaskQueueInterface
+     * @param TaskQueueInterface|null $assign Optionally specify a new queue instance.
      */
-    public static function queue(TaskQueueInterface $assign = null)
+    public static function queue(?TaskQueueInterface $assign = null): TaskQueueInterface
     {
         static $queue;
 
@@ -39,22 +39,18 @@ final class Utils
      * returns a promise that is fulfilled or rejected with the result.
      *
      * @param callable $task Task function to run.
-     *
-     * @return PromiseInterface
      */
-    public static function task(callable $task)
+    public static function task(callable $task): PromiseInterface
     {
         $queue = self::queue();
         $promise = new Promise([$queue, 'run']);
-        $queue->add(function () use ($task, $promise) {
+        $queue->add(function () use ($task, $promise): void {
             try {
                 if (Is::pending($promise)) {
                     $promise->resolve($task());
                 }
             } catch (\Throwable $e) {
                 $promise->reject($e);
-            } catch (\Exception $e) {
-                $promise->reject($e);
             }
         });
 
@@ -72,22 +68,18 @@ final class Utils
      * key mapping to the rejection reason of the promise.
      *
      * @param PromiseInterface $promise Promise or value.
-     *
-     * @return array
      */
-    public static function inspect(PromiseInterface $promise)
+    public static function inspect(PromiseInterface $promise): array
     {
         try {
             return [
                 'state' => PromiseInterface::FULFILLED,
-                'value' => $promise->wait()
+                'value' => $promise->wait(),
             ];
         } catch (RejectionException $e) {
             return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
         } catch (\Throwable $e) {
             return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
-        } catch (\Exception $e) {
-            return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
         }
     }
 
@@ -100,14 +92,12 @@ final class Utils
      * @see inspect for the inspection state array format.
      *
      * @param PromiseInterface[] $promises Traversable of promises to wait upon.
-     *
-     * @return array
      */
-    public static function inspectAll($promises)
+    public static function inspectAll($promises): array
     {
         $results = [];
         foreach ($promises as $key => $promise) {
-            $results[$key] = inspect($promise);
+            $results[$key] = self::inspect($promise);
         }
 
         return $results;
@@ -122,12 +112,9 @@ final class Utils
      *
      * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
      *
-     * @return array
-     *
-     * @throws \Exception on error
-     * @throws \Throwable on error in PHP >=7
+     * @throws \Throwable on error
      */
-    public static function unwrap($promises)
+    public static function unwrap($promises): array
     {
         $results = [];
         foreach ($promises as $key => $promise) {
@@ -147,22 +134,21 @@ final class Utils
      *
      * @param mixed $promises  Promises or values.
      * @param bool  $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
-     *
-     * @return PromiseInterface
      */
-    public static function all($promises, $recursive = false)
+    public static function all($promises, bool $recursive = false): PromiseInterface
     {
         $results = [];
         $promise = Each::of(
             $promises,
-            function ($value, $idx) use (&$results) {
+            function ($value, $idx) use (&$results): void {
                 $results[$idx] = $value;
             },
-            function ($reason, $idx, Promise $aggregate) {
+            function ($reason, $idx, Promise $aggregate): void {
                 $aggregate->reject($reason);
             }
         )->then(function () use (&$results) {
             ksort($results);
+
             return $results;
         });
 
@@ -173,6 +159,7 @@ final class Utils
                         return self::all($promises, $recursive);
                     }
                 }
+
                 return $results;
             });
         }
@@ -193,17 +180,15 @@ final class Utils
      *
      * @param int   $count    Total number of promises.
      * @param mixed $promises Promises or values.
-     *
-     * @return PromiseInterface
      */
-    public static function some($count, $promises)
+    public static function some(int $count, $promises): PromiseInterface
     {
         $results = [];
         $rejections = [];
 
         return Each::of(
             $promises,
-            function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
+            function ($value, $idx, PromiseInterface $p) use (&$results, $count): void {
                 if (Is::settled($p)) {
                     return;
                 }
@@ -212,7 +197,7 @@ final class Utils
                     $p->resolve(null);
                 }
             },
-            function ($reason) use (&$rejections) {
+            function ($reason) use (&$rejections): void {
                 $rejections[] = $reason;
             }
         )->then(
@@ -224,6 +209,7 @@ final class Utils
                     );
                 }
                 ksort($results);
+
                 return array_values($results);
             }
         );
@@ -234,10 +220,8 @@ final class Utils
      * fulfillment value is not an array of 1 but the value directly.
      *
      * @param mixed $promises Promises or values.
-     *
-     * @return PromiseInterface
      */
-    public static function any($promises)
+    public static function any($promises): PromiseInterface
     {
         return self::some(1, $promises)->then(function ($values) {
             return $values[0];
@@ -253,23 +237,22 @@ final class Utils
      * @see inspect for the inspection state array format.
      *
      * @param mixed $promises Promises or values.
-     *
-     * @return PromiseInterface
      */
-    public static function settle($promises)
+    public static function settle($promises): PromiseInterface
     {
         $results = [];
 
         return Each::of(
             $promises,
-            function ($value, $idx) use (&$results) {
+            function ($value, $idx) use (&$results): void {
                 $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
             },
-            function ($reason, $idx) use (&$results) {
+            function ($reason, $idx) use (&$results): void {
                 $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
             }
         )->then(function () use (&$results) {
             ksort($results);
+
             return $results;
         });
     }
diff --git a/vendor/guzzlehttp/promises/src/functions.php b/vendor/guzzlehttp/promises/src/functions.php
deleted file mode 100644
index c03d39d..0000000
--- a/vendor/guzzlehttp/promises/src/functions.php
+++ /dev/null
@@ -1,363 +0,0 @@
-<?php
-
-namespace GuzzleHttp\Promise;
-
-/**
- * Get the global task queue used for promise resolution.
- *
- * This task queue MUST be run in an event loop in order for promises to be
- * settled asynchronously. It will be automatically run when synchronously
- * waiting on a promise.
- *
- * <code>
- * while ($eventLoop->isRunning()) {
- *     GuzzleHttp\Promise\queue()->run();
- * }
- * </code>
- *
- * @param TaskQueueInterface $assign Optionally specify a new queue instance.
- *
- * @return TaskQueueInterface
- *
- * @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
- */
-function queue(TaskQueueInterface $assign = null)
-{
-    return Utils::queue($assign);
-}
-
-/**
- * Adds a function to run in the task queue when it is next `run()` and returns
- * a promise that is fulfilled or rejected with the result.
- *
- * @param callable $task Task function to run.
- *
- * @return PromiseInterface
- *
- * @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
- */
-function task(callable $task)
-{
-    return Utils::task($task);
-}
-
-/**
- * Creates a promise for a value if the value is not a promise.
- *
- * @param mixed $value Promise or value.
- *
- * @return PromiseInterface
- *
- * @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
- */
-function promise_for($value)
-{
-    return Create::promiseFor($value);
-}
-
-/**
- * Creates a rejected promise for a reason if the reason is not a promise. If
- * the provided reason is a promise, then it is returned as-is.
- *
- * @param mixed $reason Promise or reason.
- *
- * @return PromiseInterface
- *
- * @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
- */
-function rejection_for($reason)
-{
-    return Create::rejectionFor($reason);
-}
-
-/**
- * Create an exception for a rejected promise value.
- *
- * @param mixed $reason
- *
- * @return \Exception|\Throwable
- *
- * @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
- */
-function exception_for($reason)
-{
-    return Create::exceptionFor($reason);
-}
-
-/**
- * Returns an iterator for the given value.
- *
- * @param mixed $value
- *
- * @return \Iterator
- *
- * @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
- */
-function iter_for($value)
-{
-    return Create::iterFor($value);
-}
-
-/**
- * Synchronously waits on a promise to resolve and returns an inspection state
- * array.
- *
- * Returns a state associative array containing a "state" key mapping to a
- * valid promise state. If the state of the promise is "fulfilled", the array
- * will contain a "value" key mapping to the fulfilled value of the promise. If
- * the promise is rejected, the array will contain a "reason" key mapping to
- * the rejection reason of the promise.
- *
- * @param PromiseInterface $promise Promise or value.
- *
- * @return array
- *
- * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
- */
-function inspect(PromiseInterface $promise)
-{
-    return Utils::inspect($promise);
-}
-
-/**
- * Waits on all of the provided promises, but does not unwrap rejected promises
- * as thrown exception.
- *
- * Returns an array of inspection state arrays.
- *
- * @see inspect for the inspection state array format.
- *
- * @param PromiseInterface[] $promises Traversable of promises to wait upon.
- *
- * @return array
- *
- * @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
- */
-function inspect_all($promises)
-{
-    return Utils::inspectAll($promises);
-}
-
-/**
- * Waits on all of the provided promises and returns the fulfilled values.
- *
- * Returns an array that contains the value of each promise (in the same order
- * the promises were provided). An exception is thrown if any of the promises
- * are rejected.
- *
- * @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
- *
- * @return array
- *
- * @throws \Exception on error
- * @throws \Throwable on error in PHP >=7
- *
- * @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
- */
-function unwrap($promises)
-{
-    return Utils::unwrap($promises);
-}
-
-/**
- * Given an array of promises, return a promise that is fulfilled when all the
- * items in the array are fulfilled.
- *
- * The promise's fulfillment value is an array with fulfillment values at
- * respective positions to the original array. If any promise in the array
- * rejects, the returned promise is rejected with the rejection reason.
- *
- * @param mixed $promises  Promises or values.
- * @param bool  $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
- *
- * @return PromiseInterface
- *
- * @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
- */
-function all($promises, $recursive = false)
-{
-    return Utils::all($promises, $recursive);
-}
-
-/**
- * Initiate a competitive race between multiple promises or values (values will
- * become immediately fulfilled promises).
- *
- * When count amount of promises have been fulfilled, the returned promise is
- * fulfilled with an array that contains the fulfillment values of the winners
- * in order of resolution.
- *
- * This promise is rejected with a {@see AggregateException} if the number of
- * fulfilled promises is less than the desired $count.
- *
- * @param int   $count    Total number of promises.
- * @param mixed $promises Promises or values.
- *
- * @return PromiseInterface
- *
- * @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
- */
-function some($count, $promises)
-{
-    return Utils::some($count, $promises);
-}
-
-/**
- * Like some(), with 1 as count. However, if the promise fulfills, the
- * fulfillment value is not an array of 1 but the value directly.
- *
- * @param mixed $promises Promises or values.
- *
- * @return PromiseInterface
- *
- * @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
- */
-function any($promises)
-{
-    return Utils::any($promises);
-}
-
-/**
- * Returns a promise that is fulfilled when all of the provided promises have
- * been fulfilled or rejected.
- *
- * The returned promise is fulfilled with an array of inspection state arrays.
- *
- * @see inspect for the inspection state array format.
- *
- * @param mixed $promises Promises or values.
- *
- * @return PromiseInterface
- *
- * @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
- */
-function settle($promises)
-{
-    return Utils::settle($promises);
-}
-
-/**
- * Given an iterator that yields promises or values, returns a promise that is
- * fulfilled with a null value when the iterator has been consumed or the
- * aggregate promise has been fulfilled or rejected.
- *
- * $onFulfilled is a function that accepts the fulfilled value, iterator index,
- * and the aggregate promise. The callback can invoke any necessary side
- * effects and choose to resolve or reject the aggregate if needed.
- *
- * $onRejected is a function that accepts the rejection reason, iterator index,
- * and the aggregate promise. The callback can invoke any necessary side
- * effects and choose to resolve or reject the aggregate if needed.
- *
- * @param mixed    $iterable    Iterator or array to iterate over.
- * @param callable $onFulfilled
- * @param callable $onRejected
- *
- * @return PromiseInterface
- *
- * @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
- */
-function each(
-    $iterable,
-    callable $onFulfilled = null,
-    callable $onRejected = null
-) {
-    return Each::of($iterable, $onFulfilled, $onRejected);
-}
-
-/**
- * Like each, but only allows a certain number of outstanding promises at any
- * given time.
- *
- * $concurrency may be an integer or a function that accepts the number of
- * pending promises and returns a numeric concurrency limit value to allow for
- * dynamic a concurrency size.
- *
- * @param mixed        $iterable
- * @param int|callable $concurrency
- * @param callable     $onFulfilled
- * @param callable     $onRejected
- *
- * @return PromiseInterface
- *
- * @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
- */
-function each_limit(
-    $iterable,
-    $concurrency,
-    callable $onFulfilled = null,
-    callable $onRejected = null
-) {
-    return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
-}
-
-/**
- * Like each_limit, but ensures that no promise in the given $iterable argument
- * is rejected. If any promise is rejected, then the aggregate promise is
- * rejected with the encountered rejection.
- *
- * @param mixed        $iterable
- * @param int|callable $concurrency
- * @param callable     $onFulfilled
- *
- * @return PromiseInterface
- *
- * @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
- */
-function each_limit_all(
-    $iterable,
-    $concurrency,
-    callable $onFulfilled = null
-) {
-    return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
-}
-
-/**
- * Returns true if a promise is fulfilled.
- *
- * @return bool
- *
- * @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
- */
-function is_fulfilled(PromiseInterface $promise)
-{
-    return Is::fulfilled($promise);
-}
-
-/**
- * Returns true if a promise is rejected.
- *
- * @return bool
- *
- * @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
- */
-function is_rejected(PromiseInterface $promise)
-{
-    return Is::rejected($promise);
-}
-
-/**
- * Returns true if a promise is fulfilled or rejected.
- *
- * @return bool
- *
- * @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
- */
-function is_settled(PromiseInterface $promise)
-{
-    return Is::settled($promise);
-}
-
-/**
- * Create a new coroutine.
- *
- * @see Coroutine
- *
- * @return PromiseInterface
- *
- * @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
- */
-function coroutine(callable $generatorFn)
-{
-    return Coroutine::of($generatorFn);
-}
diff --git a/vendor/guzzlehttp/promises/src/functions_include.php b/vendor/guzzlehttp/promises/src/functions_include.php
deleted file mode 100644
index 34cd171..0000000
--- a/vendor/guzzlehttp/promises/src/functions_include.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-// Don't redefine the functions if included multiple times.
-if (!function_exists('GuzzleHttp\Promise\promise_for')) {
-    require __DIR__ . '/functions.php';
-}
diff --git a/vendor/guzzlehttp/psr7/CHANGELOG.md b/vendor/guzzlehttp/psr7/CHANGELOG.md
index 3fcf18a..75aabfb 100644
--- a/vendor/guzzlehttp/psr7/CHANGELOG.md
+++ b/vendor/guzzlehttp/psr7/CHANGELOG.md
@@ -5,7 +5,76 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## Unreleased
+## 2.7.0 - 2024-07-18
+
+### Added
+
+- Add `Utils::redactUserInfo()` method
+- Add ability to encode bools as ints in `Query::build`
+
+## 2.6.3 - 2024-07-18
+
+### Fixed
+
+- Make `StreamWrapper::stream_stat()` return `false` if inner stream's size is `null` 
+
+### Changed
+
+- PHP 8.4 support
+
+## 2.6.2 - 2023-12-03
+
+### Fixed
+
+- Fixed another issue with the fact that PHP transforms numeric strings in array keys to ints
+
+### Changed
+
+- Updated links in docs to their canonical versions
+- Replaced `call_user_func*` with native calls
+
+## 2.6.1 - 2023-08-27
+
+### Fixed
+
+- Properly handle the fact that PHP transforms numeric strings in array keys to ints
+
+## 2.6.0 - 2023-08-03
+
+### Changed
+
+- Updated the mime type map to add some new entries, fix a couple of invalid entries, and remove an invalid entry
+- Fallback to `application/octet-stream` if we are unable to guess the content type for a multipart file upload
+
+## 2.5.1 - 2023-08-03
+
+### Fixed
+
+- Corrected mime type for `.acc` files to `audio/aac`
+
+### Changed
+
+- PHP 8.3 support
+
+## 2.5.0 - 2023-04-17
+
+### Changed
+
+- Adjusted `psr/http-message` version constraint to `^1.1 || ^2.0`
+
+## 2.4.5 - 2023-04-17
+
+### Fixed
+
+- Prevent possible warnings on unset variables in `ServerRequest::normalizeNestedFileSpec`
+- Fixed `Message::bodySummary` when `preg_match` fails
+- Fixed header validation issue
+
+## 2.4.4 - 2023-03-09
+
+### Changed
+
+- Removed the need for `AllowDynamicProperties` in `LazyOpenStream`
 
 ## 2.4.3 - 2022-10-26
 
diff --git a/vendor/guzzlehttp/psr7/README.md b/vendor/guzzlehttp/psr7/README.md
index 8b9929a..2e9bb0b 100644
--- a/vendor/guzzlehttp/psr7/README.md
+++ b/vendor/guzzlehttp/psr7/README.md
@@ -8,12 +8,26 @@ functionality like query string parsing.
 ![Static analysis](https://github.com/guzzle/psr7/workflows/Static%20analysis/badge.svg)
 
 
-# Stream implementation
+## Features
 
 This package comes with a number of stream implementations and stream
 decorators.
 
 
+## Installation
+
+```shell
+composer require guzzlehttp/psr7
+```
+
+## Version Guidance
+
+| Version | Status              | PHP Version  |
+|---------|---------------------|--------------|
+| 1.x     | EOL (2024-06-30)    | >=5.4,<8.2   |
+| 2.x     | Latest              | >=7.2.5,<8.5 |
+
+
 ## AppendStream
 
 `GuzzleHttp\Psr7\AppendStream`
@@ -245,6 +259,8 @@ class EofCallbackStream implements StreamInterface
 
     private $callback;
 
+    private $stream;
+
     public function __construct(StreamInterface $stream, callable $cb)
     {
         $this->stream = $stream;
@@ -257,7 +273,7 @@ class EofCallbackStream implements StreamInterface
 
         // Invoke the callback when EOF is hit.
         if ($this->eof()) {
-            call_user_func($this->callback);
+            ($this->callback)();
         }
 
         return $result;
@@ -420,7 +436,7 @@ will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
 
 ## `GuzzleHttp\Psr7\Query::build`
 
-`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`
+`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string`
 
 Build a query string from an array of key value pairs.
 
@@ -482,11 +498,18 @@ a message.
 
 ## `GuzzleHttp\Psr7\Utils::readLine`
 
-`public static function readLine(StreamInterface $stream, int $maxLength = null): string`
+`public static function readLine(StreamInterface $stream, ?int $maxLength = null): string`
 
 Read a line from the stream up to the maximum allowed buffer length.
 
 
+## `GuzzleHttp\Psr7\Utils::redactUserInfo`
+
+`public static function redactUserInfo(UriInterface $uri): UriInterface`
+
+Redact the password in the user info part of a URI.
+
+
 ## `GuzzleHttp\Psr7\Utils::streamFor`
 
 `public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
@@ -621,7 +644,7 @@ this library also provides additional functionality when working with URIs as st
 An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
 An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
 the base URI. Relative references can be divided into several forms according to
-[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
+[RFC 3986 Section 4.2](https://datatracker.ietf.org/doc/html/rfc3986#section-4.2):
 
 - network-path references, e.g. `//example.com/path`
 - absolute-path references, e.g. `/path`
@@ -658,7 +681,7 @@ termed a relative-path reference.
 
 ### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
 
-`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
+`public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool`
 
 Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
 fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
@@ -680,8 +703,8 @@ or the standard port. This method can be used independently of the implementatio
 `public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
 
 Composes a URI reference string from its various components according to
-[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
-manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
+[RFC 3986 Section 5.3](https://datatracker.ietf.org/doc/html/rfc3986#section-5.3). Usually this method does not need
+to be called manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
 
 ### `GuzzleHttp\Psr7\Uri::fromParts`
 
@@ -725,8 +748,8 @@ Determines if a modified URL should be considered cross-origin with respect to a
 ## Reference Resolution
 
 `GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
-to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
-do when resolving a link in a website based on the current request URI.
+to [RFC 3986 Section 5](https://datatracker.ietf.org/doc/html/rfc3986#section-5). This is for example also what web
+browsers do when resolving a link in a website based on the current request URI.
 
 ### `GuzzleHttp\Psr7\UriResolver::resolve`
 
@@ -739,7 +762,7 @@ Converts the relative URI into a new URI that is resolved against the base URI.
 `public static function removeDotSegments(string $path): string`
 
 Removes dot segments from a path and returns the new path according to
-[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
+[RFC 3986 Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4).
 
 ### `GuzzleHttp\Psr7\UriResolver::relativize`
 
@@ -765,7 +788,7 @@ echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // pr
 ## Normalization and Comparison
 
 `GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
-[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
+[RFC 3986 Section 6](https://datatracker.ietf.org/doc/html/rfc3986#section-6).
 
 ### `GuzzleHttp\Psr7\UriNormalizer::normalize`
 
@@ -847,14 +870,6 @@ This of course assumes they will be resolved against the same base URI. If this
 equivalence or difference of relative references does not mean anything.
 
 
-## Version Guidance
-
-| Version | Status         | PHP Version      |
-|---------|----------------|------------------|
-| 1.x     | Security fixes | >=5.4,<8.1       |
-| 2.x     | Latest         | ^7.2.5 \|\| ^8.0 |
-
-
 ## Security
 
 If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.
diff --git a/vendor/guzzlehttp/psr7/composer.json b/vendor/guzzlehttp/psr7/composer.json
index cd91040..28d15f5 100644
--- a/vendor/guzzlehttp/psr7/composer.json
+++ b/vendor/guzzlehttp/psr7/composer.json
@@ -52,7 +52,7 @@
     "require": {
         "php": "^7.2.5 || ^8.0",
         "psr/http-factory": "^1.0",
-        "psr/http-message": "^1.0",
+        "psr/http-message": "^1.1 || ^2.0",
         "ralouphie/getallheaders": "^3.0"
     },
     "provide": {
@@ -60,9 +60,9 @@
         "psr/http-message-implementation": "1.0"
     },
     "require-dev": {
-        "bamarni/composer-bin-plugin": "^1.8.1",
-        "http-interop/http-factory-tests": "^0.9",
-        "phpunit/phpunit": "^8.5.29 || ^9.5.23"
+        "bamarni/composer-bin-plugin": "^1.8.2",
+        "http-interop/http-factory-tests": "0.9.0",
+        "phpunit/phpunit": "^8.5.39 || ^9.6.20"
     },
     "suggest": {
         "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@@ -81,9 +81,6 @@
         "bamarni-bin": {
             "bin-links": true,
             "forward-command": false
-        },
-        "branch-alias": {
-            "dev-master": "2.4-dev"
         }
     },
     "config": {
diff --git a/vendor/guzzlehttp/psr7/src/AppendStream.php b/vendor/guzzlehttp/psr7/src/AppendStream.php
index cbcfaee..ee8f378 100644
--- a/vendor/guzzlehttp/psr7/src/AppendStream.php
+++ b/vendor/guzzlehttp/psr7/src/AppendStream.php
@@ -40,12 +40,14 @@ final class AppendStream implements StreamInterface
     {
         try {
             $this->rewind();
+
             return $this->getContents();
         } catch (\Throwable $e) {
             if (\PHP_VERSION_ID >= 70400) {
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
@@ -138,9 +140,9 @@ final class AppendStream implements StreamInterface
 
     public function eof(): bool
     {
-        return !$this->streams ||
-            ($this->current >= count($this->streams) - 1 &&
-             $this->streams[$this->current]->eof());
+        return !$this->streams
+            || ($this->current >= count($this->streams) - 1
+             && $this->streams[$this->current]->eof());
     }
 
     public function rewind(): void
@@ -167,7 +169,7 @@ final class AppendStream implements StreamInterface
                 $stream->rewind();
             } catch (\Exception $e) {
                 throw new \RuntimeException('Unable to seek stream '
-                    . $i . ' of the AppendStream', 0, $e);
+                    .$i.' of the AppendStream', 0, $e);
             }
         }
 
@@ -197,7 +199,7 @@ final class AppendStream implements StreamInterface
                 if ($this->current === $total) {
                     break;
                 }
-                $this->current++;
+                ++$this->current;
             }
 
             $result = $this->streams[$this->current]->read($remaining);
@@ -237,8 +239,6 @@ final class AppendStream implements StreamInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
diff --git a/vendor/guzzlehttp/psr7/src/BufferStream.php b/vendor/guzzlehttp/psr7/src/BufferStream.php
index 21be8c0..2b0eb77 100644
--- a/vendor/guzzlehttp/psr7/src/BufferStream.php
+++ b/vendor/guzzlehttp/psr7/src/BufferStream.php
@@ -134,8 +134,6 @@ final class BufferStream implements StreamInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
diff --git a/vendor/guzzlehttp/psr7/src/CachingStream.php b/vendor/guzzlehttp/psr7/src/CachingStream.php
index f34722c..7e4554d 100644
--- a/vendor/guzzlehttp/psr7/src/CachingStream.php
+++ b/vendor/guzzlehttp/psr7/src/CachingStream.php
@@ -33,7 +33,7 @@ final class CachingStream implements StreamInterface
      */
     public function __construct(
         StreamInterface $stream,
-        StreamInterface $target = null
+        ?StreamInterface $target = null
     ) {
         $this->remoteStream = $stream;
         $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
diff --git a/vendor/guzzlehttp/psr7/src/FnStream.php b/vendor/guzzlehttp/psr7/src/FnStream.php
index 3a1a951..9e6a7f3 100644
--- a/vendor/guzzlehttp/psr7/src/FnStream.php
+++ b/vendor/guzzlehttp/psr7/src/FnStream.php
@@ -18,7 +18,7 @@ final class FnStream implements StreamInterface
     private const SLOTS = [
         '__toString', 'close', 'detach', 'rewind',
         'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
-        'isReadable', 'read', 'getContents', 'getMetadata'
+        'isReadable', 'read', 'getContents', 'getMetadata',
     ];
 
     /** @var array<string, callable> */
@@ -33,7 +33,7 @@ final class FnStream implements StreamInterface
 
         // Create the functions on the class
         foreach ($methods as $name => $fn) {
-            $this->{'_fn_' . $name} = $fn;
+            $this->{'_fn_'.$name} = $fn;
         }
     }
 
@@ -45,7 +45,7 @@ final class FnStream implements StreamInterface
     public function __get(string $name): void
     {
         throw new \BadMethodCallException(str_replace('_fn_', '', $name)
-            . '() is not implemented in the FnStream');
+            .'() is not implemented in the FnStream');
     }
 
     /**
@@ -54,7 +54,7 @@ final class FnStream implements StreamInterface
     public function __destruct()
     {
         if (isset($this->_fn_close)) {
-            call_user_func($this->_fn_close);
+            ($this->_fn_close)();
         }
     }
 
@@ -93,88 +93,88 @@ final class FnStream implements StreamInterface
     public function __toString(): string
     {
         try {
-            return call_user_func($this->_fn___toString);
+            /** @var string */
+            return ($this->_fn___toString)();
         } catch (\Throwable $e) {
             if (\PHP_VERSION_ID >= 70400) {
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
 
     public function close(): void
     {
-        call_user_func($this->_fn_close);
+        ($this->_fn_close)();
     }
 
     public function detach()
     {
-        return call_user_func($this->_fn_detach);
+        return ($this->_fn_detach)();
     }
 
     public function getSize(): ?int
     {
-        return call_user_func($this->_fn_getSize);
+        return ($this->_fn_getSize)();
     }
 
     public function tell(): int
     {
-        return call_user_func($this->_fn_tell);
+        return ($this->_fn_tell)();
     }
 
     public function eof(): bool
     {
-        return call_user_func($this->_fn_eof);
+        return ($this->_fn_eof)();
     }
 
     public function isSeekable(): bool
     {
-        return call_user_func($this->_fn_isSeekable);
+        return ($this->_fn_isSeekable)();
     }
 
     public function rewind(): void
     {
-        call_user_func($this->_fn_rewind);
+        ($this->_fn_rewind)();
     }
 
     public function seek($offset, $whence = SEEK_SET): void
     {
-        call_user_func($this->_fn_seek, $offset, $whence);
+        ($this->_fn_seek)($offset, $whence);
     }
 
     public function isWritable(): bool
     {
-        return call_user_func($this->_fn_isWritable);
+        return ($this->_fn_isWritable)();
     }
 
     public function write($string): int
     {
-        return call_user_func($this->_fn_write, $string);
+        return ($this->_fn_write)($string);
     }
 
     public function isReadable(): bool
     {
-        return call_user_func($this->_fn_isReadable);
+        return ($this->_fn_isReadable)();
     }
 
     public function read($length): string
     {
-        return call_user_func($this->_fn_read, $length);
+        return ($this->_fn_read)($length);
     }
 
     public function getContents(): string
     {
-        return call_user_func($this->_fn_getContents);
+        return ($this->_fn_getContents)();
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
     {
-        return call_user_func($this->_fn_getMetadata, $key);
+        return ($this->_fn_getMetadata)($key);
     }
 }
diff --git a/vendor/guzzlehttp/psr7/src/Header.php b/vendor/guzzlehttp/psr7/src/Header.php
index 4d7005b..bbce8b0 100644
--- a/vendor/guzzlehttp/psr7/src/Header.php
+++ b/vendor/guzzlehttp/psr7/src/Header.php
@@ -22,7 +22,7 @@ final class Header
         foreach ((array) $header as $value) {
             foreach (self::splitList($value) as $val) {
                 $part = [];
-                foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
+                foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) ?: [] as $kvp) {
                     if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
                         $m = $matches[0];
                         if (isset($m[1])) {
@@ -89,7 +89,7 @@ final class Header
             $v = '';
             $isQuoted = false;
             $isEscaped = false;
-            for ($i = 0, $max = \strlen($value); $i < $max; $i++) {
+            for ($i = 0, $max = \strlen($value); $i < $max; ++$i) {
                 if ($isEscaped) {
                     $v .= $value[$i];
                     $isEscaped = false;
diff --git a/vendor/guzzlehttp/psr7/src/HttpFactory.php b/vendor/guzzlehttp/psr7/src/HttpFactory.php
index 30be222..3ef1510 100644
--- a/vendor/guzzlehttp/psr7/src/HttpFactory.php
+++ b/vendor/guzzlehttp/psr7/src/HttpFactory.php
@@ -23,20 +23,14 @@ use Psr\Http\Message\UriInterface;
  * Note: in consuming code it is recommended to require the implemented interfaces
  * and inject the instance of this class multiple times.
  */
-final class HttpFactory implements
-    RequestFactoryInterface,
-    ResponseFactoryInterface,
-    ServerRequestFactoryInterface,
-    StreamFactoryInterface,
-    UploadedFileFactoryInterface,
-    UriFactoryInterface
+final class HttpFactory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
 {
     public function createUploadedFile(
         StreamInterface $stream,
-        int $size = null,
+        ?int $size = null,
         int $error = \UPLOAD_ERR_OK,
-        string $clientFilename = null,
-        string $clientMediaType = null
+        ?string $clientFilename = null,
+        ?string $clientMediaType = null
     ): UploadedFileInterface {
         if ($size === null) {
             $size = $stream->getSize();
diff --git a/vendor/guzzlehttp/psr7/src/InflateStream.php b/vendor/guzzlehttp/psr7/src/InflateStream.php
index 8e00f1c..e674c9a 100644
--- a/vendor/guzzlehttp/psr7/src/InflateStream.php
+++ b/vendor/guzzlehttp/psr7/src/InflateStream.php
@@ -13,9 +13,9 @@ use Psr\Http\Message\StreamInterface;
  * then appends the zlib.inflate filter. The stream is then converted back
  * to a Guzzle stream resource to be used as a Guzzle stream.
  *
- * @link http://tools.ietf.org/html/rfc1950
- * @link http://tools.ietf.org/html/rfc1952
- * @link http://php.net/manual/en/filters.compression.php
+ * @see https://datatracker.ietf.org/doc/html/rfc1950
+ * @see https://datatracker.ietf.org/doc/html/rfc1952
+ * @see https://www.php.net/manual/en/filters.compression.php
  */
 final class InflateStream implements StreamInterface
 {
@@ -28,7 +28,7 @@ final class InflateStream implements StreamInterface
     {
         $resource = StreamWrapper::getResource($stream);
         // Specify window=15+32, so zlib will use header detection to both gzip (with header) and zlib data
-        // See http://www.zlib.net/manual.html#Advanced definition of inflateInit2
+        // See https://www.zlib.net/manual.html#Advanced definition of inflateInit2
         // "Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection"
         // Default window size is 15.
         stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ, ['window' => 15 + 32]);
diff --git a/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
index 5618331..f6c8490 100644
--- a/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
+++ b/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
@@ -10,7 +10,6 @@ use Psr\Http\Message\StreamInterface;
  * Lazily reads or writes to a file that is opened only after an IO operation
  * take place on the stream.
  */
-#[\AllowDynamicProperties]
 final class LazyOpenStream implements StreamInterface
 {
     use StreamDecoratorTrait;
@@ -21,6 +20,11 @@ final class LazyOpenStream implements StreamInterface
     /** @var string */
     private $mode;
 
+    /**
+     * @var StreamInterface
+     */
+    private $stream;
+
     /**
      * @param string $filename File to lazily open
      * @param string $mode     fopen mode to use when opening the stream
@@ -29,6 +33,10 @@ final class LazyOpenStream implements StreamInterface
     {
         $this->filename = $filename;
         $this->mode = $mode;
+
+        // unsetting the property forces the first access to go through
+        // __get().
+        unset($this->stream);
     }
 
     /**
diff --git a/vendor/guzzlehttp/psr7/src/Message.php b/vendor/guzzlehttp/psr7/src/Message.php
index 61c1a5d..5561a51 100644
--- a/vendor/guzzlehttp/psr7/src/Message.php
+++ b/vendor/guzzlehttp/psr7/src/Message.php
@@ -18,31 +18,31 @@ final class Message
     public static function toString(MessageInterface $message): string
     {
         if ($message instanceof RequestInterface) {
-            $msg = trim($message->getMethod() . ' '
-                    . $message->getRequestTarget())
-                . ' HTTP/' . $message->getProtocolVersion();
+            $msg = trim($message->getMethod().' '
+                    .$message->getRequestTarget())
+                .' HTTP/'.$message->getProtocolVersion();
             if (!$message->hasHeader('host')) {
-                $msg .= "\r\nHost: " . $message->getUri()->getHost();
+                $msg .= "\r\nHost: ".$message->getUri()->getHost();
             }
         } elseif ($message instanceof ResponseInterface) {
-            $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
-                . $message->getStatusCode() . ' '
-                . $message->getReasonPhrase();
+            $msg = 'HTTP/'.$message->getProtocolVersion().' '
+                .$message->getStatusCode().' '
+                .$message->getReasonPhrase();
         } else {
             throw new \InvalidArgumentException('Unknown message type');
         }
 
         foreach ($message->getHeaders() as $name => $values) {
-            if (strtolower($name) === 'set-cookie') {
+            if (is_string($name) && strtolower($name) === 'set-cookie') {
                 foreach ($values as $value) {
-                    $msg .= "\r\n{$name}: " . $value;
+                    $msg .= "\r\n{$name}: ".$value;
                 }
             } else {
-                $msg .= "\r\n{$name}: " . implode(', ', $values);
+                $msg .= "\r\n{$name}: ".implode(', ', $values);
             }
         }
 
-        return "{$msg}\r\n\r\n" . $message->getBody();
+        return "{$msg}\r\n\r\n".$message->getBody();
     }
 
     /**
@@ -77,7 +77,7 @@ final class Message
 
         // Matches any printable character, including unicode characters:
         // letters, marks, numbers, punctuation, spacing, and separators.
-        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) {
+        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary) !== 0) {
             return null;
         }
 
@@ -146,7 +146,7 @@ final class Message
 
         // If these aren't the same, then one line didn't match and there's an invalid header.
         if ($count !== substr_count($rawHeaders, "\n")) {
-            // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
+            // Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
             if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
                 throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
             }
@@ -190,7 +190,7 @@ final class Message
         $host = $headers[reset($hostKey)][0];
         $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
 
-        return $scheme . '://' . $host . '/' . ltrim($path, '/');
+        return $scheme.'://'.$host.'/'.ltrim($path, '/');
     }
 
     /**
@@ -227,11 +227,11 @@ final class Message
     public static function parseResponse(string $message): ResponseInterface
     {
         $data = self::parseMessage($message);
-        // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
-        // between status-code and reason-phrase is required. But browsers accept
-        // responses without space and reason as well.
+        // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+        // the space between status-code and reason-phrase is required. But
+        // browsers accept responses without space and reason as well.
         if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
-            throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
+            throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']);
         }
         $parts = explode(' ', $data['start-line'], 3);
 
diff --git a/vendor/guzzlehttp/psr7/src/MessageTrait.php b/vendor/guzzlehttp/psr7/src/MessageTrait.php
index d2dc28b..65dbc4b 100644
--- a/vendor/guzzlehttp/psr7/src/MessageTrait.php
+++ b/vendor/guzzlehttp/psr7/src/MessageTrait.php
@@ -12,11 +12,11 @@ use Psr\Http\Message\StreamInterface;
  */
 trait MessageTrait
 {
-    /** @var array<string, string[]> Map of all registered headers, as original name => array of values */
+    /** @var string[][] Map of all registered headers, as original name => array of values */
     private $headers = [];
 
-    /** @var array<string, string> Map of lowercase header name => original name at registration */
-    private $headerNames  = [];
+    /** @var string[] Map of lowercase header name => original name at registration */
+    private $headerNames = [];
 
     /** @var string */
     private $protocol = '1.1';
@@ -37,6 +37,7 @@ trait MessageTrait
 
         $new = clone $this;
         $new->protocol = $version;
+
         return $new;
     }
 
@@ -135,11 +136,12 @@ trait MessageTrait
 
         $new = clone $this;
         $new->stream = $body;
+
         return $new;
     }
 
     /**
-     * @param array<string|int, string|string[]> $headers
+     * @param (string|string[])[] $headers
      */
     private function setHeaders(array $headers): void
     {
@@ -191,7 +193,7 @@ trait MessageTrait
      *
      * @return string[] Trimmed header values
      *
-     * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4
      */
     private function trimAndValidateHeaderValues(array $values): array
     {
@@ -211,7 +213,7 @@ trait MessageTrait
     }
 
     /**
-     * @see https://tools.ietf.org/html/rfc7230#section-3.2
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
      *
      * @param mixed $header
      */
@@ -224,18 +226,15 @@ trait MessageTrait
             ));
         }
 
-        if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) {
+        if (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) {
             throw new \InvalidArgumentException(
-                sprintf(
-                    '"%s" is not valid header name',
-                    $header
-                )
+                sprintf('"%s" is not valid header name.', $header)
             );
         }
     }
 
     /**
-     * @see https://tools.ietf.org/html/rfc7230#section-3.2
+     * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
      *
      * field-value    = *( field-content / obs-fold )
      * field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
@@ -257,8 +256,10 @@ trait MessageTrait
         // Clients must not send a request with line folding and a server sending folded headers is
         // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting
         // folding is not likely to break any legitimate use case.
-        if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/', $value)) {
-            throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value));
+        if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) {
+            throw new \InvalidArgumentException(
+                sprintf('"%s" is not valid header value.', $value)
+            );
         }
     }
 }
diff --git a/vendor/guzzlehttp/psr7/src/MimeType.php b/vendor/guzzlehttp/psr7/src/MimeType.php
index 0debbd1..b131bdb 100644
--- a/vendor/guzzlehttp/psr7/src/MimeType.php
+++ b/vendor/guzzlehttp/psr7/src/MimeType.php
@@ -18,7 +18,7 @@ final class MimeType
         '7zip' => 'application/x-7z-compressed',
         '123' => 'application/vnd.lotus-1-2-3',
         'aab' => 'application/x-authorware-bin',
-        'aac' => 'audio/x-acc',
+        'aac' => 'audio/aac',
         'aam' => 'application/x-authorware-map',
         'aas' => 'application/x-authorware-seg',
         'abw' => 'application/x-abiword',
@@ -29,6 +29,7 @@ final class MimeType
         'acu' => 'application/vnd.acucobol',
         'acutc' => 'application/vnd.acucorp',
         'adp' => 'audio/adpcm',
+        'adts' => 'audio/aac',
         'aep' => 'application/vnd.audiograph',
         'afm' => 'application/x-font-type1',
         'afp' => 'application/vnd.ibm.modcap',
@@ -41,11 +42,16 @@ final class MimeType
         'air' => 'application/vnd.adobe.air-application-installer-package+zip',
         'ait' => 'application/vnd.dvb.ait',
         'ami' => 'application/vnd.amiga.ami',
+        'aml' => 'application/automationml-aml+xml',
+        'amlx' => 'application/automationml-amlx+zip',
         'amr' => 'audio/amr',
         'apk' => 'application/vnd.android.package-archive',
         'apng' => 'image/apng',
         'appcache' => 'text/cache-manifest',
+        'appinstaller' => 'application/appinstaller',
         'application' => 'application/x-ms-application',
+        'appx' => 'application/appx',
+        'appxbundle' => 'application/appxbundle',
         'apr' => 'application/vnd.lotus-approach',
         'arc' => 'application/x-freearc',
         'arj' => 'application/x-arj',
@@ -90,6 +96,7 @@ final class MimeType
         'bpk' => 'application/octet-stream',
         'bpmn' => 'application/octet-stream',
         'bsp' => 'model/vnd.valve.source.compiled-map',
+        'btf' => 'image/prs.btif',
         'btif' => 'image/prs.btif',
         'buffer' => 'application/octet-stream',
         'bz' => 'application/x-bzip',
@@ -141,6 +148,7 @@ final class MimeType
         'cjs' => 'application/node',
         'cla' => 'application/vnd.claymore',
         'class' => 'application/octet-stream',
+        'cld' => 'model/vnd.cld',
         'clkk' => 'application/vnd.crick.clicker.keyboard',
         'clkp' => 'application/vnd.crick.clicker.palette',
         'clkt' => 'application/vnd.crick.clicker.template',
@@ -175,6 +183,7 @@ final class MimeType
         'csv' => 'text/csv',
         'cu' => 'application/cu-seeme',
         'curl' => 'text/vnd.curl',
+        'cwl' => 'application/cwl',
         'cww' => 'application/prs.cww',
         'cxt' => 'application/x-director',
         'cxx' => 'text/x-c',
@@ -197,6 +206,7 @@ final class MimeType
         'der' => 'application/x-x509-ca-cert',
         'dfac' => 'application/vnd.dreamfactory',
         'dgc' => 'application/x-dgc-compressed',
+        'dib' => 'image/bmp',
         'dic' => 'text/x-c',
         'dir' => 'application/x-director',
         'dis' => 'application/vnd.mobius.dis',
@@ -219,6 +229,7 @@ final class MimeType
         'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
         'dp' => 'application/vnd.osgi.dp',
         'dpg' => 'application/vnd.dpgraph',
+        'dpx' => 'image/dpx',
         'dra' => 'audio/vnd.dra',
         'drle' => 'image/dicom-rle',
         'dsc' => 'text/prs.lines.tag',
@@ -255,7 +266,6 @@ final class MimeType
         'eot' => 'application/vnd.ms-fontobject',
         'eps' => 'application/postscript',
         'epub' => 'application/epub+zip',
-        'es' => 'application/ecmascript',
         'es3' => 'application/vnd.eszigno3+xml',
         'esa' => 'application/vnd.osgi.subsystem',
         'esf' => 'application/vnd.epson.esf',
@@ -448,6 +458,7 @@ final class MimeType
         'jsonld' => 'application/ld+json',
         'jsonml' => 'application/jsonml+json',
         'jsx' => 'text/jsx',
+        'jt' => 'model/jt',
         'jxr' => 'image/jxr',
         'jxra' => 'image/jxra',
         'jxrs' => 'image/jxrs',
@@ -552,7 +563,7 @@ final class MimeType
         'mime' => 'message/rfc822',
         'mj2' => 'video/mj2',
         'mjp2' => 'video/mj2',
-        'mjs' => 'application/javascript',
+        'mjs' => 'text/javascript',
         'mk3d' => 'video/x-matroska',
         'mka' => 'audio/x-matroska',
         'mkd' => 'text/x-markdown',
@@ -602,6 +613,8 @@ final class MimeType
         'msg' => 'application/vnd.ms-outlook',
         'msh' => 'model/mesh',
         'msi' => 'application/x-msdownload',
+        'msix' => 'application/msix',
+        'msixbundle' => 'application/msixbundle',
         'msl' => 'application/vnd.mobius.msl',
         'msm' => 'application/octet-stream',
         'msp' => 'application/octet-stream',
@@ -775,6 +788,8 @@ final class MimeType
         'pvb' => 'application/vnd.3gpp.pic-bw-var',
         'pwn' => 'application/vnd.3m.post-it-notes',
         'pya' => 'audio/vnd.ms-playready.media.pya',
+        'pyo' => 'model/vnd.pytha.pyox',
+        'pyox' => 'model/vnd.pytha.pyox',
         'pyv' => 'video/vnd.ms-playready.media.pyv',
         'qam' => 'application/vnd.epson.quickanime',
         'qbo' => 'application/vnd.intu.qbo',
@@ -923,10 +938,12 @@ final class MimeType
         'st' => 'application/vnd.sailingtracker.track',
         'stc' => 'application/vnd.sun.xml.calc.template',
         'std' => 'application/vnd.sun.xml.draw.template',
+        'step' => 'application/STEP',
         'stf' => 'application/vnd.wt.stf',
         'sti' => 'application/vnd.sun.xml.impress.template',
         'stk' => 'application/hyperstudio',
         'stl' => 'model/stl',
+        'stp' => 'application/STEP',
         'stpx' => 'model/step+xml',
         'stpxz' => 'model/step-xml+zip',
         'stpz' => 'model/step+zip',
@@ -1013,10 +1030,12 @@ final class MimeType
         'ulx' => 'application/x-glulx',
         'umj' => 'application/vnd.umajin',
         'unityweb' => 'application/vnd.unity',
+        'uo' => 'application/vnd.uoml+xml',
         'uoml' => 'application/vnd.uoml+xml',
         'uri' => 'text/uri-list',
         'uris' => 'text/uri-list',
         'urls' => 'text/uri-list',
+        'usda' => 'model/vnd.usda',
         'usdz' => 'model/vnd.usdz+zip',
         'ustar' => 'application/x-ustar',
         'utz' => 'application/vnd.uiq.theme',
@@ -1096,6 +1115,7 @@ final class MimeType
         'webmanifest' => 'application/manifest+json',
         'webp' => 'image/webp',
         'wg' => 'application/vnd.pmi.widget',
+        'wgsl' => 'text/wgsl',
         'wgt' => 'application/widget',
         'wif' => 'application/watcherinfo+xml',
         'wks' => 'application/vnd.ms-works',
@@ -1150,9 +1170,10 @@ final class MimeType
         'xel' => 'application/xcap-el+xml',
         'xenc' => 'application/xenc+xml',
         'xer' => 'application/patch-ops-error+xml',
-        'xfdf' => 'application/vnd.adobe.xfdf',
+        'xfdf' => 'application/xfdf',
         'xfdl' => 'application/vnd.xfdl',
         'xht' => 'application/xhtml+xml',
+        'xhtm' => 'application/vnd.pwg-xhtml-print+xml',
         'xhtml' => 'application/xhtml+xml',
         'xhvml' => 'application/xv+xml',
         'xif' => 'image/vnd.xiff',
@@ -1183,6 +1204,7 @@ final class MimeType
         'xpw' => 'application/vnd.intercon.formnet',
         'xpx' => 'application/vnd.intercon.formnet',
         'xsd' => 'application/xml',
+        'xsf' => 'application/prs.xsf+xml',
         'xsl' => 'application/xml',
         'xslt' => 'application/xslt+xml',
         'xsm' => 'application/vnd.syncml+xml',
@@ -1218,7 +1240,7 @@ final class MimeType
     /**
      * Determines the mimetype of a file by looking at its extension.
      *
-     * @link https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
+     * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
      */
     public static function fromFilename(string $filename): ?string
     {
@@ -1228,7 +1250,7 @@ final class MimeType
     /**
      * Maps a file extensions to a mimetype.
      *
-     * @link https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
+     * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json
      */
     public static function fromExtension(string $extension): ?string
     {
diff --git a/vendor/guzzlehttp/psr7/src/MultipartStream.php b/vendor/guzzlehttp/psr7/src/MultipartStream.php
index 3e12b74..43d718f 100644
--- a/vendor/guzzlehttp/psr7/src/MultipartStream.php
+++ b/vendor/guzzlehttp/psr7/src/MultipartStream.php
@@ -32,7 +32,7 @@ final class MultipartStream implements StreamInterface
      *
      * @throws \InvalidArgumentException
      */
-    public function __construct(array $elements = [], string $boundary = null)
+    public function __construct(array $elements = [], ?string $boundary = null)
     {
         $this->boundary = $boundary ?: bin2hex(random_bytes(20));
         $this->stream = $this->createStream($elements);
@@ -51,7 +51,7 @@ final class MultipartStream implements StreamInterface
     /**
      * Get the headers needed before transferring the content of a POST file
      *
-     * @param array<string, string> $headers
+     * @param string[] $headers
      */
     private function getHeaders(array $headers): string
     {
@@ -60,7 +60,7 @@ final class MultipartStream implements StreamInterface
             $str .= "{$key}: {$value}\r\n";
         }
 
-        return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
+        return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n";
     }
 
     /**
@@ -72,7 +72,7 @@ final class MultipartStream implements StreamInterface
 
         foreach ($elements as $element) {
             if (!is_array($element)) {
-                throw new \UnexpectedValueException("An array is expected");
+                throw new \UnexpectedValueException('An array is expected');
             }
             $this->addElement($stream, $element);
         }
@@ -112,10 +112,15 @@ final class MultipartStream implements StreamInterface
         $stream->addStream(Utils::streamFor("\r\n"));
     }
 
+    /**
+     * @param string[] $headers
+     *
+     * @return array{0: StreamInterface, 1: string[]}
+     */
     private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array
     {
         // Set a default content-disposition header if one was no provided
-        $disposition = $this->getHeader($headers, 'content-disposition');
+        $disposition = self::getHeader($headers, 'content-disposition');
         if (!$disposition) {
             $headers['Content-Disposition'] = ($filename === '0' || $filename)
                 ? sprintf(
@@ -127,7 +132,7 @@ final class MultipartStream implements StreamInterface
         }
 
         // Set a default content-length header if one was no provided
-        $length = $this->getHeader($headers, 'content-length');
+        $length = self::getHeader($headers, 'content-length');
         if (!$length) {
             if ($length = $stream->getSize()) {
                 $headers['Content-Length'] = (string) $length;
@@ -135,21 +140,22 @@ final class MultipartStream implements StreamInterface
         }
 
         // Set a default Content-Type if one was not supplied
-        $type = $this->getHeader($headers, 'content-type');
+        $type = self::getHeader($headers, 'content-type');
         if (!$type && ($filename === '0' || $filename)) {
-            if ($type = MimeType::fromFilename($filename)) {
-                $headers['Content-Type'] = $type;
-            }
+            $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream';
         }
 
         return [$stream, $headers];
     }
 
-    private function getHeader(array $headers, string $key)
+    /**
+     * @param string[] $headers
+     */
+    private static function getHeader(array $headers, string $key): ?string
     {
         $lowercaseHeader = strtolower($key);
         foreach ($headers as $k => $v) {
-            if (strtolower($k) === $lowercaseHeader) {
+            if (strtolower((string) $k) === $lowercaseHeader) {
                 return $v;
             }
         }
diff --git a/vendor/guzzlehttp/psr7/src/PumpStream.php b/vendor/guzzlehttp/psr7/src/PumpStream.php
index e90389c..e204070 100644
--- a/vendor/guzzlehttp/psr7/src/PumpStream.php
+++ b/vendor/guzzlehttp/psr7/src/PumpStream.php
@@ -18,7 +18,7 @@ use Psr\Http\Message\StreamInterface;
  */
 final class PumpStream implements StreamInterface
 {
-    /** @var callable|null */
+    /** @var callable(int): (string|false|null)|null */
     private $source;
 
     /** @var int|null */
@@ -34,7 +34,7 @@ final class PumpStream implements StreamInterface
     private $buffer;
 
     /**
-     * @param callable(int): (string|null|false)  $source  Source of the stream data. The callable MAY
+     * @param callable(int): (string|false|null)  $source  Source of the stream data. The callable MAY
      *                                                     accept an integer argument used to control the
      *                                                     amount of data to return. The callable MUST
      *                                                     return a string when called, or false|null on error
@@ -60,6 +60,7 @@ final class PumpStream implements StreamInterface
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
@@ -149,8 +150,6 @@ final class PumpStream implements StreamInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
@@ -164,11 +163,12 @@ final class PumpStream implements StreamInterface
 
     private function pump(int $length): void
     {
-        if ($this->source) {
+        if ($this->source !== null) {
             do {
-                $data = call_user_func($this->source, $length);
+                $data = ($this->source)($length);
                 if ($data === false || $data === null) {
                     $this->source = null;
+
                     return;
                 }
                 $this->buffer->write($data);
diff --git a/vendor/guzzlehttp/psr7/src/Query.php b/vendor/guzzlehttp/psr7/src/Query.php
index 2faab3a..ccf867a 100644
--- a/vendor/guzzlehttp/psr7/src/Query.php
+++ b/vendor/guzzlehttp/psr7/src/Query.php
@@ -63,12 +63,15 @@ final class Query
      * string. This function does not modify the provided keys when an array is
      * encountered (like `http_build_query()` would).
      *
-     * @param array     $params   Query string parameters.
-     * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
-     *                            to encode using RFC3986, or PHP_QUERY_RFC1738
-     *                            to encode using RFC1738.
+     * @param array     $params           Query string parameters.
+     * @param int|false $encoding         Set to false to not encode,
+     *                                    PHP_QUERY_RFC3986 to encode using
+     *                                    RFC3986, or PHP_QUERY_RFC1738 to
+     *                                    encode using RFC1738.
+     * @param bool      $treatBoolsAsInts Set to true to encode as 0/1, and
+     *                                    false as false/true.
      */
-    public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string
+    public static function build(array $params, $encoding = PHP_QUERY_RFC3986, bool $treatBoolsAsInts = true): string
     {
         if (!$params) {
             return '';
@@ -86,22 +89,24 @@ final class Query
             throw new \InvalidArgumentException('Invalid type');
         }
 
+        $castBool = $treatBoolsAsInts ? static function ($v) { return (int) $v; } : static function ($v) { return $v ? 'true' : 'false'; };
+
         $qs = '';
         foreach ($params as $k => $v) {
             $k = $encoder((string) $k);
             if (!is_array($v)) {
                 $qs .= $k;
-                $v = is_bool($v) ? (int) $v : $v;
+                $v = is_bool($v) ? $castBool($v) : $v;
                 if ($v !== null) {
-                    $qs .= '=' . $encoder((string) $v);
+                    $qs .= '='.$encoder((string) $v);
                 }
                 $qs .= '&';
             } else {
                 foreach ($v as $vv) {
                     $qs .= $k;
-                    $vv = is_bool($vv) ? (int) $vv : $vv;
+                    $vv = is_bool($vv) ? $castBool($vv) : $vv;
                     if ($vv !== null) {
-                        $qs .= '=' . $encoder((string) $vv);
+                        $qs .= '='.$encoder((string) $vv);
                     }
                     $qs .= '&';
                 }
diff --git a/vendor/guzzlehttp/psr7/src/Request.php b/vendor/guzzlehttp/psr7/src/Request.php
index b17af66..faafe1a 100644
--- a/vendor/guzzlehttp/psr7/src/Request.php
+++ b/vendor/guzzlehttp/psr7/src/Request.php
@@ -28,7 +28,7 @@ class Request implements RequestInterface
     /**
      * @param string                               $method  HTTP method
      * @param string|UriInterface                  $uri     URI
-     * @param array<string, string|string[]>       $headers Request headers
+     * @param (string|string[])[]                  $headers Request headers
      * @param string|resource|StreamInterface|null $body    Request body
      * @param string                               $version Protocol version
      */
@@ -69,7 +69,7 @@ class Request implements RequestInterface
             $target = '/';
         }
         if ($this->uri->getQuery() != '') {
-            $target .= '?' . $this->uri->getQuery();
+            $target .= '?'.$this->uri->getQuery();
         }
 
         return $target;
@@ -85,6 +85,7 @@ class Request implements RequestInterface
 
         $new = clone $this;
         $new->requestTarget = $requestTarget;
+
         return $new;
     }
 
@@ -98,6 +99,7 @@ class Request implements RequestInterface
         $this->assertMethod($method);
         $new = clone $this;
         $new->method = strtoupper($method);
+
         return $new;
     }
 
@@ -131,7 +133,7 @@ class Request implements RequestInterface
         }
 
         if (($port = $this->uri->getPort()) !== null) {
-            $host .= ':' . $port;
+            $host .= ':'.$port;
         }
 
         if (isset($this->headerNames['host'])) {
@@ -141,7 +143,7 @@ class Request implements RequestInterface
             $this->headerNames['host'] = 'Host';
         }
         // Ensure Host is the first header.
-        // See: http://tools.ietf.org/html/rfc7230#section-5.4
+        // See: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4
         $this->headers = [$header => [$host]] + $this->headers;
     }
 
diff --git a/vendor/guzzlehttp/psr7/src/Response.php b/vendor/guzzlehttp/psr7/src/Response.php
index 4c6ee6f..34e612f 100644
--- a/vendor/guzzlehttp/psr7/src/Response.php
+++ b/vendor/guzzlehttp/psr7/src/Response.php
@@ -86,7 +86,7 @@ class Response implements ResponseInterface
 
     /**
      * @param int                                  $status  Status code
-     * @param array<string, string|string[]>       $headers Response headers
+     * @param (string|string[])[]                  $headers Response headers
      * @param string|resource|StreamInterface|null $body    Response body
      * @param string                               $version Protocol version
      * @param string|null                          $reason  Reason phrase (when empty a default will be used based on the status code)
@@ -96,7 +96,7 @@ class Response implements ResponseInterface
         array $headers = [],
         $body = null,
         string $version = '1.1',
-        string $reason = null
+        ?string $reason = null
     ) {
         $this->assertStatusCodeRange($status);
 
@@ -138,6 +138,7 @@ class Response implements ResponseInterface
             $reasonPhrase = self::PHRASES[$new->statusCode];
         }
         $new->reasonPhrase = (string) $reasonPhrase;
+
         return $new;
     }
 
diff --git a/vendor/guzzlehttp/psr7/src/Rfc7230.php b/vendor/guzzlehttp/psr7/src/Rfc7230.php
index 3022401..8219dba 100644
--- a/vendor/guzzlehttp/psr7/src/Rfc7230.php
+++ b/vendor/guzzlehttp/psr7/src/Rfc7230.php
@@ -14,7 +14,7 @@ final class Rfc7230
      *
      * Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons.
      *
-     * @link    https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
+     * @see https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
      *
      * @license https://github.com/amphp/http/blob/v1.0.1/LICENSE
      */
diff --git a/vendor/guzzlehttp/psr7/src/ServerRequest.php b/vendor/guzzlehttp/psr7/src/ServerRequest.php
index 43cbb50..3cc9534 100644
--- a/vendor/guzzlehttp/psr7/src/ServerRequest.php
+++ b/vendor/guzzlehttp/psr7/src/ServerRequest.php
@@ -59,7 +59,7 @@ class ServerRequest extends Request implements ServerRequestInterface
     /**
      * @param string                               $method       HTTP method
      * @param string|UriInterface                  $uri          URI
-     * @param array<string, string|string[]>       $headers      Request headers
+     * @param (string|string[])[]                  $headers      Request headers
      * @param string|resource|StreamInterface|null $body         Request body
      * @param string                               $version      Protocol version
      * @param array                                $serverParams Typically the $_SERVER superglobal
@@ -144,10 +144,10 @@ class ServerRequest extends Request implements ServerRequestInterface
         foreach (array_keys($files['tmp_name']) as $key) {
             $spec = [
                 'tmp_name' => $files['tmp_name'][$key],
-                'size'     => $files['size'][$key],
-                'error'    => $files['error'][$key],
-                'name'     => $files['name'][$key],
-                'type'     => $files['type'][$key],
+                'size' => $files['size'][$key] ?? null,
+                'error' => $files['error'][$key] ?? null,
+                'name' => $files['name'][$key] ?? null,
+                'type' => $files['type'][$key] ?? null,
             ];
             $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
         }
@@ -182,7 +182,7 @@ class ServerRequest extends Request implements ServerRequestInterface
 
     private static function extractHostAndPortFromAuthority(string $authority): array
     {
-        $uri = 'http://' . $authority;
+        $uri = 'http://'.$authority;
         $parts = parse_url($uri);
         if (false === $parts) {
             return [null, null];
@@ -286,8 +286,6 @@ class ServerRequest extends Request implements ServerRequestInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return array|object|null
      */
     public function getParsedBody()
@@ -309,8 +307,6 @@ class ServerRequest extends Request implements ServerRequestInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getAttribute($attribute, $default = null)
diff --git a/vendor/guzzlehttp/psr7/src/Stream.php b/vendor/guzzlehttp/psr7/src/Stream.php
index ecd3186..0aff9b2 100644
--- a/vendor/guzzlehttp/psr7/src/Stream.php
+++ b/vendor/guzzlehttp/psr7/src/Stream.php
@@ -12,8 +12,8 @@ use Psr\Http\Message\StreamInterface;
 class Stream implements StreamInterface
 {
     /**
-     * @see http://php.net/manual/function.fopen.php
-     * @see http://php.net/manual/en/function.gzopen.php
+     * @see https://www.php.net/manual/en/function.fopen.php
+     * @see https://www.php.net/manual/en/function.gzopen.php
      */
     private const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/';
     private const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/';
@@ -61,8 +61,8 @@ class Stream implements StreamInterface
         $this->stream = $stream;
         $meta = stream_get_meta_data($this->stream);
         $this->seekable = $meta['seekable'];
-        $this->readable = (bool)preg_match(self::READABLE_MODES, $meta['mode']);
-        $this->writable = (bool)preg_match(self::WRITABLE_MODES, $meta['mode']);
+        $this->readable = (bool) preg_match(self::READABLE_MODES, $meta['mode']);
+        $this->writable = (bool) preg_match(self::WRITABLE_MODES, $meta['mode']);
         $this->uri = $this->getMetadata('uri');
     }
 
@@ -80,12 +80,14 @@ class Stream implements StreamInterface
             if ($this->isSeekable()) {
                 $this->seek(0);
             }
+
             return $this->getContents();
         } catch (\Throwable $e) {
             if (\PHP_VERSION_ID >= 70400) {
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
@@ -145,6 +147,7 @@ class Stream implements StreamInterface
         $stats = fstat($this->stream);
         if (is_array($stats) && isset($stats['size'])) {
             $this->size = $stats['size'];
+
             return $this->size;
         }
 
@@ -207,7 +210,7 @@ class Stream implements StreamInterface
         }
         if (fseek($this->stream, $offset, $whence) === -1) {
             throw new \RuntimeException('Unable to seek to stream position '
-                . $offset . ' with whence ' . var_export($whence, true));
+                .$offset.' with whence '.var_export($whence, true));
         }
     }
 
@@ -261,8 +264,6 @@ class Stream implements StreamInterface
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
diff --git a/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
index 56d4104..601c13a 100644
--- a/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
+++ b/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
@@ -31,6 +31,7 @@ trait StreamDecoratorTrait
     {
         if ($name === 'stream') {
             $this->stream = $this->createStream();
+
             return $this->stream;
         }
 
@@ -43,12 +44,14 @@ trait StreamDecoratorTrait
             if ($this->isSeekable()) {
                 $this->seek(0);
             }
+
             return $this->getContents();
         } catch (\Throwable $e) {
             if (\PHP_VERSION_ID >= 70400) {
                 throw $e;
             }
             trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
+
             return '';
         }
     }
@@ -67,7 +70,7 @@ trait StreamDecoratorTrait
     {
         /** @var callable $callable */
         $callable = [$this->stream, $method];
-        $result = call_user_func_array($callable, $args);
+        $result = ($callable)(...$args);
 
         // Always return the wrapped object if the result is a return $this
         return $result === $this->stream ? $this : $result;
@@ -79,8 +82,6 @@ trait StreamDecoratorTrait
     }
 
     /**
-     * {@inheritdoc}
-     *
      * @return mixed
      */
     public function getMetadata($key = null)
diff --git a/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/vendor/guzzlehttp/psr7/src/StreamWrapper.php
index 2a93464..77b04d7 100644
--- a/vendor/guzzlehttp/psr7/src/StreamWrapper.php
+++ b/vendor/guzzlehttp/psr7/src/StreamWrapper.php
@@ -41,7 +41,7 @@ final class StreamWrapper
             $mode = 'w';
         } else {
             throw new \InvalidArgumentException('The stream must be readable, '
-                . 'writable, or both.');
+                .'writable, or both.');
         }
 
         return fopen('guzzle://stream', $mode, false, self::createStreamContext($stream));
@@ -55,7 +55,7 @@ final class StreamWrapper
     public static function createStreamContext(StreamInterface $stream)
     {
         return stream_context_create([
-            'guzzle' => ['stream' => $stream]
+            'guzzle' => ['stream' => $stream],
         ]);
     }
 
@@ -69,7 +69,7 @@ final class StreamWrapper
         }
     }
 
-    public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool
+    public function stream_open(string $path, string $mode, int $options, ?string &$opened_path = null): bool
     {
         $options = stream_context_get_options($this->context);
 
@@ -115,61 +115,93 @@ final class StreamWrapper
      */
     public function stream_cast(int $cast_as)
     {
-        $stream = clone($this->stream);
+        $stream = clone $this->stream;
         $resource = $stream->detach();
 
         return $resource ?? false;
     }
 
     /**
-     * @return array<int|string, int>
+     * @return array{
+     *   dev: int,
+     *   ino: int,
+     *   mode: int,
+     *   nlink: int,
+     *   uid: int,
+     *   gid: int,
+     *   rdev: int,
+     *   size: int,
+     *   atime: int,
+     *   mtime: int,
+     *   ctime: int,
+     *   blksize: int,
+     *   blocks: int
+     * }|false
      */
-    public function stream_stat(): array
+    public function stream_stat()
     {
+        if ($this->stream->getSize() === null) {
+            return false;
+        }
+
         static $modeMap = [
-            'r'  => 33060,
+            'r' => 33060,
             'rb' => 33060,
             'r+' => 33206,
-            'w'  => 33188,
-            'wb' => 33188
+            'w' => 33188,
+            'wb' => 33188,
         ];
 
         return [
-            'dev'     => 0,
-            'ino'     => 0,
-            'mode'    => $modeMap[$this->mode],
-            'nlink'   => 0,
-            'uid'     => 0,
-            'gid'     => 0,
-            'rdev'    => 0,
-            'size'    => $this->stream->getSize() ?: 0,
-            'atime'   => 0,
-            'mtime'   => 0,
-            'ctime'   => 0,
+            'dev' => 0,
+            'ino' => 0,
+            'mode' => $modeMap[$this->mode],
+            'nlink' => 0,
+            'uid' => 0,
+            'gid' => 0,
+            'rdev' => 0,
+            'size' => $this->stream->getSize() ?: 0,
+            'atime' => 0,
+            'mtime' => 0,
+            'ctime' => 0,
             'blksize' => 0,
-            'blocks'  => 0
+            'blocks' => 0,
         ];
     }
 
     /**
-     * @return array<int|string, int>
+     * @return array{
+     *   dev: int,
+     *   ino: int,
+     *   mode: int,
+     *   nlink: int,
+     *   uid: int,
+     *   gid: int,
+     *   rdev: int,
+     *   size: int,
+     *   atime: int,
+     *   mtime: int,
+     *   ctime: int,
+     *   blksize: int,
+     *   blocks: int
+     * }
      */
     public function url_stat(string $path, int $flags): array
     {
         return [
-            'dev'     => 0,
-            'ino'     => 0,
-            'mode'    => 0,
-            'nlink'   => 0,
-            'uid'     => 0,
-            'gid'     => 0,
-            'rdev'    => 0,
-            'size'    => 0,
-            'atime'   => 0,
-            'mtime'   => 0,
-            'ctime'   => 0,
+            'dev' => 0,
+            'ino' => 0,
+            'mode' => 0,
+            'nlink' => 0,
+            'uid' => 0,
+            'gid' => 0,
+            'rdev' => 0,
+            'size' => 0,
+            'atime' => 0,
+            'mtime' => 0,
+            'ctime' => 0,
             'blksize' => 0,
-            'blocks'  => 0
+            'blocks' => 0,
         ];
     }
 }
diff --git a/vendor/guzzlehttp/psr7/src/UploadedFile.php b/vendor/guzzlehttp/psr7/src/UploadedFile.php
index b1521bc..9c9ea49 100644
--- a/vendor/guzzlehttp/psr7/src/UploadedFile.php
+++ b/vendor/guzzlehttp/psr7/src/UploadedFile.php
@@ -64,8 +64,8 @@ class UploadedFile implements UploadedFileInterface
         $streamOrFile,
         ?int $size,
         int $errorStatus,
-        string $clientFilename = null,
-        string $clientMediaType = null
+        ?string $clientFilename = null,
+        ?string $clientMediaType = null
     ) {
         $this->setError($errorStatus);
         $this->size = $size;
@@ -113,7 +113,7 @@ class UploadedFile implements UploadedFileInterface
         $this->error = $error;
     }
 
-    private function isStringNotEmpty($param): bool
+    private static function isStringNotEmpty($param): bool
     {
         return is_string($param) && false === empty($param);
     }
@@ -163,7 +163,7 @@ class UploadedFile implements UploadedFileInterface
     {
         $this->validateActive();
 
-        if (false === $this->isStringNotEmpty($targetPath)) {
+        if (false === self::isStringNotEmpty($targetPath)) {
             throw new InvalidArgumentException(
                 'Invalid path provided for move operation; must be a non-empty string'
             );
diff --git a/vendor/guzzlehttp/psr7/src/Uri.php b/vendor/guzzlehttp/psr7/src/Uri.php
index 09e878d..481dfca 100644
--- a/vendor/guzzlehttp/psr7/src/Uri.php
+++ b/vendor/guzzlehttp/psr7/src/Uri.php
@@ -25,7 +25,7 @@ class Uri implements UriInterface, \JsonSerializable
     private const HTTP_DEFAULT_HOST = 'localhost';
 
     private const DEFAULT_PORTS = [
-        'http'  => 80,
+        'http' => 80,
         'https' => 443,
         'ftp' => 21,
         'gopher' => 70,
@@ -41,14 +41,14 @@ class Uri implements UriInterface, \JsonSerializable
     /**
      * Unreserved characters for use in a regex.
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-2.3
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
      */
     private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
 
     /**
      * Sub-delims for use in a regex.
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-2.2
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2
      */
     private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
     private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26'];
@@ -87,6 +87,7 @@ class Uri implements UriInterface, \JsonSerializable
             $this->applyParts($parts);
         }
     }
+
     /**
      * UTF-8 aware \parse_url() replacement.
      *
@@ -121,7 +122,7 @@ class Uri implements UriInterface, \JsonSerializable
             $url
         );
 
-        $result = parse_url($prefix . $encodedUrl);
+        $result = parse_url($prefix.$encodedUrl);
 
         if ($result === false) {
             return false;
@@ -161,7 +162,7 @@ class Uri implements UriInterface, \JsonSerializable
      * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
      * that format).
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-5.3
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.3
      */
     public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string
     {
@@ -169,25 +170,25 @@ class Uri implements UriInterface, \JsonSerializable
 
         // weak type checks to also accept null until we can add scalar type hints
         if ($scheme != '') {
-            $uri .= $scheme . ':';
+            $uri .= $scheme.':';
         }
 
         if ($authority != '' || $scheme === 'file') {
-            $uri .= '//' . $authority;
+            $uri .= '//'.$authority;
         }
 
         if ($authority != '' && $path != '' && $path[0] != '/') {
-            $path = '/' . $path;
+            $path = '/'.$path;
         }
 
         $uri .= $path;
 
         if ($query != '') {
-            $uri .= '?' . $query;
+            $uri .= '?'.$query;
         }
 
         if ($fragment != '') {
-            $uri .= '#' . $fragment;
+            $uri .= '#'.$fragment;
         }
 
         return $uri;
@@ -218,7 +219,7 @@ class Uri implements UriInterface, \JsonSerializable
      * @see Uri::isNetworkPathReference
      * @see Uri::isAbsolutePathReference
      * @see Uri::isRelativePathReference
-     * @link https://tools.ietf.org/html/rfc3986#section-4
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4
      */
     public static function isAbsolute(UriInterface $uri): bool
     {
@@ -230,7 +231,7 @@ class Uri implements UriInterface, \JsonSerializable
      *
      * A relative reference that begins with two slash characters is termed an network-path reference.
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-4.2
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
      */
     public static function isNetworkPathReference(UriInterface $uri): bool
     {
@@ -242,7 +243,7 @@ class Uri implements UriInterface, \JsonSerializable
      *
      * A relative reference that begins with a single slash character is termed an absolute-path reference.
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-4.2
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
      */
     public static function isAbsolutePathReference(UriInterface $uri): bool
     {
@@ -257,7 +258,7 @@ class Uri implements UriInterface, \JsonSerializable
      *
      * A relative reference that does not begin with a slash character is termed a relative-path reference.
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-4.2
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
      */
     public static function isRelativePathReference(UriInterface $uri): bool
     {
@@ -276,9 +277,9 @@ class Uri implements UriInterface, \JsonSerializable
      * @param UriInterface      $uri  The URI to check
      * @param UriInterface|null $base An optional base URI to compare against
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-4.4
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4
      */
-    public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool
+    public static function isSameDocumentReference(UriInterface $uri, ?UriInterface $base = null): bool
     {
         if ($base !== null) {
             $uri = UriResolver::resolve($base, $uri);
@@ -335,8 +336,8 @@ class Uri implements UriInterface, \JsonSerializable
      *
      * It has the same behavior as withQueryValue() but for an associative array of key => value.
      *
-     * @param UriInterface               $uri           URI to use as a base.
-     * @param array<string, string|null> $keyValueArray Associative array of key and values
+     * @param UriInterface    $uri           URI to use as a base.
+     * @param (string|null)[] $keyValueArray Associative array of key and values
      */
     public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface
     {
@@ -352,7 +353,7 @@ class Uri implements UriInterface, \JsonSerializable
     /**
      * Creates a URI from a hash of `parse_url` components.
      *
-     * @link http://php.net/manual/en/function.parse-url.php
+     * @see https://www.php.net/manual/en/function.parse-url.php
      *
      * @throws MalformedUriException If the components do not form a valid URI.
      */
@@ -374,11 +375,11 @@ class Uri implements UriInterface, \JsonSerializable
     {
         $authority = $this->host;
         if ($this->userInfo !== '') {
-            $authority = $this->userInfo . '@' . $authority;
+            $authority = $this->userInfo.'@'.$authority;
         }
 
         if ($this->port !== null) {
-            $authority .= ':' . $this->port;
+            $authority .= ':'.$this->port;
         }
 
         return $authority;
@@ -435,7 +436,7 @@ class Uri implements UriInterface, \JsonSerializable
     {
         $info = $this->filterUserInfoComponent($user);
         if ($password !== null) {
-            $info .= ':' . $this->filterUserInfoComponent($password);
+            $info .= ':'.$this->filterUserInfoComponent($password);
         }
 
         if ($this->userInfo === $info) {
@@ -563,7 +564,7 @@ class Uri implements UriInterface, \JsonSerializable
             ? $this->filterQueryAndFragment($parts['fragment'])
             : '';
         if (isset($parts['pass'])) {
-            $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']);
+            $this->userInfo .= ':'.$this->filterUserInfoComponent($parts['pass']);
         }
 
         $this->removeDefaultPort();
@@ -595,7 +596,7 @@ class Uri implements UriInterface, \JsonSerializable
         }
 
         return preg_replace_callback(
-            '/(?:[^%' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/',
+            '/(?:[^%'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/',
             [$this, 'rawurlencodeMatchZero'],
             $component
         );
@@ -627,7 +628,7 @@ class Uri implements UriInterface, \JsonSerializable
         }
 
         $port = (int) $port;
-        if (0 > $port || 0xffff < $port) {
+        if (0 > $port || 0xFFFF < $port) {
             throw new \InvalidArgumentException(
                 sprintf('Invalid port: %d. Must be between 0 and 65535', $port)
             );
@@ -637,7 +638,7 @@ class Uri implements UriInterface, \JsonSerializable
     }
 
     /**
-     * @param string[] $keys
+     * @param (string|int)[] $keys
      *
      * @return string[]
      */
@@ -649,7 +650,9 @@ class Uri implements UriInterface, \JsonSerializable
             return [];
         }
 
-        $decodedKeys = array_map('rawurldecode', $keys);
+        $decodedKeys = array_map(function ($k): string {
+            return rawurldecode((string) $k);
+        }, $keys);
 
         return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
             return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
@@ -664,7 +667,7 @@ class Uri implements UriInterface, \JsonSerializable
         $queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT);
 
         if ($value !== null) {
-            $queryString .= '=' . strtr($value, self::QUERY_SEPARATORS_REPLACEMENT);
+            $queryString .= '='.strtr($value, self::QUERY_SEPARATORS_REPLACEMENT);
         }
 
         return $queryString;
@@ -691,7 +694,7 @@ class Uri implements UriInterface, \JsonSerializable
         }
 
         return preg_replace_callback(
-            '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
+            '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
             [$this, 'rawurlencodeMatchZero'],
             $path
         );
@@ -711,7 +714,7 @@ class Uri implements UriInterface, \JsonSerializable
         }
 
         return preg_replace_callback(
-            '/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
+            '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
             [$this, 'rawurlencodeMatchZero'],
             $str
         );
diff --git a/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/vendor/guzzlehttp/psr7/src/UriNormalizer.php
index e12971e..e174557 100644
--- a/vendor/guzzlehttp/psr7/src/UriNormalizer.php
+++ b/vendor/guzzlehttp/psr7/src/UriNormalizer.php
@@ -11,7 +11,7 @@ use Psr\Http\Message\UriInterface;
  *
  * @author Tobias Schultze
  *
- * @link https://tools.ietf.org/html/rfc3986#section-6
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6
  */
 final class UriNormalizer
 {
@@ -119,7 +119,7 @@ final class UriNormalizer
      * @param UriInterface $uri   The URI to normalize
      * @param int          $flags A bitmask of normalizations to apply, see constants
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-6.2
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.2
      */
     public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface
     {
@@ -131,8 +131,8 @@ final class UriNormalizer
             $uri = self::decodeUnreservedCharacters($uri);
         }
 
-        if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' &&
-            ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
+        if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === ''
+            && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
         ) {
             $uri = $uri->withPath('/');
         }
@@ -174,7 +174,7 @@ final class UriNormalizer
      * @param UriInterface $uri2           An URI to compare
      * @param int          $normalizations A bitmask of normalizations to apply, see constants
      *
-     * @link https://tools.ietf.org/html/rfc3986#section-6.1
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.1
      */
     public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool
     {
@@ -185,7 +185,7 @@ final class UriNormalizer
     {
         $regex = '/(?:%[A-Fa-f0-9]{2})++/';
 
-        $callback = function (array $match) {
+        $callback = function (array $match): string {
             return strtoupper($match[0]);
         };
 
@@ -201,7 +201,7 @@ final class UriNormalizer
     {
         $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
 
-        $callback = function (array $match) {
+        $callback = function (array $match): string {
             return rawurldecode($match[0]);
         };
 
diff --git a/vendor/guzzlehttp/psr7/src/UriResolver.php b/vendor/guzzlehttp/psr7/src/UriResolver.php
index 426e5c9..3737be1 100644
--- a/vendor/guzzlehttp/psr7/src/UriResolver.php
+++ b/vendor/guzzlehttp/psr7/src/UriResolver.php
@@ -11,14 +11,14 @@ use Psr\Http\Message\UriInterface;
  *
  * @author Tobias Schultze
  *
- * @link https://tools.ietf.org/html/rfc3986#section-5
+ * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5
  */
 final class UriResolver
 {
     /**
      * Removes dot segments from a path and returns the new path.
      *
-     * @link http://tools.ietf.org/html/rfc3986#section-5.2.4
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
      */
     public static function removeDotSegments(string $path): string
     {
@@ -40,7 +40,7 @@ final class UriResolver
 
         if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {
             // Re-add the leading slash if necessary for cases like "/.."
-            $newPath = '/' . $newPath;
+            $newPath = '/'.$newPath;
         } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
             // Add the trailing slash if necessary
             // If newPath is not empty, then $segment must be set and is the last segment from the foreach
@@ -53,7 +53,7 @@ final class UriResolver
     /**
      * Converts the relative URI into a new URI that is resolved against the base URI.
      *
-     * @link http://tools.ietf.org/html/rfc3986#section-5.2
+     * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.2
      */
     public static function resolve(UriInterface $base, UriInterface $rel): UriInterface
     {
@@ -80,13 +80,13 @@ final class UriResolver
                     $targetPath = $rel->getPath();
                 } else {
                     if ($targetAuthority != '' && $base->getPath() === '') {
-                        $targetPath = '/' . $rel->getPath();
+                        $targetPath = '/'.$rel->getPath();
                     } else {
                         $lastSlashPos = strrpos($base->getPath(), '/');
                         if ($lastSlashPos === false) {
                             $targetPath = $rel->getPath();
                         } else {
-                            $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath();
+                            $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1).$rel->getPath();
                         }
                     }
                 }
@@ -127,8 +127,8 @@ final class UriResolver
      */
     public static function relativize(UriInterface $base, UriInterface $target): UriInterface
     {
-        if ($target->getScheme() !== '' &&
-            ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
+        if ($target->getScheme() !== ''
+            && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
         ) {
             return $target;
         }
@@ -185,7 +185,7 @@ final class UriResolver
             }
         }
         $targetSegments[] = $targetLastSegment;
-        $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments);
+        $relativePath = str_repeat('../', count($sourceSegments)).implode('/', $targetSegments);
 
         // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
         // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
diff --git a/vendor/guzzlehttp/psr7/src/Utils.php b/vendor/guzzlehttp/psr7/src/Utils.php
index 3a4cf39..7682d2c 100644
--- a/vendor/guzzlehttp/psr7/src/Utils.php
+++ b/vendor/guzzlehttp/psr7/src/Utils.php
@@ -14,18 +14,18 @@ final class Utils
     /**
      * Remove the items given by the keys, case insensitively from the data.
      *
-     * @param string[] $keys
+     * @param (string|int)[] $keys
      */
     public static function caselessRemove(array $keys, array $data): array
     {
         $result = [];
 
         foreach ($keys as &$key) {
-            $key = strtolower($key);
+            $key = strtolower((string) $key);
         }
 
         foreach ($data as $k => $v) {
-            if (!is_string($k) || !in_array(strtolower($k), $keys)) {
+            if (!in_array(strtolower((string) $k), $keys)) {
                 $result[$k] = $v;
             }
         }
@@ -90,6 +90,7 @@ final class Utils
                 }
                 $buffer .= $buf;
             }
+
             return $buffer;
         }
 
@@ -174,7 +175,7 @@ final class Utils
                     $standardPorts = ['http' => 80, 'https' => 443];
                     $scheme = $changes['uri']->getScheme();
                     if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
-                        $changes['set_headers']['Host'] .= ':' . $port;
+                        $changes['set_headers']['Host'] .= ':'.$port;
                     }
                 }
             }
@@ -249,6 +250,20 @@ final class Utils
         return $buffer;
     }
 
+    /**
+     * Redact the password in the user info part of a URI.
+     */
+    public static function redactUserInfo(UriInterface $uri): UriInterface
+    {
+        $userInfo = $uri->getUserInfo();
+
+        if (false !== ($pos = \strpos($userInfo, ':'))) {
+            return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***');
+        }
+
+        return $uri;
+    }
+
     /**
      * Create a new stream based on the input type.
      *
@@ -291,6 +306,7 @@ final class Utils
                 fwrite($stream, (string) $resource);
                 fseek($stream, 0);
             }
+
             return new Stream($stream, $options);
         }
 
@@ -308,6 +324,7 @@ final class Utils
                     fseek($stream, 0);
                     $resource = $stream;
                 }
+
                 return new Stream($resource, $options);
             case 'object':
                 /** @var object $resource */
@@ -320,6 +337,7 @@ final class Utils
                         }
                         $result = $resource->current();
                         $resource->next();
+
                         return $result;
                     }, $options);
                 } elseif (method_exists($resource, '__toString')) {
@@ -334,7 +352,7 @@ final class Utils
             return new PumpStream($resource, $options);
         }
 
-        throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
+        throw new \InvalidArgumentException('Invalid resource type: '.gettype($resource));
     }
 
     /**
diff --git a/vendor/maennchen/zipstream-php/.editorconfig b/vendor/maennchen/zipstream-php/.editorconfig
new file mode 100644
index 0000000..f7cd914
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/.editorconfig
@@ -0,0 +1,22 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+
+[*.{yml,md,xml}]
+indent_style = space
+indent_size = 2
+
+[*.{rst,php}]
+indent_style = space
+indent_size = 4
+
+[composer.json]
+indent_style = space
+indent_size = 2
+
+[composer.lock]
+indent_style = space
+indent_size = 4
diff --git a/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php b/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
index 3ba86a4..b978f06 100644
--- a/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
+++ b/vendor/maennchen/zipstream-php/.php-cs-fixer.dist.php
@@ -26,7 +26,7 @@ $config = new Config();
 return $config->setRules([
         '@PER' => true,
         '@PER:risky' => true,
-        '@PHP81Migration' => true,
+        '@PHP82Migration' => true,
         '@PHPUnit84Migration:risky' => true,
         'array_syntax' => ['syntax' => 'short'],
         'class_attributes_separation' => true,
@@ -68,4 +68,4 @@ return $config->setRules([
         ],
     ])
     ->setFinder($finder)
-    ->setRiskyAllowed(true);
\ No newline at end of file
+    ->setRiskyAllowed(true);
diff --git a/vendor/maennchen/zipstream-php/.tool-versions b/vendor/maennchen/zipstream-php/.tool-versions
index 18e9535..2618178 100644
--- a/vendor/maennchen/zipstream-php/.tool-versions
+++ b/vendor/maennchen/zipstream-php/.tool-versions
@@ -1 +1 @@
-php 8.1.13
+php 8.2.5
diff --git a/vendor/maennchen/zipstream-php/README.md b/vendor/maennchen/zipstream-php/README.md
index 155a265..8ebb2c3 100644
--- a/vendor/maennchen/zipstream-php/README.md
+++ b/vendor/maennchen/zipstream-php/README.md
@@ -14,13 +14,17 @@ version.
 
 ## Overview
 
-A fast and simple streaming zip file downloader for PHP. Using this library will save you from having to write the Zip to disk. You can directly send it to the user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
+A fast and simple streaming zip file downloader for PHP. Using this library will
+save you from having to write the Zip to disk. You can directly send it to the
+user, which is much faster. It can work with S3 buckets or any PSR7 Stream.
 
 Please see the [LICENSE](LICENSE) file for licensing and warranty information.
 
 ## Installation
 
-Simply add a dependency on maennchen/zipstream-php to your project's composer.json file if you use Composer to manage the dependencies of your project. Use following command to add the package to your project's dependencies:
+Simply add a dependency on maennchen/zipstream-php to your project's
+`composer.json` file if you use Composer to manage the dependencies of your
+project. Use following command to add the package to your project's dependencies:
 
 ```bash
 composer require maennchen/zipstream-php
@@ -29,51 +33,116 @@ composer require maennchen/zipstream-php
 ## Usage
 
 For detailed instructions, please check the
-[Documentation](https://maennchen.dev/ZipStream-PHP/).
-
-Here's a simple example:
+[Documentation](https://maennchen.github.io/ZipStream-PHP/).
 
 ```php
 // Autoload the dependencies
 require 'vendor/autoload.php';
 
-// enable output of HTTP headers
-$options = new ZipStream\Option\Archive();
-$options->setSendHttpHeaders(true);
-
 // create a new zipstream object
-$zip = new ZipStream\ZipStream('example.zip', $options);
+$zip = new ZipStream\ZipStream(
+    outputName: 'example.zip',
+
+    // enable output of HTTP headers
+    sendHttpHeaders: true,
+);
 
 // create a file named 'hello.txt'
-$zip->addFile('hello.txt', 'This is the contents of hello.txt');
+$zip->addFile(
+    fileName: 'hello.txt',
+    data: 'This is the contents of hello.txt',
+);
 
 // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
-$zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
+$zip->addFileFromPath(
+    fileName: 'some_image.jpg',
+    path: 'path/to/image.jpg',
+);
 
 // finish the zip stream
 $zip->finish();
 ```
 
+## Upgrade to version 3.0.0
+
+### General
+
+- Minimum PHP Version: `8.1`
+- Only 64bit Architecture is supported.
+- The class `ZipStream\Option\Method` has been replaced with the enum
+  `ZipStream\CompressionMethod`.
+- Most clases have been flagged as `@internal` and should not be used from the
+  outside.
+  If you're using internal resources to extend this library, please open an
+  issue so that a clean interface can be added & published.
+  The externally available classes & enums are:
+  - `ZipStream\CompressionMethod`
+  - `ZipStream\Exception*`
+  - `ZipStream\ZipStream`
+
+### Archive Options
+
+- The class `ZipStream\Option\Archive` has been replaced in favor of named
+  arguments in the `ZipStream\ZipStream` constuctor.
+- The archive options `largeFileSize` & `largeFileMethod` has been removed. If
+  you want different `compressionMethods` based on the file size, you'll have to
+  implement this yourself.
+- The archive option `httpHeaderCallback` changed the type from `callable` to
+  `Closure`.
+- The archive option `zeroHeader` has been replaced with the option
+  `defaultEnableZeroHeader` and can be overridden for every file. Its default
+  value changed from `false` to `true`.
+- The archive option `statFiles` was removed since the library no longer checks
+  filesizes this way.
+- The archive option `deflateLevel` has been replaced with the option
+  `defaultDeflateLevel` and can be overridden for every file.
+- The first argument (`name`) of the `ZipStream\ZipStream` constuctor has been
+  replaced with the named argument `outputName`.
+- Headers are now also sent if the `outputName` is empty. If you do not want to
+  automatically send http headers, set `sendHttpHeaders` to `false`.
+
+### File Options
+
+- The class `ZipStream\Option\File` has been replaced in favor of named
+  arguments in the `ZipStream\ZipStream->addFile*` functions.
+- The file option `method` has been renamed to `compressionMethod`.
+- The file option `time` has been renamed to `lastModificationDateTime`.
+- The file option `size` has been renamed to `maxSize`.
+
 ## Upgrade to version 2.0.0
 
-- Only the self opened streams will be closed (#139)
-  If you were relying on ZipStream to close streams that the library didn't open,
-  you'll need to close them yourself now.
+https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-200
 
 ## Upgrade to version 1.0.0
 
-- All options parameters to all function have been moved from an `array` to structured option objects. See [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Available-options) for examples.
-- The whole library has been refactored. The minimal PHP requirement has been raised to PHP 7.1.
-
-## Usage with Symfony and S3
-
-You can find example code on [the wiki](https://github.com/maennchen/ZipStream-PHP/wiki/Symfony-example).
+https://github.com/maennchen/ZipStream-PHP/tree/2.0.0#upgrade-to-version-100
 
 ## Contributing
 
 ZipStream-PHP is a collaborative project. Please take a look at the
 [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) file.
 
+## Version Support
+
+Versions are supported according to the table below.
+
+Please do not open any pull requests contradicting the current version support
+status.
+
+Careful: Always check the `README` on `main` for up-to-date information.
+
+| Version | New Features | Bugfixes | Security |
+|---------|--------------|----------|----------|
+| *3*     | ✓            | ✓        | ✓        |
+| *2*     | ✗            | ✓        | ✓        |
+| *1*     | ✗            | ✗        | ✓        |
+| *0*     | ✗            | ✗        | ✗        |
+
+This library aligns itself with the PHP core support. New features and bugfixes
+will only target PHP versions according to their current status.
+
+See: https://www.php.net/supported-versions.php
+
 ## About the Authors
 
 - Paul Duncan <pabs@pablotron.org> - https://pablotron.org/
diff --git a/vendor/maennchen/zipstream-php/composer.json b/vendor/maennchen/zipstream-php/composer.json
index 47eee14..98c536a 100644
--- a/vendor/maennchen/zipstream-php/composer.json
+++ b/vendor/maennchen/zipstream-php/composer.json
@@ -22,27 +22,36 @@
     }
   ],
   "require": {
-    "php": "^7.4 || ^8.0",
-    "symfony/polyfill-mbstring": "^1.0",
-    "psr/http-message": "^1.0",
-    "myclabs/php-enum": "^1.5"
+    "php-64bit": "^8.1",
+    "ext-mbstring": "*",
+    "ext-zlib": "*"
   },
   "require-dev": {
-    "phpunit/phpunit": "^8.5.8 || ^9.4.2",
-    "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+    "phpunit/phpunit": "^10.0",
+    "guzzlehttp/guzzle": "^7.5",
     "ext-zip": "*",
     "mikey179/vfsstream": "^1.6",
-    "vimeo/psalm": "^4.1",
-    "php-coveralls/php-coveralls": "^2.4",
-    "friendsofphp/php-cs-fixer": "^3.9"
+    "php-coveralls/php-coveralls": "^2.5",
+    "friendsofphp/php-cs-fixer": "^3.16",
+    "vimeo/psalm": "^5.0"
+  },
+  "suggest": {
+    "psr/http-message": "^2.0",
+    "guzzlehttp/psr7": "^2.4"
   },
   "scripts": {
     "format": "php-cs-fixer fix",
-    "test": "composer run test:unit && composer run test:formatted && composer run test:lint",
-    "test:unit": "phpunit --coverage-clover=coverage.clover.xml",
-    "test:formatted": "composer run format -- --dry-run --stop-on-violation --using-cache=no",
-    "test:lint": "psalm --stats --show-info --find-unused-psalm-suppress",
-    "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json -v",
+    "test": [
+      "@test:unit",
+      "@test:formatted",
+      "@test:lint"
+    ],
+    "test:unit": "phpunit --coverage-clover=coverage.clover.xml --coverage-html cov",
+    "test:unit:slow": "@test:unit --group slow",
+    "test:unit:fast": "@test:unit --exclude-group slow",
+    "test:formatted": "@format --dry-run --stop-on-violation --using-cache=no",
+    "test:lint": "psalm --stats --show-info=true --find-unused-psalm-suppress",
+    "coverage:report": "php-coveralls --coverage_clover=coverage.clover.xml --json_path=coveralls-upload.json --insecure",
     "install:tools": "phive install --trust-gpg-keys 0x67F861C3D889C656",
     "docs:generate": "tools/phpdocumentor --sourcecode"
   },
@@ -51,6 +60,9 @@
       "ZipStream\\": "src/"
     }
   },
+  "autoload-dev": {
+    "psr-4": { "ZipStream\\Test\\": "test/" }
+  },
   "archive": {
     "exclude": [
       "/composer.lock",
diff --git a/vendor/maennchen/zipstream-php/guides/ContentLength.rst b/vendor/maennchen/zipstream-php/guides/ContentLength.rst
index e51e692..21fea34 100644
--- a/vendor/maennchen/zipstream-php/guides/ContentLength.rst
+++ b/vendor/maennchen/zipstream-php/guides/ContentLength.rst
@@ -1,79 +1,47 @@
 Adding Content-Length header
 =============
 
-Adding a ``Content-Length`` header for ``ZipStream`` is not trivial since the
-size is not known beforehand.
+Adding a ``Content-Length`` header for ``ZipStream`` can be achieved by
+using the options ``SIMULATION_STRICT`` or ``SIMULATION_LAX`` in the
+``operationMode`` parameter.
 
-The following workaround adds an approximated header:
+In the ``SIMULATION_STRICT`` mode, ``ZipStream`` will not allow to calculate the
+size based on reading the whole file. ``SIMULATION_LAX`` will read the whole
+file if neccessary.
+
+``SIMULATION_STRICT`` is therefore useful to make sure that the size can be
+calculated efficiently.
 
 .. code-block:: php
+    use ZipStream\OperationMode;
+    use ZipStream\ZipStream;
 
-    class Zip
-    {
-        /** @var string */
-        private $name;
+    $zip = new ZipStream(
+        operationMode: OperationMode::SIMULATE_STRICT, // or SIMULATE_LAX
+        defaultEnableZeroHeader: false,
+        sendHttpHeaders: true,
+        outputStream: $stream,
+    );
 
-        private $files = [];
+    // Normally add files
+    $zip->addFile('sample.txt', 'Sample String Data');
 
-        public function __construct($name)
-        {
-            $this->name = $name;
+    // Use addFileFromCallback and exactSize if you want to defer opening of
+    // the file resource
+    $zip->addFileFromCallback(
+        'sample.txt',
+        exactSize: 18,
+        callback: function () {
+            return fopen('...');
         }
+    );
 
-        public function addFile($name, $data)
-        {
-            $this->files[] = ['type' => 'addFile', 'name' => $name, 'data' => $data];
-        }
+    // Read resulting file size
+    $size = $zip->finish();
+    
+    // Tell it to the browser
+    header('Content-Length: '. $size);
+    
+    // Execute the Simulation and stream the actual zip to the client
+    $zip->executeSimulation();
 
-        public function addFileFromPath($name, $path)
-        {
-            $this->files[] = ['type' => 'addFileFromPath', 'name' => $name, 'path' => $path];
-        }
-
-        public function getEstimate()
-        {
-            $estimate = 22;
-            foreach ($this->files as $file) {
-            $estimate += 76 + 2 * strlen($file['name']);
-            if ($file['type'] === 'addFile') {
-                $estimate += strlen($file['data']);
-            }
-            if ($file['type'] === 'addFileFromPath') {
-                $estimate += filesize($file['path']);
-            }
-            }
-            return $estimate;
-        }
-
-        public function finish()
-        {
-            header('Content-Length: ' . $this->getEstimate());
-            $options = new \ZipStream\Option\Archive();
-            $options->setSendHttpHeaders(true);
-            $options->setEnableZip64(false);
-            $options->setDeflateLevel(-1);
-            $zip = new \ZipStream\ZipStream($this->name, $options);
-
-            $fileOptions = new \ZipStream\Option\File();
-            $fileOptions->setMethod(\ZipStream\Option\Method::STORE());
-            foreach ($this->files as $file) {
-            if ($file['type'] === 'addFile') {
-                $zip->addFile($file['name'], $file['data'], $fileOptions);
-            }
-            if ($file['type'] === 'addFileFromPath') {
-                $zip->addFileFromPath($file['name'], $file['path'], $fileOptions);
-            }
-            }
-            $zip->finish();
-            exit;
-        }
-    }
-
-It only works with the following constraints:
-
-- All file content is known beforehand.
-- Content Deflation is disabled
-
-Thanks to
-`partiellkorrekt <https://github.com/maennchen/ZipStream-PHP/issues/89#issuecomment-1047949274>`_
-for this workaround.
\ No newline at end of file
diff --git a/vendor/maennchen/zipstream-php/guides/FlySystem.rst b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
index 0243f24..4e6c6fb 100644
--- a/vendor/maennchen/zipstream-php/guides/FlySystem.rst
+++ b/vendor/maennchen/zipstream-php/guides/FlySystem.rst
@@ -14,20 +14,21 @@ default one, and pass it to Flysystem ``putStream`` method.
     // the content is lost when closing the stream / opening another one
     $tempStream = fopen('php://memory', 'w+');
 
-    // Init Options
-    $zipStreamOptions = new Archive();
-    $zipStreamOptions->setOutputStream($tempStream);
-
     // Create Zip Archive
-    $zipStream = new ZipStream('test.zip', $zipStreamOptions);
+    $zipStream = new ZipStream(
+        outputStream: $tempStream,
+        outputName: 'test.zip',
+    );
     $zipStream->addFile('test.txt', 'text');
     $zipStream->finish();
 
-    // Store File (see Flysystem documentation, and all its framework integration)
-    $adapter = new Local(__DIR__.'/path/to/folder'); // Can be any adapter (AWS, Google, Ftp, etc.)
+    // Store File
+    // (see Flysystem documentation, and all its framework integration)
+    // Can be any adapter (AWS, Google, Ftp, etc.)
+    $adapter = new Local(__DIR__.'/path/to/folder');
     $filesystem = new Filesystem($adapter);
 
-    $filesystem->putStream('test.zip', $tempStream)
+    $filesystem->writeStream('test.zip', $tempStream)
 
     // Close Stream
-    fclose($tempStream);
\ No newline at end of file
+    fclose($tempStream);
diff --git a/vendor/maennchen/zipstream-php/guides/Options.rst b/vendor/maennchen/zipstream-php/guides/Options.rst
index eabaa6f..5e92e94 100644
--- a/vendor/maennchen/zipstream-php/guides/Options.rst
+++ b/vendor/maennchen/zipstream-php/guides/Options.rst
@@ -2,60 +2,65 @@ Available options
 ===============
 
 Here is the full list of options available to you. You can also have a look at
-``src/Option/Archive.php`` file.
-
-First, an instance of ``ZipStream\Option\Archive`` needs to be created, and
-after that you use setters methods to modify the values.
+``src/ZipStream.php`` file.
 
 .. code-block:: php
+
     use ZipStream\ZipStream;
-    use ZipStream\Option\Archive as ArchiveOptions;
 
     require_once 'vendor/autoload.php';
 
-    $opt = new ArchiveOptions();
+    $zip = new ZipStream(
+        // Define output stream
+        // (argument is eiter a resource or implementing
+        // `Psr\Http\Message\StreamInterface`)
+        //
+        // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies
+        // required when using `Psr\Http\Message\StreamInterface`.
+        outputStream: $filePointer,
 
-    // Define output stream (argument is of type resource)
-    $opt->setOutputStream($fd);
+        // Set the deflate level (default is 6; use -1 to disable it)
+        defaultDeflateLevel: 6,
 
-    // Set the deflate level (default is 6; use -1 to disable it)
-    $opt->setDeflateLevel(6);
+        // Add a comment to the zip file
+        comment: 'This is a comment.',
 
-    // Add a comment to the zip file
-    $opt->setComment('This is a comment.');
+        // Send http headers (default is true)
+        sendHttpHeaders: false,
 
-    // Size, in bytes, of the largest file to try and load into memory (used by addFileFromPath()).  Large files may also be compressed differently; see the 'largeFileMethod' option.
-    $opt->setLargeFileSize(30000000);
+        // HTTP Content-Disposition.
+        // Defaults to 'attachment', where FILENAME is the specified filename.
+        // Note that this does nothing if you are not sending HTTP headers.
+        contentDisposition: 'attachment',
 
-    // How to handle large files.  Legal values are STORE (the default), or DEFLATE. Store sends the file raw and is significantly faster, while DEFLATE compresses the file and is much, much slower. Note that deflate must compress the file twice and is extremely slow.
-    $opt->setLargeFileMethod(ZipStream\Option\Method::STORE());
-    $opt->setLargeFileMethod(ZipStream\Option\Method::DEFLATE());
+        // Output Name for HTTP Content-Disposition
+        // Defaults to no name
+        outputName: "example.zip",
 
-    // Send http headers (default is false)
-    $opt->setSendHttpHeaders(false);
+        // HTTP Content-Type.
+        // Defaults to 'application/x-zip'.
+        // Note that this does nothing if you are not sending HTTP headers.
+        contentType: 'application/x-zip',
 
-    // HTTP Content-Disposition.  Defaults to 'attachment', where FILENAME is the specified filename. Note that this does nothing if you are not sending HTTP headers.
-    $opt->setContentDisposition('attachment');
+        // Set the function called for setting headers.
+        // Default is the `header()` of PHP
+        httpHeaderCallback: header(...),
 
-    // Set the content type (does nothing if you are not sending HTTP headers)
-    $opt->setContentType('application/x-zip');
+        // Enable streaming files with single read where general purpose bit 3
+        // indicates local file header contain zero values in crc and size
+        // fields, these appear only after file contents in data descriptor
+        // block.
+        // Set to true if your input stream is remote
+        // (used with addFileFromStream()).
+        // Default is false.
+        defaultEnableZeroHeader: false,
 
-    // Set the function called for setting headers. Default is the `header()` of PHP
-    $opt->setHttpHeaderCallback('header');
+        // Enable zip64 extension, allowing very large archives
+        // (> 4Gb or file count > 64k)
+        // Default is true
+        enableZip64: true,
 
-    // Enable streaming files with single read where general purpose bit 3 indicates local file header contain zero values in crc and size fields, these appear only after file contents in data descriptor block. Default is false. Set to true if your input stream is remote (used with addFileFromStream()).
-    $opt->setZeroHeader(false);
-
-    // Enable reading file stat for determining file size. When a 32-bit system reads file size that is over 2 GB, invalid value appears in file size due to integer overflow. Should be disabled on 32-bit systems with method addFileFromPath if any file may exceed 2 GB. In this case file will be read in blocks and correct size will be determined from content. Default is true.
-    $opt->setStatFiles(true);
-
-    // Enable zip64 extension, allowing very large archives (> 4Gb or file count > 64k)
-    // default is true
-    $opt->setEnableZip64(true);
-
-    // Flush output buffer after every write
-    // default is false
-    $opt->setFlushOutput(true);
-
-    // Now that everything is set you can pass the options to the ZipStream instance
-    $zip = new ZipStream('example.zip', $opt);
+        // Flush output buffer after every write
+        // Default is false
+        flushOutput: true,
+    );
diff --git a/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
index 4b4ca4b..22af71d 100644
--- a/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
+++ b/vendor/maennchen/zipstream-php/guides/PSR7Streams.rst
@@ -12,7 +12,10 @@ Example
 ---------------
 
 .. code-block:: php
-    
+
     $stream = $response->getBody();
     // add a file named 'streamfile.txt' from the content of the stream
-    $zip->addFileFromPsr7Stream('streamfile.txt', $stream);
+    $zip->addFileFromPsr7Stream(
+        fileName: 'streamfile.txt',
+        stream: $stream,
+    );
diff --git a/vendor/maennchen/zipstream-php/guides/StreamOutput.rst b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
index 1a0495f..9f3165b 100644
--- a/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
+++ b/vendor/maennchen/zipstream-php/guides/StreamOutput.rst
@@ -5,9 +5,9 @@ Stream to S3 Bucket
 ---------------
 
 .. code-block:: php
+
     use Aws\S3\S3Client;
     use Aws\Credentials\CredentialProvider;
-    use ZipStream\Option\Archive;
     use ZipStream\ZipStream;
 
     $bucket = 'your bucket name';
@@ -21,13 +21,19 @@ Stream to S3 Bucket
 
     $zipFile = fopen("s3://$bucket/example.zip", 'w');
 
-    $options = new Archive();
-    $options->setEnableZip64(false);
-    $options->setOutputStream($zipFile);
+    $zip = new ZipStream(
+        enableZip64: false,
+        outputStream: $zipFile,
+    );
 
-    $zip = new ZipStream(null, $options);
-    $zip->addFile('file1.txt', 'File1 data');
-    $zip->addFile('file2.txt', 'File2 data');
+    $zip->addFile(
+        fileName: 'file1.txt',
+        data: 'File1 data',
+    );
+    $zip->addFile(
+        fileName: 'file2.txt',
+        data: 'File2 data',
+    );
     $zip->finish();
 
-    fclose($zipFile);
\ No newline at end of file
+    fclose($zipFile);
diff --git a/vendor/maennchen/zipstream-php/guides/Symfony.rst b/vendor/maennchen/zipstream-php/guides/Symfony.rst
index 18f9059..902552c 100644
--- a/vendor/maennchen/zipstream-php/guides/Symfony.rst
+++ b/vendor/maennchen/zipstream-php/guides/Symfony.rst
@@ -31,7 +31,7 @@ stored in an AWS S3 bucket by key:
     */
     public function zipStreamAction()
     {
-        //sample test file on s3
+        // sample test file on s3
         $s3keys = array(
         "ziptestfolder/file1.txt"
         );
@@ -39,18 +39,18 @@ stored in an AWS S3 bucket by key:
         $s3Client = $this->get('app.amazon.s3'); //s3client service
         $s3Client->registerStreamWrapper(); //required
 
-        //using StreamedResponse to wrap ZipStream functionality for files on AWS s3.
+        // using StreamedResponse to wrap ZipStream functionality
+        // for files on AWS s3.
         $response = new StreamedResponse(function() use($s3keys, $s3Client)
         {
             // Define suitable options for ZipStream Archive.
-            $options = new \ZipStream\Option\Archive();
-            $options->setContentType('application/octet-stream');
             // this is needed to prevent issues with truncated zip files
-            $options->setZeroHeader(true);
-            $options->setComment('test zip file.');
-
             //initialise zipstream with output zip filename and options.
-            $zip = new ZipStream\ZipStream('test.zip', $options);
+            $zip = new ZipStream\ZipStream(
+                outputName: 'test.zip',
+                defaultEnableZeroHeader: true,
+                contentType: 'application/octet-stream',
+            );
 
             //loop keys - useful for multiple files
             foreach ($s3keys as $key) {
@@ -58,15 +58,19 @@ stored in an AWS S3 bucket by key:
                 //file using the same name.
                 $fileName = basename($key);
 
-                //concatenate s3path.
-                $bucket = 'bucketname'; //replace with your bucket name or get from parameters file.
+                // concatenate s3path.
+                // replace with your bucket name or get from parameters file.
+                $bucket = 'bucketname';
                 $s3path = "s3://" . $bucket . "/" . $key;
 
                 //addFileFromStream
                 if ($streamRead = fopen($s3path, 'r')) {
-                $zip->addFileFromStream($fileName, $streamRead);
+                    $zip->addFileFromStream(
+                        fileName: $fileName,
+                        stream: $streamRead,
+                    );
                 } else {
-                die('Could not open stream for reading');
+                    die('Could not open stream for reading');
                 }
             }
 
@@ -123,4 +127,4 @@ You need to add correct permissions
         's3' => ['ACL' => 'public-read'],
     ]);
 
-    fopen($path, 'w', null, $outputContext);
\ No newline at end of file
+    fopen($path, 'w', null, $outputContext);
diff --git a/vendor/maennchen/zipstream-php/guides/index.rst b/vendor/maennchen/zipstream-php/guides/index.rst
index 01ea229..4583ca5 100644
--- a/vendor/maennchen/zipstream-php/guides/index.rst
+++ b/vendor/maennchen/zipstream-php/guides/index.rst
@@ -22,11 +22,35 @@ Installation
 
 Simply add a dependency on ``maennchen/zipstream-php`` to your project's
 ``composer.json`` file if you use Composer to manage the dependencies of your
-project. Use following command to add the package to your project's dependencies:
+project. Use following command to add the package to your project's
+dependencies:
 
 .. code-block:: sh
    composer require maennchen/zipstream-php
 
+If you want to use``addFileFromPsr7Stream```
+(``Psr\Http\Message\StreamInterface``) or use a stream instead of a
+``resource`` as ``outputStream``, the following dependencies must be installed
+as well:
+
+.. code-block:: sh
+   composer require psr/http-message guzzlehttp/psr7
+
+If ``composer install`` yields the following error, your installation is missing
+the `mbstring extension <https://www.php.net/manual/en/book.mbstring.php>`_,
+either `install it <https://www.php.net/manual/en/mbstring.installation.php>`_
+or run the follwoing command:
+
+.. code-block::
+    Your requirements could not be resolved to an installable set of packages.
+
+    Problem 1
+        - Root composer.json requires PHP extension ext-mbstring * but it is
+          missing from your system. Install or enable PHP's mbstrings extension.
+
+.. code-block:: sh
+   composer require symfony/polyfill-mbstring
+
 Usage Intro
 ---------------
 
@@ -37,25 +61,42 @@ Here's a simple example:
    // Autoload the dependencies
    require 'vendor/autoload.php';
 
-   // enable output of HTTP headers
-   $options = new ZipStream\Option\Archive();
-   $options->setSendHttpHeaders(true);
-
    // create a new zipstream object
-   $zip = new ZipStream\ZipStream('example.zip', $options);
+   $zip = new ZipStream\ZipStream(
+      outputName: 'example.zip',
+
+      // enable output of HTTP headers
+      sendHttpHeaders: true,
+   );
 
    // create a file named 'hello.txt'
-   $zip->addFile('hello.txt', 'This is the contents of hello.txt');
+   $zip->addFile(
+      fileName: 'hello.txt',
+      data: 'This is the contents of hello.txt',
+   );
 
    // add a file named 'some_image.jpg' from a local file 'path/to/image.jpg'
-   $zip->addFileFromPath('some_image.jpg', 'path/to/image.jpg');
+   $zip->addFileFromPath(
+      fileName: 'some_image.jpg',
+      path: 'path/to/image.jpg',
+   );
 
    // add a file named 'goodbye.txt' from an open stream resource
-   $fp = tmpfile();
-   fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
-   rewind($fp);
-   $zip->addFileFromStream('goodbye.txt', $fp);
-   fclose($fp);
+   $filePointer = tmpfile();
+   fwrite($filePointer, 'The quick brown fox jumped over the lazy dog.');
+   rewind($filePointer);
+   $zip->addFileFromStream(
+      fileName: 'goodbye.txt',
+      stream: $filePointer,
+   );
+   fclose($filePointer);
+
+   // add a file named 'streamfile.txt' from the body of a `guzzle` response
+   // Setup with `psr/http-message` & `guzzlehttp/psr7` dependencies required.
+   $zip->addFileFromPsr7Stream(
+      fileName: 'streamfile.txt',
+      stream: $response->getBody(),
+   );
 
    // finish the zip stream
    $zip->finish();
@@ -82,4 +123,4 @@ See `#146 <https://github.com/maennchen/ZipStream-PHP/issues/146>`_.
 It is the responsability of the client code to make sure that files are not
 saved with the same path, as it is not possible for the library to figure it out
 while streaming a zip.
-See `#154 <https://github.com/maennchen/ZipStream-PHP/issues/154>`_.
\ No newline at end of file
+See `#154 <https://github.com/maennchen/ZipStream-PHP/issues/154>`_.
diff --git a/vendor/maennchen/zipstream-php/phpdoc.dist.xml b/vendor/maennchen/zipstream-php/phpdoc.dist.xml
index 0a18848..b98fe1c 100644
--- a/vendor/maennchen/zipstream-php/phpdoc.dist.xml
+++ b/vendor/maennchen/zipstream-php/phpdoc.dist.xml
@@ -5,7 +5,7 @@
         xmlns="https://www.phpdoc.org"
         xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/phpDocumentor/phpDocumentor/master/data/xsd/phpdoc.xsd"
 >
-    <title>ZipStream-PHP</title>
+    <title>💾 ZipStream-PHP</title>
     <paths>
         <output>docs</output>
     </paths>
diff --git a/vendor/maennchen/zipstream-php/phpunit.xml.dist b/vendor/maennchen/zipstream-php/phpunit.xml.dist
index 8a2f318..1b02a3a 100644
--- a/vendor/maennchen/zipstream-php/phpunit.xml.dist
+++ b/vendor/maennchen/zipstream-php/phpunit.xml.dist
@@ -1,14 +1,15 @@
 <?xml version="1.0"?>
-<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="test/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
-  <coverage processUncoveredFiles="true">
-    <include>
-      <directory suffix=".php">src</directory>
-    </include>
-  </coverage>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="test/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd" cacheDirectory=".phpunit.cache">
+  <coverage/>
   <testsuites>
     <testsuite name="Application">
       <directory>test</directory>
     </testsuite>
   </testsuites>
   <logging/>
+  <source>
+    <include>
+      <directory suffix=".php">src</directory>
+    </include>
+  </source>
 </phpunit>
diff --git a/vendor/maennchen/zipstream-php/psalm.xml b/vendor/maennchen/zipstream-php/psalm.xml
index 4e4c4f6..5d050d1 100644
--- a/vendor/maennchen/zipstream-php/psalm.xml
+++ b/vendor/maennchen/zipstream-php/psalm.xml
@@ -1,9 +1,12 @@
 <?xml version="1.0"?>
 <psalm
+    errorLevel="1"
     resolveFromConfigFile="true"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="https://getpsalm.org/schema/config"
     xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
+    findUnusedBaselineEntry="true"
+    findUnusedCode="true"
 >
     <projectFiles>
         <directory name="src" />
@@ -11,43 +14,10 @@
             <directory name="vendor" />
         </ignoreFiles>
     </projectFiles>
-
     <issueHandlers>
-        <LessSpecificReturnType errorLevel="info" />
-
-        <!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->
-
-        <DeprecatedMethod errorLevel="info" />
-        <DeprecatedProperty errorLevel="info" />
-        <DeprecatedClass errorLevel="info" />
-        <DeprecatedConstant errorLevel="info" />
-        <DeprecatedFunction errorLevel="info" />
-        <DeprecatedInterface errorLevel="info" />
-        <DeprecatedTrait errorLevel="info" />
-
-        <InternalMethod errorLevel="info" />
-        <InternalProperty errorLevel="info" />
-        <InternalClass errorLevel="info" />
-
-        <MissingClosureReturnType errorLevel="info" />
-        <MissingReturnType errorLevel="info" />
-        <MissingPropertyType errorLevel="info" />
-        <InvalidDocblock errorLevel="info" />
-
-        <PropertyNotSetInConstructor errorLevel="info" />
-        <MissingConstructor errorLevel="info" />
-        <MissingClosureParamType errorLevel="info" />
-        <MissingParamType errorLevel="info" />
-
-        <RedundantCondition errorLevel="info" />
-
-        <DocblockTypeContradiction errorLevel="info" />
-        <RedundantConditionGivenDocblockType errorLevel="info" />
-
-        <UnresolvableInclude errorLevel="info" />
-
-        <RawObjectIteration errorLevel="info" />
-
-        <InvalidStringClass errorLevel="info" />
+        <!-- Turn off dead code warnings for externally called functions -->
+        <PossiblyUnusedProperty errorLevel="suppress" />
+        <PossiblyUnusedMethod errorLevel="suppress" />
+        <PossiblyUnusedReturnValue errorLevel="suppress" />
     </issueHandlers>
 </psalm>
diff --git a/vendor/maennchen/zipstream-php/src/Bigint.php b/vendor/maennchen/zipstream-php/src/Bigint.php
deleted file mode 100644
index f2565e9..0000000
--- a/vendor/maennchen/zipstream-php/src/Bigint.php
+++ /dev/null
@@ -1,174 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream;
-
-use OverflowException;
-
-class Bigint
-{
-    /**
-     * @var int[]
-     */
-    private $bytes = [0, 0, 0, 0, 0, 0, 0, 0];
-
-    /**
-     * Initialize the bytes array
-     *
-     * @param int $value
-     */
-    public function __construct(int $value = 0)
-    {
-        $this->fillBytes($value, 0, 8);
-    }
-
-    /**
-     * Get an instance
-     *
-     * @param int $value
-     * @return Bigint
-     */
-    public static function init(int $value = 0): self
-    {
-        return new self($value);
-    }
-
-    /**
-     * Fill bytes from low to high
-     *
-     * @param int $low
-     * @param int $high
-     * @return Bigint
-     */
-    public static function fromLowHigh(int $low, int $high): self
-    {
-        $bigint = new self();
-        $bigint->fillBytes($low, 0, 4);
-        $bigint->fillBytes($high, 4, 4);
-        return $bigint;
-    }
-
-    /**
-     * Get high 32
-     *
-     * @return int
-     */
-    public function getHigh32(): int
-    {
-        return $this->getValue(4, 4);
-    }
-
-    /**
-     * Get value from bytes array
-     *
-     * @param int $end
-     * @param int $length
-     * @return int
-     */
-    public function getValue(int $end = 0, int $length = 8): int
-    {
-        $result = 0;
-        for ($i = $end + $length - 1; $i >= $end; $i--) {
-            $result <<= 8;
-            $result |= $this->bytes[$i];
-        }
-        return $result;
-    }
-
-    /**
-     * Get low FF
-     *
-     * @param bool $force
-     * @return float
-     */
-    public function getLowFF(bool $force = false): float
-    {
-        if ($force || $this->isOver32()) {
-            return (float)0xFFFFFFFF;
-        }
-        return (float)$this->getLow32();
-    }
-
-    /**
-     * Check if is over 32
-     *
-     * @psalm-suppress ArgumentTypeCoercion
-     * @param bool $force
-     * @return bool
-     */
-    public function isOver32(bool $force = false): bool
-    {
-        // value 0xFFFFFFFF already needs a Zip64 header
-        return $force ||
-            max(array_slice($this->bytes, 4, 4)) > 0 ||
-            min(array_slice($this->bytes, 0, 4)) === 0xFF;
-    }
-
-    /**
-     * Get low 32
-     *
-     * @return int
-     */
-    public function getLow32(): int
-    {
-        return $this->getValue(0, 4);
-    }
-
-    /**
-     * Get hexadecimal
-     *
-     * @return string
-     */
-    public function getHex64(): string
-    {
-        $result = '0x';
-        for ($i = 7; $i >= 0; $i--) {
-            $result .= sprintf('%02X', $this->bytes[$i]);
-        }
-        return $result;
-    }
-
-    /**
-     * Add
-     *
-     * @param Bigint $other
-     * @return Bigint
-     */
-    public function add(self $other): self
-    {
-        $result = clone $this;
-        $overflow = false;
-        for ($i = 0; $i < 8; $i++) {
-            $result->bytes[$i] += $other->bytes[$i];
-            if ($overflow) {
-                $result->bytes[$i]++;
-                $overflow = false;
-            }
-            if ($result->bytes[$i] & 0x100) {
-                $overflow = true;
-                $result->bytes[$i] &= 0xFF;
-            }
-        }
-        if ($overflow) {
-            throw new OverflowException();
-        }
-        return $result;
-    }
-
-    /**
-     * Fill the bytes field with int
-     *
-     * @param int $value
-     * @param int $start
-     * @param int $count
-     * @return void
-     */
-    protected function fillBytes(int $value, int $start, int $count): void
-    {
-        for ($i = 0; $i < $count; $i++) {
-            $this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF;
-            $value >>= 8;
-        }
-    }
-}
diff --git a/vendor/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php b/vendor/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php
new file mode 100644
index 0000000..ffcfc6e
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/CentralDirectoryFileHeader.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use DateTimeInterface;
+
+/**
+ * @internal
+ */
+abstract class CentralDirectoryFileHeader
+{
+    private const SIGNATURE = 0x02014b50;
+
+    public static function generate(
+        int $versionMadeBy,
+        int $versionNeededToExtract,
+        int $generalPurposeBitFlag,
+        CompressionMethod $compressionMethod,
+        DateTimeInterface $lastModificationDateTime,
+        int $crc32,
+        int $compressedSize,
+        int $uncompressedSize,
+        string $fileName,
+        string $extraField,
+        string $fileComment,
+        int $diskNumberStart,
+        int $internalFileAttributes,
+        int $externalFileAttributes,
+        int $relativeOffsetOfLocalHeader,
+    ): string {
+        return PackField::pack(
+            new PackField(format: 'V', value: self::SIGNATURE),
+            new PackField(format: 'v', value: $versionMadeBy),
+            new PackField(format: 'v', value: $versionNeededToExtract),
+            new PackField(format: 'v', value: $generalPurposeBitFlag),
+            new PackField(format: 'v', value: $compressionMethod->value),
+            new PackField(format: 'V', value: Time::dateTimeToDosTime($lastModificationDateTime)),
+            new PackField(format: 'V', value: $crc32),
+            new PackField(format: 'V', value: $compressedSize),
+            new PackField(format: 'V', value: $uncompressedSize),
+            new PackField(format: 'v', value: strlen($fileName)),
+            new PackField(format: 'v', value: strlen($extraField)),
+            new PackField(format: 'v', value: strlen($fileComment)),
+            new PackField(format: 'v', value: $diskNumberStart),
+            new PackField(format: 'v', value: $internalFileAttributes),
+            new PackField(format: 'V', value: $externalFileAttributes),
+            new PackField(format: 'V', value: $relativeOffsetOfLocalHeader),
+        ) . $fileName . $extraField . $fileComment;
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/CompressionMethod.php b/vendor/maennchen/zipstream-php/src/CompressionMethod.php
new file mode 100644
index 0000000..51e4363
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/CompressionMethod.php
@@ -0,0 +1,106 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+enum CompressionMethod: int
+{
+    /**
+     * The file is stored (no compression)
+     */
+    case STORE = 0x00;
+
+    // 0x01: legacy algorithm - The file is Shrunk
+    // 0x02: legacy algorithm - The file is Reduced with compression factor 1
+    // 0x03: legacy algorithm - The file is Reduced with compression factor 2
+    // 0x04: legacy algorithm - The file is Reduced with compression factor 3
+    // 0x05: legacy algorithm - The file is Reduced with compression factor 4
+    // 0x06: legacy algorithm - The file is Imploded
+    // 0x07: Reserved for Tokenizing compression algorithm
+
+    /**
+     * The file is Deflated
+     */
+    case DEFLATE = 0x08;
+
+    // /**
+    //  * Enhanced Deflating using Deflate64(tm)
+    //  */
+    // case DEFLATE_64 = 0x09;
+
+    // /**
+    //  * PKWARE Data Compression Library Imploding (old IBM TERSE)
+    //  */
+    // case PKWARE = 0x0a;
+
+    // // 0x0b: Reserved by PKWARE
+
+    // /**
+    //  * File is compressed using BZIP2 algorithm
+    //  */
+    // case BZIP2 = 0x0c;
+
+    // // 0x0d: Reserved by PKWARE
+
+    // /**
+    //  * LZMA
+    //  */
+    // case LZMA = 0x0e;
+
+    // // 0x0f: Reserved by PKWARE
+
+    // /**
+    //  * IBM z/OS CMPSC Compression
+    //  */
+    // case IBM_ZOS_CMPSC = 0x10;
+
+    // // 0x11: Reserved by PKWARE
+
+    // /**
+    //  * File is compressed using IBM TERSE
+    //  */
+    // case IBM_TERSE = 0x12;
+
+    // /**
+    //  * IBM LZ77 z Architecture
+    //  */
+    // case IBM_LZ77 = 0x13;
+
+    // // 0x14: deprecated (use method 93 for zstd)
+
+    // /**
+    //  * Zstandard (zstd) Compression
+    //  */
+    // case ZSTD = 0x5d;
+
+    // /**
+    //  * MP3 Compression
+    //  */
+    // case MP3 = 0x5e;
+
+    // /**
+    //  * XZ Compression
+    //  */
+    // case XZ = 0x5f;
+
+    // /**
+    //  * JPEG variant
+    //  */
+    // case JPEG = 0x60;
+
+    // /**
+    //  * WavPack compressed data
+    //  */
+    // case WAV_PACK = 0x61;
+
+    // /**
+    //  * PPMd version I, Rev 1
+    //  */
+    // case PPMD_1_1 = 0x62;
+
+    // /**
+    //  * AE-x encryption marker
+    //  */
+    // case AE_X_ENCRYPTION = 0x63;
+}
diff --git a/vendor/maennchen/zipstream-php/src/DataDescriptor.php b/vendor/maennchen/zipstream-php/src/DataDescriptor.php
new file mode 100644
index 0000000..0414619
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/DataDescriptor.php
@@ -0,0 +1,26 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * @internal
+ */
+abstract class DataDescriptor
+{
+    private const SIGNATURE = 0x08074b50;
+
+    public static function generate(
+        int $crc32UncompressedData,
+        int $compressedSize,
+        int $uncompressedSize,
+    ): string {
+        return PackField::pack(
+            new PackField(format: 'V', value: self::SIGNATURE),
+            new PackField(format: 'V', value: $crc32UncompressedData),
+            new PackField(format: 'V', value: $compressedSize),
+            new PackField(format: 'V', value: $uncompressedSize),
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/DeflateStream.php b/vendor/maennchen/zipstream-php/src/DeflateStream.php
deleted file mode 100644
index 7dc4d3a..0000000
--- a/vendor/maennchen/zipstream-php/src/DeflateStream.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream;
-
-class DeflateStream extends Stream
-{
-    protected $filter;
-
-    /**
-     * @var Option\File
-     */
-    protected $options;
-
-    /**
-     * Rewind stream
-     *
-     * @return void
-     */
-    public function rewind(): void
-    {
-        // deflate filter needs to be removed before rewind
-        if ($this->filter) {
-            $this->removeDeflateFilter();
-            $this->seek(0);
-            $this->addDeflateFilter($this->options);
-        } else {
-            rewind($this->stream);
-        }
-    }
-
-    /**
-     * Remove the deflate filter
-     *
-     * @return void
-     */
-    public function removeDeflateFilter(): void
-    {
-        if (!$this->filter) {
-            return;
-        }
-        stream_filter_remove($this->filter);
-        $this->filter = null;
-    }
-
-    /**
-     * Add a deflate filter
-     *
-     * @param Option\File $options
-     * @return void
-     */
-    public function addDeflateFilter(Option\File $options): void
-    {
-        $this->options = $options;
-        // parameter 4 for stream_filter_append expects array
-        // so we convert the option object in an array
-        $optionsArr = [
-            'comment' => $options->getComment(),
-            'method' => $options->getMethod(),
-            'deflateLevel' => $options->getDeflateLevel(),
-            'time' => $options->getTime(),
-        ];
-        $this->filter = stream_filter_append(
-            $this->stream,
-            'zlib.deflate',
-            STREAM_FILTER_READ,
-            $optionsArr
-        );
-    }
-}
diff --git a/vendor/maennchen/zipstream-php/src/EndOfCentralDirectory.php b/vendor/maennchen/zipstream-php/src/EndOfCentralDirectory.php
new file mode 100644
index 0000000..4320add
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/EndOfCentralDirectory.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * @internal
+ */
+abstract class EndOfCentralDirectory
+{
+    private const SIGNATURE = 0x06054b50;
+
+    public static function generate(
+        int $numberOfThisDisk,
+        int $numberOfTheDiskWithCentralDirectoryStart,
+        int $numberOfCentralDirectoryEntriesOnThisDisk,
+        int $numberOfCentralDirectoryEntries,
+        int $sizeOfCentralDirectory,
+        int $centralDirectoryStartOffsetOnDisk,
+        string $zipFileComment,
+    ): string {
+        /** @psalm-suppress MixedArgument */
+        return PackField::pack(
+            new PackField(format: 'V', value: static::SIGNATURE),
+            new PackField(format: 'v', value: $numberOfThisDisk),
+            new PackField(format: 'v', value: $numberOfTheDiskWithCentralDirectoryStart),
+            new PackField(format: 'v', value: $numberOfCentralDirectoryEntriesOnThisDisk),
+            new PackField(format: 'v', value: $numberOfCentralDirectoryEntries),
+            new PackField(format: 'V', value: $sizeOfCentralDirectory),
+            new PackField(format: 'V', value: $centralDirectoryStartOffsetOnDisk),
+            new PackField(format: 'v', value: strlen($zipFileComment)),
+        ) . $zipFileComment;
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception.php b/vendor/maennchen/zipstream-php/src/Exception.php
index 03a8767..27f4f30 100644
--- a/vendor/maennchen/zipstream-php/src/Exception.php
+++ b/vendor/maennchen/zipstream-php/src/Exception.php
@@ -4,9 +4,6 @@ declare(strict_types=1);
 
 namespace ZipStream;
 
-/**
- * This class is only for inheriting
- */
 abstract class Exception extends \Exception
 {
 }
diff --git a/vendor/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php b/vendor/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php
new file mode 100644
index 0000000..b8d0508
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/DosTimeOverflowException.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use DateTimeInterface;
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a file wasn't found
+ */
+class DosTimeOverflowException extends Exception
+{
+    /**
+     * @internal
+     */
+    public function __construct(
+        public readonly DateTimeInterface $dateTime
+    ) {
+        parent::__construct('The date ' . $dateTime->format(DateTimeInterface::ATOM) . " can't be represented as DOS time / date.");
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/EncodingException.php b/vendor/maennchen/zipstream-php/src/Exception/EncodingException.php
deleted file mode 100644
index 5b0267d..0000000
--- a/vendor/maennchen/zipstream-php/src/Exception/EncodingException.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream\Exception;
-
-use ZipStream\Exception;
-
-/**
- * This Exception gets invoked if file or comment encoding is incorrect
- */
-class EncodingException extends Exception
-{
-}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php b/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php
index eb82001..350a7bf 100644
--- a/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileNotFoundException.php
@@ -12,12 +12,11 @@ use ZipStream\Exception;
 class FileNotFoundException extends Exception
 {
     /**
-     * Constructor of the Exception
-     *
-     * @param String $path - The path which wasn't found
+     * @internal
      */
-    public function __construct(string $path)
-    {
+    public function __construct(
+        public readonly string $path
+    ) {
         parent::__construct("The file with the path $path wasn't found.");
     }
 }
diff --git a/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php b/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php
index 1fbfdc5..93d0c6c 100644
--- a/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileNotReadableException.php
@@ -12,12 +12,11 @@ use ZipStream\Exception;
 class FileNotReadableException extends Exception
 {
     /**
-     * Constructor of the Exception
-     *
-     * @param String $path - The path which wasn't found
+     * @internal
      */
-    public function __construct(string $path)
-    {
+    public function __construct(
+        public readonly string $path
+    ) {
         parent::__construct("The file with the path $path isn't readable.");
     }
 }
diff --git a/vendor/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php b/vendor/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php
new file mode 100644
index 0000000..11f0b67
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/FileSizeIncorrectException.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a file is not as large as it was specified.
+ */
+class FileSizeIncorrectException extends Exception
+{
+    /**
+     * @internal
+     */
+    public function __construct(
+        public readonly int $expectedSize,
+        public readonly int $actualSize
+    ) {
+        parent::__construct("File is {$actualSize} instead of {$expectedSize} bytes large. Adjust `exactSize` parameter.");
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php b/vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php
deleted file mode 100644
index 2f1a7ef..0000000
--- a/vendor/maennchen/zipstream-php/src/Exception/IncompatibleOptionsException.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream\Exception;
-
-use ZipStream\Exception;
-
-/**
- * This Exception gets invoked if options are incompatible
- */
-class IncompatibleOptionsException extends Exception
-{
-}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php b/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php
index a1bc4d0..09bdafb 100644
--- a/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php
+++ b/vendor/maennchen/zipstream-php/src/Exception/OverflowException.php
@@ -11,6 +11,9 @@ use ZipStream\Exception;
  */
 class OverflowException extends Exception
 {
+    /**
+     * @internal
+     */
     public function __construct()
     {
         parent::__construct('File size exceeds limit of 32 bit integer. Please enable "zip64" option.');
diff --git a/vendor/maennchen/zipstream-php/src/Exception/ResourceActionException.php b/vendor/maennchen/zipstream-php/src/Exception/ResourceActionException.php
new file mode 100644
index 0000000..cbd9b0b
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/ResourceActionException.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a resource like `fread` returns false
+ */
+class ResourceActionException extends Exception
+{
+    /**
+     * @var ?resource
+     */
+    public $resource;
+
+    /**
+     * @param resource $resource
+     */
+    public function __construct(
+        public readonly string $function,
+        $resource = null,
+    ) {
+        $this->resource = $resource;
+        parent::__construct('Function ' . $function . 'failed on resource.');
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php b/vendor/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php
new file mode 100644
index 0000000..717c1aa
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/SimulationFileUnknownException.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a strict simulation is executed and the file
+ * information can't be determined without reading the entire file.
+ */
+class SimulationFileUnknownException extends Exception
+{
+    public function __construct()
+    {
+        parent::__construct('The details of the strict simulation file could not be determined without reading the entire file.');
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php b/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php
index e676e37..c144673 100644
--- a/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php
+++ b/vendor/maennchen/zipstream-php/src/Exception/StreamNotReadableException.php
@@ -7,17 +7,15 @@ namespace ZipStream\Exception;
 use ZipStream\Exception;
 
 /**
- * This Exception gets invoked if `fread` fails on a stream.
+ * This Exception gets invoked if a stream can't be read.
  */
 class StreamNotReadableException extends Exception
 {
     /**
-     * Constructor of the Exception
-     *
-     * @param string $fileName - The name of the file which the stream belongs to.
+     * @internal
      */
-    public function __construct(string $fileName)
+    public function __construct()
     {
-        parent::__construct("The stream for $fileName could not be read.");
+        parent::__construct('The stream could not be read.');
     }
 }
diff --git a/vendor/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php b/vendor/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php
new file mode 100644
index 0000000..606f11f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Exception/StreamNotSeekableException.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Exception;
+
+use ZipStream\Exception;
+
+/**
+ * This Exception gets invoked if a non seekable stream is
+ * provided and zero headers are disabled.
+ */
+class StreamNotSeekableException extends Exception
+{
+    /**
+     * @internal
+     */
+    public function __construct()
+    {
+        parent::__construct('enableZeroHeader must be enable to add non seekable streams');
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/File.php b/vendor/maennchen/zipstream-php/src/File.php
index 7b32956..3f35de4 100644
--- a/vendor/maennchen/zipstream-php/src/File.php
+++ b/vendor/maennchen/zipstream-php/src/File.php
@@ -4,480 +4,417 @@ declare(strict_types=1);
 
 namespace ZipStream;
 
-use HashContext;
-use Psr\Http\Message\StreamInterface;
-use ZipStream\Exception\FileNotFoundException;
-use ZipStream\Exception\FileNotReadableException;
+use Closure;
+use DateTimeInterface;
+use DeflateContext;
+use RuntimeException;
+use ZipStream\Exception\FileSizeIncorrectException;
 use ZipStream\Exception\OverflowException;
-use ZipStream\Option\File as FileOptions;
-use ZipStream\Option\Method;
-use ZipStream\Option\Version;
+use ZipStream\Exception\ResourceActionException;
+use ZipStream\Exception\SimulationFileUnknownException;
+use ZipStream\Exception\StreamNotReadableException;
+use ZipStream\Exception\StreamNotSeekableException;
 
+/**
+ * @internal
+ */
 class File
 {
-    public const HASH_ALGORITHM = 'crc32b';
+    private const CHUNKED_READ_BLOCK_SIZE = 0x1000000;
 
-    public const BIT_ZERO_HEADER = 0x0008;
+    private Version $version;
 
-    public const BIT_EFS_UTF8 = 0x0800;
+    private int $compressedSize = 0;
 
-    public const COMPUTE = 1;
+    private int $uncompressedSize = 0;
 
-    public const SEND = 2;
+    private int $crc = 0;
 
-    private const CHUNKED_READ_BLOCK_SIZE = 1048576;
+    private int $generalPurposeBitFlag = 0;
+
+    private readonly string $fileName;
 
     /**
-     * @var string
+     * @var resource|null
      */
-    public $name;
+    private $stream;
 
     /**
-     * @var FileOptions
+     * @param Closure $dataCallback
+     * @psalm-param Closure(): resource $dataCallback
      */
-    public $opt;
+    public function __construct(
+        string $fileName,
+        private readonly Closure $dataCallback,
+        private readonly OperationMode $operationMode,
+        private readonly int $startOffset,
+        private readonly CompressionMethod $compressionMethod,
+        private readonly string $comment,
+        private readonly DateTimeInterface $lastModificationDateTime,
+        private readonly int $deflateLevel,
+        private readonly ?int $maxSize,
+        private readonly ?int $exactSize,
+        private readonly bool $enableZip64,
+        private readonly bool $enableZeroHeader,
+        private readonly Closure $send,
+        private readonly Closure $recordSentBytes,
+    ) {
+        $this->fileName = self::filterFilename($fileName);
+        $this->checkEncoding();
 
-    /**
-     * @var Bigint
-     */
-    public $len;
+        if ($this->enableZeroHeader) {
+            $this->generalPurposeBitFlag |= GeneralPurposeBitFlag::ZERO_HEADER;
+        }
 
-    /**
-     * @var Bigint
-     */
-    public $zlen;
-
-    /** @var  int */
-    public $crc;
-
-    /**
-     * @var Bigint
-     */
-    public $hlen;
-
-    /**
-     * @var Bigint
-     */
-    public $ofs;
-
-    /**
-     * @var int
-     */
-    public $bits;
-
-    /**
-     * @var Version
-     */
-    public $version;
-
-    /**
-     * @var ZipStream
-     */
-    public $zip;
-
-    /**
-     * @var resource
-     */
-    private $deflate;
-
-    /**
-     * @var HashContext
-     */
-    private $hash;
-
-    /**
-     * @var Method
-     */
-    private $method;
-
-    /**
-     * @var Bigint
-     */
-    private $totalLength;
-
-    public function __construct(ZipStream $zip, string $name, ?FileOptions $opt = null)
-    {
-        $this->zip = $zip;
-
-        $this->name = $name;
-        $this->opt = $opt ?: new FileOptions();
-        $this->method = $this->opt->getMethod();
-        $this->version = Version::STORE();
-        $this->ofs = new Bigint();
+        $this->version = $this->compressionMethod === CompressionMethod::DEFLATE ? Version::DEFLATE : Version::STORE;
     }
 
-    public function processPath(string $path): void
+    public function cloneSimulationExecution(): self
     {
-        if (!is_readable($path)) {
-            if (!file_exists($path)) {
-                throw new FileNotFoundException($path);
-            }
-            throw new FileNotReadableException($path);
-        }
-        if ($this->zip->isLargeFile($path) === false) {
-            $data = file_get_contents($path);
-            $this->processData($data);
+        return new self(
+            $this->fileName,
+            $this->dataCallback,
+            OperationMode::NORMAL,
+            $this->startOffset,
+            $this->compressionMethod,
+            $this->comment,
+            $this->lastModificationDateTime,
+            $this->deflateLevel,
+            $this->maxSize,
+            $this->exactSize,
+            $this->enableZip64,
+            $this->enableZeroHeader,
+            $this->send,
+            $this->recordSentBytes,
+        );
+    }
+
+    public function process(): string
+    {
+        $forecastSize = $this->forecastSize();
+
+        if ($this->enableZeroHeader) {
+            // No calculation required
+        } elseif ($this->isSimulation() && $forecastSize) {
+            $this->uncompressedSize = $forecastSize;
+            $this->compressedSize = $forecastSize;
         } else {
-            $this->method = $this->zip->opt->getLargeFileMethod();
-
-            $stream = new DeflateStream(fopen($path, 'rb'));
-            $this->processStream($stream);
-            $stream->close();
+            $this->readStream(send: false);
+            if (rewind($this->unpackStream()) === false) {
+                throw new ResourceActionException('rewind', $this->unpackStream());
+            }
         }
+
+        $this->addFileHeader();
+
+        $detectedSize = $forecastSize ?? $this->compressedSize;
+
+        if (
+            $this->isSimulation() &&
+            $detectedSize > 0
+        ) {
+            ($this->recordSentBytes)($detectedSize);
+        } else {
+            $this->readStream(send: true);
+        }
+
+        $this->addFileFooter();
+        return $this->getCdrFile();
     }
 
-    public function processData(string $data): void
+    /**
+     * @return resource
+     */
+    private function unpackStream()
     {
-        $this->len = new Bigint(strlen($data));
-        $this->crc = crc32($data);
-
-        // compress data if needed
-        if ($this->method->equals(Method::DEFLATE())) {
-            $data = gzdeflate($data);
+        if ($this->stream) {
+            return $this->stream;
         }
 
-        $this->zlen = new Bigint(strlen($data));
-        $this->addFileHeader();
-        $this->zip->send($data);
-        $this->addFileFooter();
+        if ($this->operationMode === OperationMode::SIMULATE_STRICT) {
+            throw new SimulationFileUnknownException();
+        }
+
+        $this->stream = ($this->dataCallback)();
+
+        if (!$this->enableZeroHeader && !stream_get_meta_data($this->stream)['seekable']) {
+            throw new StreamNotSeekableException();
+        }
+        if (!(
+            str_contains(stream_get_meta_data($this->stream)['mode'], 'r')
+            || str_contains(stream_get_meta_data($this->stream)['mode'], 'w+')
+            || str_contains(stream_get_meta_data($this->stream)['mode'], 'a+')
+            || str_contains(stream_get_meta_data($this->stream)['mode'], 'x+')
+            || str_contains(stream_get_meta_data($this->stream)['mode'], 'c+')
+        )) {
+            throw new StreamNotReadableException();
+        }
+
+        return $this->stream;
+    }
+
+    private function forecastSize(): ?int
+    {
+        if ($this->compressionMethod !== CompressionMethod::STORE) {
+            return null;
+        }
+        if ($this->exactSize) {
+            return $this->exactSize;
+        }
+        $fstat = fstat($this->unpackStream());
+        if (!$fstat || !array_key_exists('size', $fstat) || $fstat['size'] < 1) {
+            return null;
+        }
+
+        if ($this->maxSize !== null && $this->maxSize < $fstat['size']) {
+            return $this->maxSize;
+        }
+
+        return $fstat['size'];
     }
 
     /**
      * Create and send zip header for this file.
-     *
-     * @return void
-     * @throws \ZipStream\Exception\EncodingException
      */
-    public function addFileHeader(): void
+    private function addFileHeader(): void
     {
-        $name = static::filterFilename($this->name);
+        $forceEnableZip64 = $this->enableZeroHeader && $this->enableZip64;
 
-        // calculate name length
-        $nameLength = strlen($name);
+        $footer = $this->buildZip64ExtraBlock($forceEnableZip64);
 
-        // create dos timestamp
-        $time = static::dosTime($this->opt->getTime()->getTimestamp());
+        $zip64Enabled = $footer !== '';
 
-        $comment = $this->opt->getComment();
-
-        if (!mb_check_encoding($name, 'ASCII') ||
-            !mb_check_encoding($comment, 'ASCII')) {
-            // Sets Bit 11: Language encoding flag (EFS).  If this bit is set,
-            // the filename and comment fields for this file
-            // MUST be encoded using UTF-8. (see APPENDIX D)
-            if (mb_check_encoding($name, 'UTF-8') &&
-                mb_check_encoding($comment, 'UTF-8')) {
-                $this->bits |= self::BIT_EFS_UTF8;
-            }
+        if($zip64Enabled) {
+            $this->version = Version::ZIP64;
         }
 
-        if ($this->method->equals(Method::DEFLATE())) {
-            $this->version = Version::DEFLATE();
+        if ($this->generalPurposeBitFlag & GeneralPurposeBitFlag::EFS) {
+            // Put the tricky entry to
+            // force Linux unzip to lookup EFS flag.
+            $footer .= Zs\ExtendedInformationExtraField::generate();
         }
 
-        $force = (bool)($this->bits & self::BIT_ZERO_HEADER) &&
-            $this->zip->opt->isEnableZip64();
+        $data = LocalFileHeader::generate(
+            versionNeededToExtract: $this->version->value,
+            generalPurposeBitFlag: $this->generalPurposeBitFlag,
+            compressionMethod: $this->compressionMethod,
+            lastModificationDateTime: $this->lastModificationDateTime,
+            crc32UncompressedData: $this->crc,
+            compressedSize: $zip64Enabled
+                ? 0xFFFFFFFF
+                : $this->compressedSize,
+            uncompressedSize: $zip64Enabled
+                ? 0xFFFFFFFF
+                : $this->uncompressedSize,
+            fileName: $this->fileName,
+            extraField: $footer,
+        );
 
-        $footer = $this->buildZip64ExtraBlock($force);
 
-        // If this file will start over 4GB limit in ZIP file,
-        // CDR record will have to use Zip64 extension to describe offset
-        // to keep consistency we use the same value here
-        if ($this->zip->ofs->isOver32()) {
-            $this->version = Version::ZIP64();
-        }
-
-        $fields = [
-            ['V', ZipStream::FILE_HEADER_SIGNATURE],
-            ['v', $this->version->getValue()],      // Version needed to Extract
-            ['v', $this->bits],                     // General purpose bit flags - data descriptor flag set
-            ['v', $this->method->getValue()],       // Compression method
-            ['V', $time],                           // Timestamp (DOS Format)
-            ['V', $this->crc],                      // CRC32 of data (0 -> moved to data descriptor footer)
-            ['V', $this->zlen->getLowFF($force)],   // Length of compressed data (forced to 0xFFFFFFFF for zero header)
-            ['V', $this->len->getLowFF($force)],    // Length of original data (forced to 0xFFFFFFFF for zero header)
-            ['v', $nameLength],                     // Length of filename
-            ['v', strlen($footer)],                 // Extra data (see above)
-        ];
-
-        // pack fields and calculate "total" length
-        $header = ZipStream::packFields($fields);
-
-        // print header and filename
-        $data = $header . $name . $footer;
-        $this->zip->send($data);
-
-        // save header length
-        $this->hlen = Bigint::init(strlen($data));
+        ($this->send)($data);
     }
 
     /**
      * Strip characters that are not legal in Windows filenames
      * to prevent compatibility issues
-     *
-     * @param string $filename Unprocessed filename
-     * @return string
      */
-    public static function filterFilename(string $filename): string
-    {
+    private static function filterFilename(
+        /**
+         * Unprocessed filename
+         */
+        string $fileName
+    ): string {
         // strip leading slashes from file name
         // (fixes bug in windows archive viewer)
-        $filename = preg_replace('/^\\/+/', '', $filename);
+        $fileName = ltrim($fileName, '/');
 
-        return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $filename);
+        return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $fileName);
     }
 
-    /**
-     * Create and send data descriptor footer for this file.
-     *
-     * @return void
-     */
-    public function addFileFooter(): void
+    private function checkEncoding(): void
     {
-        if ($this->bits & self::BIT_ZERO_HEADER) {
-            // compressed and uncompressed size
-            $sizeFormat = 'V';
-            if ($this->zip->opt->isEnableZip64()) {
-                $sizeFormat = 'P';
+        // Sets Bit 11: Language encoding flag (EFS).  If this bit is set,
+        // the filename and comment fields for this file
+        // MUST be encoded using UTF-8. (see APPENDIX D)
+        if (mb_check_encoding($this->fileName, 'UTF-8') &&
+                mb_check_encoding($this->comment, 'UTF-8')) {
+            $this->generalPurposeBitFlag |= GeneralPurposeBitFlag::EFS;
+        }
+    }
+
+    private function buildZip64ExtraBlock(bool $force = false): string
+    {
+        $outputZip64ExtraBlock = false;
+
+        $originalSize = null;
+        if ($force || $this->uncompressedSize > 0xFFFFFFFF) {
+            $outputZip64ExtraBlock = true;
+            $originalSize = $this->uncompressedSize;
+        }
+
+        $compressedSize = null;
+        if ($force || $this->compressedSize > 0xFFFFFFFF) {
+            $outputZip64ExtraBlock = true;
+            $compressedSize = $this->compressedSize;
+        }
+
+        // If this file will start over 4GB limit in ZIP file,
+        // CDR record will have to use Zip64 extension to describe offset
+        // to keep consistency we use the same value here
+        $relativeHeaderOffset = null;
+        if ($this->startOffset > 0xFFFFFFFF) {
+            $outputZip64ExtraBlock = true;
+            $relativeHeaderOffset = $this->startOffset;
+        }
+
+        if (!$outputZip64ExtraBlock) {
+            return '';
+        }
+
+        if (!$this->enableZip64) {
+            throw new OverflowException();
+        }
+
+        return Zip64\ExtendedInformationExtraField::generate(
+            originalSize: $originalSize,
+            compressedSize: $compressedSize,
+            relativeHeaderOffset: $relativeHeaderOffset,
+            diskStartNumber: null,
+        );
+    }
+
+    private function addFileFooter(): void
+    {
+        if (($this->compressedSize > 0xFFFFFFFF || $this->uncompressedSize > 0xFFFFFFFF) && $this->version !== Version::ZIP64) {
+            throw new OverflowException();
+        }
+
+        if (!$this->enableZeroHeader) {
+            return;
+        }
+
+        if ($this->version === Version::ZIP64) {
+            $footer = Zip64\DataDescriptor::generate(
+                crc32UncompressedData: $this->crc,
+                compressedSize: $this->compressedSize,
+                uncompressedSize: $this->uncompressedSize,
+            );
+        } else {
+            $footer = DataDescriptor::generate(
+                crc32UncompressedData: $this->crc,
+                compressedSize: $this->compressedSize,
+                uncompressedSize: $this->uncompressedSize,
+            );
+        }
+
+        ($this->send)($footer);
+    }
+
+    private function readStream(bool $send): void
+    {
+        $this->compressedSize = 0;
+        $this->uncompressedSize = 0;
+        $hash = hash_init('crc32b');
+
+        $deflate = $this->compressionInit();
+
+        while (
+            !feof($this->unpackStream()) &&
+            ($this->maxSize === null || $this->uncompressedSize < $this->maxSize) &&
+            ($this->exactSize === null || $this->uncompressedSize < $this->exactSize)
+        ) {
+            $readLength = min(
+                ($this->maxSize ?? PHP_INT_MAX) - $this->uncompressedSize,
+                ($this->exactSize ?? PHP_INT_MAX) - $this->uncompressedSize,
+                self::CHUNKED_READ_BLOCK_SIZE
+            );
+
+            $data = fread($this->unpackStream(), $readLength);
+
+            hash_update($hash, $data);
+
+            $this->uncompressedSize += strlen($data);
+
+            if ($deflate) {
+                $data =  deflate_add(
+                    $deflate,
+                    $data,
+                    feof($this->unpackStream()) ? ZLIB_FINISH : ZLIB_NO_FLUSH
+                );
             }
-            $fields = [
-                ['V', ZipStream::DATA_DESCRIPTOR_SIGNATURE],
-                ['V', $this->crc],              // CRC32
-                [$sizeFormat, $this->zlen],     // Length of compressed data
-                [$sizeFormat, $this->len],      // Length of original data
-            ];
 
-            $footer = ZipStream::packFields($fields);
-            $this->zip->send($footer);
-        } else {
-            $footer = '';
+            $this->compressedSize += strlen($data);
+
+            if ($send) {
+                ($this->send)($data);
+            }
         }
-        $this->totalLength = $this->hlen->add($this->zlen)->add(Bigint::init(strlen($footer)));
-        $this->zip->addToCdr($this);
+
+        if ($this->exactSize && $this->uncompressedSize !== $this->exactSize) {
+            throw new FileSizeIncorrectException(expectedSize: $this->exactSize, actualSize: $this->uncompressedSize);
+        }
+
+        $this->crc = hexdec(hash_final($hash));
     }
 
-    public function processStream(StreamInterface $stream): void
+    private function compressionInit(): ?DeflateContext
     {
-        $this->zlen = new Bigint();
-        $this->len = new Bigint();
+        switch($this->compressionMethod) {
+            case CompressionMethod::STORE:
+                // Noting to do
+                return null;
+            case CompressionMethod::DEFLATE:
+                $deflateContext = deflate_init(
+                    ZLIB_ENCODING_RAW,
+                    ['level' => $this->deflateLevel]
+                );
 
-        if ($this->zip->opt->isZeroHeader()) {
-            $this->processStreamWithZeroHeader($stream);
-        } else {
-            $this->processStreamWithComputedHeader($stream);
+                if (!$deflateContext) {
+                    // @codeCoverageIgnoreStart
+                    throw new RuntimeException("Can't initialize deflate context.");
+                    // @codeCoverageIgnoreEnd
+                }
+
+                // False positive, resource is no longer returned from this function
+                return $deflateContext;
+            default:
+                // @codeCoverageIgnoreStart
+                throw new RuntimeException('Unsupported Compression Method ' . print_r($this->compressionMethod, true));
+                // @codeCoverageIgnoreEnd
         }
     }
 
-    /**
-     * Send CDR record for specified file.
-     *
-     * @return string
-     */
-    public function getCdrFile(): string
+    private function getCdrFile(): string
     {
-        $name = static::filterFilename($this->name);
-
-        // get attributes
-        $comment = $this->opt->getComment();
-
-        // get dos timestamp
-        $time = static::dosTime($this->opt->getTime()->getTimestamp());
-
         $footer = $this->buildZip64ExtraBlock();
 
-        $fields = [
-            ['V', ZipStream::CDR_FILE_SIGNATURE],   // Central file header signature
-            ['v', ZipStream::ZIP_VERSION_MADE_BY],  // Made by version
-            ['v', $this->version->getValue()],      // Extract by version
-            ['v', $this->bits],                     // General purpose bit flags - data descriptor flag set
-            ['v', $this->method->getValue()],       // Compression method
-            ['V', $time],                           // Timestamp (DOS Format)
-            ['V', $this->crc],                      // CRC32
-            ['V', $this->zlen->getLowFF()],         // Compressed Data Length
-            ['V', $this->len->getLowFF()],          // Original Data Length
-            ['v', strlen($name)],                   // Length of filename
-            ['v', strlen($footer)],                 // Extra data len (see above)
-            ['v', strlen($comment)],                // Length of comment
-            ['v', 0],                               // Disk number
-            ['v', 0],                               // Internal File Attributes
-            ['V', 32],                              // External File Attributes
-            ['V', $this->ofs->getLowFF()],           // Relative offset of local header
-        ];
-
-        // pack fields, then append name and comment
-        $header = ZipStream::packFields($fields);
-
-        return $header . $name . $footer . $comment;
+        return CentralDirectoryFileHeader::generate(
+            versionMadeBy: ZipStream::ZIP_VERSION_MADE_BY,
+            versionNeededToExtract:$this->version->value,
+            generalPurposeBitFlag: $this->generalPurposeBitFlag,
+            compressionMethod: $this->compressionMethod,
+            lastModificationDateTime: $this->lastModificationDateTime,
+            crc32: $this->crc,
+            compressedSize: $this->compressedSize > 0xFFFFFFFF
+                ? 0xFFFFFFFF
+                : $this->compressedSize,
+            uncompressedSize: $this->uncompressedSize > 0xFFFFFFFF
+                ? 0xFFFFFFFF
+                : $this->uncompressedSize,
+            fileName: $this->fileName,
+            extraField: $footer,
+            fileComment: $this->comment,
+            diskNumberStart: 0,
+            internalFileAttributes: 0,
+            externalFileAttributes: 32,
+            relativeOffsetOfLocalHeader: $this->startOffset > 0xFFFFFFFF
+                ? 0xFFFFFFFF
+                : $this->startOffset,
+        );
     }
 
-    /**
-     * @return Bigint
-     */
-    public function getTotalLength(): Bigint
+    private function isSimulation(): bool
     {
-        return $this->totalLength;
-    }
-
-    /**
-     * Convert a UNIX timestamp to a DOS timestamp.
-     *
-     * @param int $when
-     * @return int DOS Timestamp
-     */
-    final protected static function dosTime(int $when): int
-    {
-        // get date array for timestamp
-        $d = getdate($when);
-
-        // set lower-bound on dates
-        if ($d['year'] < 1980) {
-            $d = [
-                'year' => 1980,
-                'mon' => 1,
-                'mday' => 1,
-                'hours' => 0,
-                'minutes' => 0,
-                'seconds' => 0,
-            ];
-        }
-
-        // remove extra years from 1980
-        $d['year'] -= 1980;
-
-        // return date string
-        return
-            ($d['year'] << 25) |
-            ($d['mon'] << 21) |
-            ($d['mday'] << 16) |
-            ($d['hours'] << 11) |
-            ($d['minutes'] << 5) |
-            ($d['seconds'] >> 1);
-    }
-
-    protected function buildZip64ExtraBlock(bool $force = false): string
-    {
-        $fields = [];
-        if ($this->len->isOver32($force)) {
-            $fields[] = ['P', $this->len];          // Length of original data
-        }
-
-        if ($this->len->isOver32($force)) {
-            $fields[] = ['P', $this->zlen];         // Length of compressed data
-        }
-
-        if ($this->ofs->isOver32()) {
-            $fields[] = ['P', $this->ofs];          // Offset of local header record
-        }
-
-        if (!empty($fields)) {
-            if (!$this->zip->opt->isEnableZip64()) {
-                throw new OverflowException();
-            }
-
-            array_unshift(
-                $fields,
-                ['v', 0x0001],                      // 64 bit extension
-                ['v', count($fields) * 8]             // Length of data block
-            );
-            $this->version = Version::ZIP64();
-        }
-
-        if ($this->bits & self::BIT_EFS_UTF8) {
-            // Put the tricky entry to
-            // force Linux unzip to lookup EFS flag.
-            $fields[] = ['v', 0x5653];  // Choose 'ZS' for proprietary usage
-            $fields[] = ['v', 0x0000];  // zero length
-        }
-
-        return ZipStream::packFields($fields);
-    }
-
-    protected function processStreamWithZeroHeader(StreamInterface $stream): void
-    {
-        $this->bits |= self::BIT_ZERO_HEADER;
-        $this->addFileHeader();
-        $this->readStream($stream, self::COMPUTE | self::SEND);
-        $this->addFileFooter();
-    }
-
-    protected function readStream(StreamInterface $stream, ?int $options = null): void
-    {
-        $this->deflateInit();
-        $total = 0;
-        $size = $this->opt->getSize();
-        while (!$stream->eof() && ($size === 0 || $total < $size)) {
-            $data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE);
-            $total += strlen($data);
-            if ($size > 0 && $total > $size) {
-                $data = substr($data, 0, strlen($data)-($total - $size));
-            }
-            $this->deflateData($stream, $data, $options);
-            if ($options & self::SEND) {
-                $this->zip->send($data);
-            }
-        }
-        $this->deflateFinish($options);
-    }
-
-    protected function deflateInit(): void
-    {
-        $hash = hash_init(self::HASH_ALGORITHM);
-        $this->hash = $hash;
-        if ($this->method->equals(Method::DEFLATE())) {
-            $this->deflate = deflate_init(
-                ZLIB_ENCODING_RAW,
-                ['level' => $this->opt->getDeflateLevel()]
-            );
-        }
-    }
-
-    protected function deflateData(StreamInterface $stream, string &$data, ?int $options = null): void
-    {
-        if ($options & self::COMPUTE) {
-            $this->len = $this->len->add(Bigint::init(strlen($data)));
-            hash_update($this->hash, $data);
-        }
-        if ($this->deflate) {
-            $data = deflate_add(
-                $this->deflate,
-                $data,
-                $stream->eof()
-                    ? ZLIB_FINISH
-                    : ZLIB_NO_FLUSH
-            );
-        }
-        if ($options & self::COMPUTE) {
-            $this->zlen = $this->zlen->add(Bigint::init(strlen($data)));
-        }
-    }
-
-    protected function deflateFinish(?int $options = null): void
-    {
-        if ($options & self::COMPUTE) {
-            $this->crc = hexdec(hash_final($this->hash));
-        }
-    }
-
-    protected function processStreamWithComputedHeader(StreamInterface $stream): void
-    {
-        $this->readStream($stream, self::COMPUTE);
-        $stream->rewind();
-
-        // incremental compression with deflate_add
-        // makes this second read unnecessary
-        // but it is only available from PHP 7.0
-        if (!$this->deflate && $stream instanceof DeflateStream && $this->method->equals(Method::DEFLATE())) {
-            $stream->addDeflateFilter($this->opt);
-            $this->zlen = new Bigint();
-            while (!$stream->eof()) {
-                $data = $stream->read(self::CHUNKED_READ_BLOCK_SIZE);
-                $this->zlen = $this->zlen->add(Bigint::init(strlen($data)));
-            }
-            $stream->rewind();
-        }
-
-        $this->addFileHeader();
-        $this->readStream($stream, self::SEND);
-        $this->addFileFooter();
+        return $this->operationMode === OperationMode::SIMULATE_LAX || $this->operationMode === OperationMode::SIMULATE_STRICT;
     }
 }
diff --git a/vendor/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php b/vendor/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php
new file mode 100644
index 0000000..23a66d8
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/GeneralPurposeBitFlag.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * @internal
+ */
+abstract class GeneralPurposeBitFlag
+{
+    /**
+     * If set, indicates that the file is encrypted.
+     */
+    public const ENCRYPTED = 1 << 0;
+
+    /**
+     * (For Methods 8 and 9 - Deflating)
+     * Normal (-en) compression option was used.
+     */
+    public const DEFLATE_COMPRESSION_NORMAL = 0 << 1;
+
+    /**
+     * (For Methods 8 and 9 - Deflating)
+     * Maximum (-exx/-ex) compression option was used.
+     */
+    public const DEFLATE_COMPRESSION_MAXIMUM = 1 << 1;
+
+    /**
+     * (For Methods 8 and 9 - Deflating)
+     * Fast (-ef) compression option was used.
+     */
+    public const DEFLATE_COMPRESSION_FAST = 10 << 1;
+
+    /**
+     * (For Methods 8 and 9 - Deflating)
+     * Super Fast (-es) compression option was used.
+     */
+    public const DEFLATE_COMPRESSION_SUPERFAST = 11 << 1;
+
+    /**
+     * If the compression method used was type 14,
+     * LZMA, then this bit, if set, indicates
+     * an end-of-stream (EOS) marker is used to
+     * mark the end of the compressed data stream.
+     * If clear, then an EOS marker is not present
+     * and the compressed data size must be known
+     * to extract.
+     */
+    public const LZMA_EOS = 1 << 1;
+
+    /**
+     * If this bit is set, the fields crc-32, compressed
+     * size and uncompressed size are set to zero in the
+     * local header.  The correct values are put in the
+     * data descriptor immediately following the compressed
+     * data.
+     */
+    public const ZERO_HEADER = 1 << 3;
+
+    /**
+     * If this bit is set, this indicates that the file is
+     * compressed patched data.
+     */
+    public const COMPRESSED_PATCHED_DATA = 1 << 5;
+
+    /**
+     * Strong encryption. If this bit is set, you MUST
+     * set the version needed to extract value to at least
+     * 50 and you MUST also set bit 0.  If AES encryption
+     * is used, the version needed to extract value MUST
+     * be at least 51.
+     */
+    public const STRONG_ENCRYPTION = 1 << 6;
+
+    /**
+     * Language encoding flag (EFS).  If this bit is set,
+     * the filename and comment fields for this file
+     * MUST be encoded using UTF-8.
+     */
+    public const EFS = 1 << 11;
+
+    /**
+     * Set when encrypting the Central Directory to indicate
+     * selected data values in the Local Header are masked to
+     * hide their actual values.
+     */
+    public const ENCRYPT_CENTRAL_DIRECTORY = 1 << 13;
+}
diff --git a/vendor/maennchen/zipstream-php/src/LocalFileHeader.php b/vendor/maennchen/zipstream-php/src/LocalFileHeader.php
new file mode 100644
index 0000000..e08b656
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/LocalFileHeader.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use DateTimeInterface;
+
+/**
+ * @internal
+ */
+abstract class LocalFileHeader
+{
+    private const SIGNATURE = 0x04034b50;
+
+    public static function generate(
+        int $versionNeededToExtract,
+        int $generalPurposeBitFlag,
+        CompressionMethod $compressionMethod,
+        DateTimeInterface $lastModificationDateTime,
+        int $crc32UncompressedData,
+        int $compressedSize,
+        int $uncompressedSize,
+        string $fileName,
+        string $extraField,
+    ): string {
+        return PackField::pack(
+            new PackField(format: 'V', value: self::SIGNATURE),
+            new PackField(format: 'v', value: $versionNeededToExtract),
+            new PackField(format: 'v', value: $generalPurposeBitFlag),
+            new PackField(format: 'v', value: $compressionMethod->value),
+            new PackField(format: 'V', value: Time::dateTimeToDosTime($lastModificationDateTime)),
+            new PackField(format: 'V', value: $crc32UncompressedData),
+            new PackField(format: 'V', value: $compressedSize),
+            new PackField(format: 'V', value: $uncompressedSize),
+            new PackField(format: 'v', value: strlen($fileName)),
+            new PackField(format: 'v', value: strlen($extraField)),
+        ) . $fileName . $extraField;
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/OperationMode.php b/vendor/maennchen/zipstream-php/src/OperationMode.php
new file mode 100644
index 0000000..dd650f0
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/OperationMode.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+/**
+ * ZipStream execution operation modes
+ */
+enum OperationMode
+{
+    /**
+     * Stream file into output stream
+     */
+    case NORMAL;
+
+    /**
+     * Simulate the zip to figure out the resulting file size
+     *
+     * This only supports entries where the file size is known beforehand and
+     * deflation is disabled.
+     */
+    case SIMULATE_STRICT;
+
+    /**
+     * Simulate the zip to figure out the resulting file size
+     *
+     * If the file size is not known beforehand or deflation is enabled, the
+     * entry streams will be read and rewound.
+     *
+     * If the entry does not support rewinding either, you will not be able to
+     * use the same stream in a later operation mode like `NORMAL`.
+     */
+    case SIMULATE_LAX;
+}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Archive.php b/vendor/maennchen/zipstream-php/src/Option/Archive.php
deleted file mode 100644
index 374dd1d..0000000
--- a/vendor/maennchen/zipstream-php/src/Option/Archive.php
+++ /dev/null
@@ -1,276 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream\Option;
-
-use Psr\Http\Message\StreamInterface;
-
-final class Archive
-{
-    public const DEFAULT_DEFLATE_LEVEL = 6;
-
-    /**
-     * @var string
-     */
-    private $comment = '';
-
-    /**
-     * Size, in bytes, of the largest file to try
-     * and load into memory (used by
-     * addFileFromPath()).  Large files may also
-     * be compressed differently; see the
-     * 'largeFileMethod' option. Default is ~20 Mb.
-     *
-     * @var int
-     */
-    private $largeFileSize = 20 * 1024 * 1024;
-
-    /**
-     * How to handle large files.  Legal values are
-     * Method::STORE() (the default), or
-     * Method::DEFLATE(). STORE sends the file
-     * raw and is significantly
-     * faster, while DEFLATE compresses the file
-     * and is much, much slower. Note that DEFLATE
-     * must compress the file twice and is extremely slow.
-     *
-     * @var Method
-     */
-    private $largeFileMethod;
-
-    /**
-     * Boolean indicating whether or not to send
-     * the HTTP headers for this file.
-     *
-     * @var bool
-     */
-    private $sendHttpHeaders = false;
-
-    /**
-     * The method called to send headers
-     *
-     * @var Callable
-     */
-    private $httpHeaderCallback = 'header';
-
-    /**
-     * Enable Zip64 extension, supporting very large
-     * archives (any size > 4 GB or file count > 64k)
-     *
-     * @var bool
-     */
-    private $enableZip64 = true;
-
-    /**
-     * Enable streaming files with single read where
-     * general purpose bit 3 indicates local file header
-     * contain zero values in crc and size fields,
-     * these appear only after file contents
-     * in data descriptor block.
-     *
-     * @var bool
-     */
-    private $zeroHeader = false;
-
-    /**
-     * Enable reading file stat for determining file size.
-     * When a 32-bit system reads file size that is
-     * over 2 GB, invalid value appears in file size
-     * due to integer overflow. Should be disabled on
-     * 32-bit systems with method addFileFromPath
-     * if any file may exceed 2 GB. In this case file
-     * will be read in blocks and correct size will be
-     * determined from content.
-     *
-     * @var bool
-     */
-    private $statFiles = true;
-
-    /**
-     * Enable flush after every write to output stream.
-     * @var bool
-     */
-    private $flushOutput = false;
-
-    /**
-     * HTTP Content-Disposition.  Defaults to
-     * 'attachment', where
-     * FILENAME is the specified filename.
-     *
-     * Note that this does nothing if you are
-     * not sending HTTP headers.
-     *
-     * @var string
-     */
-    private $contentDisposition = 'attachment';
-
-    /**
-     * Note that this does nothing if you are
-     * not sending HTTP headers.
-     *
-     * @var string
-     */
-    private $contentType = 'application/x-zip';
-
-    /**
-     * @var int
-     */
-    private $deflateLevel = 6;
-
-    /**
-     * @var StreamInterface|resource
-     */
-    private $outputStream;
-
-    /**
-     * Options constructor.
-     */
-    public function __construct()
-    {
-        $this->largeFileMethod = Method::STORE();
-        $this->outputStream = fopen('php://output', 'wb');
-    }
-
-    public function getComment(): string
-    {
-        return $this->comment;
-    }
-
-    public function setComment(string $comment): void
-    {
-        $this->comment = $comment;
-    }
-
-    public function getLargeFileSize(): int
-    {
-        return $this->largeFileSize;
-    }
-
-    public function setLargeFileSize(int $largeFileSize): void
-    {
-        $this->largeFileSize = $largeFileSize;
-    }
-
-    public function getLargeFileMethod(): Method
-    {
-        return $this->largeFileMethod;
-    }
-
-    public function setLargeFileMethod(Method $largeFileMethod): void
-    {
-        $this->largeFileMethod = $largeFileMethod;
-    }
-
-    public function isSendHttpHeaders(): bool
-    {
-        return $this->sendHttpHeaders;
-    }
-
-    public function setSendHttpHeaders(bool $sendHttpHeaders): void
-    {
-        $this->sendHttpHeaders = $sendHttpHeaders;
-    }
-
-    public function getHttpHeaderCallback(): callable
-    {
-        return $this->httpHeaderCallback;
-    }
-
-    public function setHttpHeaderCallback(callable $httpHeaderCallback): void
-    {
-        $this->httpHeaderCallback = $httpHeaderCallback;
-    }
-
-    public function isEnableZip64(): bool
-    {
-        return $this->enableZip64;
-    }
-
-    public function setEnableZip64(bool $enableZip64): void
-    {
-        $this->enableZip64 = $enableZip64;
-    }
-
-    public function isZeroHeader(): bool
-    {
-        return $this->zeroHeader;
-    }
-
-    public function setZeroHeader(bool $zeroHeader): void
-    {
-        $this->zeroHeader = $zeroHeader;
-    }
-
-    public function isFlushOutput(): bool
-    {
-        return $this->flushOutput;
-    }
-
-    public function setFlushOutput(bool $flushOutput): void
-    {
-        $this->flushOutput = $flushOutput;
-    }
-
-    public function isStatFiles(): bool
-    {
-        return $this->statFiles;
-    }
-
-    public function setStatFiles(bool $statFiles): void
-    {
-        $this->statFiles = $statFiles;
-    }
-
-    public function getContentDisposition(): string
-    {
-        return $this->contentDisposition;
-    }
-
-    public function setContentDisposition(string $contentDisposition): void
-    {
-        $this->contentDisposition = $contentDisposition;
-    }
-
-    public function getContentType(): string
-    {
-        return $this->contentType;
-    }
-
-    public function setContentType(string $contentType): void
-    {
-        $this->contentType = $contentType;
-    }
-
-    /**
-     * @return StreamInterface|resource
-     */
-    public function getOutputStream()
-    {
-        return $this->outputStream;
-    }
-
-    /**
-     * @param StreamInterface|resource $outputStream
-     */
-    public function setOutputStream($outputStream): void
-    {
-        $this->outputStream = $outputStream;
-    }
-
-    /**
-     * @return int
-     */
-    public function getDeflateLevel(): int
-    {
-        return $this->deflateLevel;
-    }
-
-    /**
-     * @param int $deflateLevel
-     */
-    public function setDeflateLevel(int $deflateLevel): void
-    {
-        $this->deflateLevel = $deflateLevel;
-    }
-}
diff --git a/vendor/maennchen/zipstream-php/src/Option/File.php b/vendor/maennchen/zipstream-php/src/Option/File.php
deleted file mode 100644
index 37e37ce..0000000
--- a/vendor/maennchen/zipstream-php/src/Option/File.php
+++ /dev/null
@@ -1,122 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream\Option;
-
-use DateTime;
-use DateTimeInterface;
-
-final class File
-{
-    /**
-     * @var string
-     */
-    private $comment = '';
-
-    /**
-     * @var Method
-     */
-    private $method;
-
-    /**
-     * @var int
-     */
-    private $deflateLevel;
-
-    /**
-     * @var DateTimeInterface
-     */
-    private $time;
-
-    /**
-     * @var int
-     */
-    private $size = 0;
-
-    public function defaultTo(Archive $archiveOptions): void
-    {
-        $this->deflateLevel = $this->deflateLevel ?: $archiveOptions->getDeflateLevel();
-        $this->time = $this->time ?: new DateTime();
-    }
-
-    /**
-     * @return string
-     */
-    public function getComment(): string
-    {
-        return $this->comment;
-    }
-
-    /**
-     * @param string $comment
-     */
-    public function setComment(string $comment): void
-    {
-        $this->comment = $comment;
-    }
-
-    /**
-     * @return Method
-     */
-    public function getMethod(): Method
-    {
-        return $this->method ?: Method::DEFLATE();
-    }
-
-    /**
-     * @param Method $method
-     */
-    public function setMethod(Method $method): void
-    {
-        $this->method = $method;
-    }
-
-    /**
-     * @return int
-     */
-    public function getDeflateLevel(): int
-    {
-        return $this->deflateLevel ?: Archive::DEFAULT_DEFLATE_LEVEL;
-    }
-
-    /**
-     * @param int $deflateLevel
-     */
-    public function setDeflateLevel(int $deflateLevel): void
-    {
-        $this->deflateLevel = $deflateLevel;
-    }
-
-    /**
-     * @return DateTimeInterface
-     */
-    public function getTime(): DateTimeInterface
-    {
-        return $this->time;
-    }
-
-    /**
-     * @param DateTimeInterface $time
-     */
-    public function setTime(DateTimeInterface $time): void
-    {
-        $this->time = $time;
-    }
-
-    /**
-     * @return int
-     */
-    public function getSize(): int
-    {
-        return $this->size;
-    }
-
-    /**
-     * @param int $size
-     */
-    public function setSize(int $size): void
-    {
-        $this->size = $size;
-    }
-}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Method.php b/vendor/maennchen/zipstream-php/src/Option/Method.php
deleted file mode 100644
index d361c22..0000000
--- a/vendor/maennchen/zipstream-php/src/Option/Method.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream\Option;
-
-use MyCLabs\Enum\Enum;
-
-/**
- * Methods enum
- *
- * @method static STORE(): Method
- * @method static DEFLATE(): Method
- * @psalm-immutable
- */
-class Method extends Enum
-{
-    public const STORE = 0x00;
-
-    public const DEFLATE = 0x08;
-}
diff --git a/vendor/maennchen/zipstream-php/src/Option/Version.php b/vendor/maennchen/zipstream-php/src/Option/Version.php
deleted file mode 100644
index da85baf..0000000
--- a/vendor/maennchen/zipstream-php/src/Option/Version.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream\Option;
-
-use MyCLabs\Enum\Enum;
-
-/**
- * Class Version
- * @package ZipStream\Option
- *
- * @method static STORE(): Version
- * @method static DEFLATE(): Version
- * @method static ZIP64(): Version
- * @psalm-immutable
- */
-class Version extends Enum
-{
-    public const STORE = 0x000A; // 1.00
-
-    public const DEFLATE = 0x0014; // 2.00
-
-    public const ZIP64 = 0x002D; // 4.50
-}
diff --git a/vendor/maennchen/zipstream-php/src/PackField.php b/vendor/maennchen/zipstream-php/src/PackField.php
new file mode 100644
index 0000000..3370dd8
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/PackField.php
@@ -0,0 +1,57 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use RuntimeException;
+
+/**
+ * @internal
+ * TODO: Make class readonly when requiring PHP 8.2 exclusively
+ */
+class PackField
+{
+    public const MAX_V = 0xFFFFFFFF;
+
+    public const MAX_v = 0xFFFF;
+
+    public function __construct(
+        public readonly string $format,
+        public readonly int|string $value
+    ) {
+    }
+
+    /**
+     * Create a format string and argument list for pack(), then call
+     * pack() and return the result.
+     */
+    public static function pack(self ...$fields): string
+    {
+        $fmt = array_reduce($fields, function (string $acc, self $field) {
+            return $acc . $field->format;
+        }, '');
+
+        $args = array_map(function (self $field) {
+            switch($field->format) {
+                case 'V':
+                    if ($field->value > self::MAX_V) {
+                        throw new RuntimeException(print_r($field->value, true) . ' is larger than 32 bits');
+                    }
+                    break;
+                case 'v':
+                    if ($field->value > self::MAX_v) {
+                        throw new RuntimeException(print_r($field->value, true) . ' is larger than 16 bits');
+                    }
+                    break;
+                case 'P': break;
+                default:
+                    break;
+            }
+
+            return $field->value;
+        }, $fields);
+
+        return pack($fmt, ...$args);
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Stream.php b/vendor/maennchen/zipstream-php/src/Stream.php
deleted file mode 100644
index d80e70f..0000000
--- a/vendor/maennchen/zipstream-php/src/Stream.php
+++ /dev/null
@@ -1,265 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace ZipStream;
-
-use function mb_strlen;
-
-use Psr\Http\Message\StreamInterface;
-use RuntimeException;
-
-/**
- * Describes a data stream.
- *
- * Typically, an instance will wrap a PHP stream; this interface provides
- * a wrapper around the most common operations, including serialization of
- * the entire stream to a string.
- */
-class Stream implements StreamInterface
-{
-    protected $stream;
-
-    public function __construct($stream)
-    {
-        $this->stream = $stream;
-    }
-
-    /**
-     * Reads all data from the stream into a string, from the beginning to end.
-     *
-     * This method MUST attempt to seek to the beginning of the stream before
-     * reading data and read the stream until the end is reached.
-     *
-     * Warning: This could attempt to load a large amount of data into memory.
-     *
-     * This method MUST NOT raise an exception in order to conform with PHP's
-     * string casting operations.
-     *
-     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
-     * @return string
-     */
-    public function __toString(): string
-    {
-        try {
-            $this->seek(0);
-        } catch (RuntimeException $e) {
-        }
-        return (string) stream_get_contents($this->stream);
-    }
-
-    /**
-     * Closes the stream and any underlying resources.
-     *
-     * @return void
-     */
-    public function close(): void
-    {
-        if (is_resource($this->stream)) {
-            fclose($this->stream);
-        }
-        $this->detach();
-    }
-
-    /**
-     * Separates any underlying resources from the stream.
-     *
-     * After the stream has been detached, the stream is in an unusable state.
-     *
-     * @return resource|null Underlying PHP stream, if any
-     */
-    public function detach()
-    {
-        $result = $this->stream;
-        $this->stream = null;
-        return $result;
-    }
-
-    /**
-     * Seek to a position in the stream.
-     *
-     * @link http://www.php.net/manual/en/function.fseek.php
-     * @param int $offset Stream offset
-     * @param int $whence Specifies how the cursor position will be calculated
-     *     based on the seek offset. Valid values are identical to the built-in
-     *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
-     *     offset bytes SEEK_CUR: Set position to current location plus offset
-     *     SEEK_END: Set position to end-of-stream plus offset.
-     * @throws RuntimeException on failure.
-     */
-    public function seek($offset, $whence = SEEK_SET): void
-    {
-        if (!$this->isSeekable()) {
-            throw new RuntimeException();
-        }
-        if (fseek($this->stream, $offset, $whence) !== 0) {
-            throw new RuntimeException();
-        }
-    }
-
-    /**
-     * Returns whether or not the stream is seekable.
-     *
-     * @return bool
-     */
-    public function isSeekable(): bool
-    {
-        return (bool)$this->getMetadata('seekable');
-    }
-
-    /**
-     * Get stream metadata as an associative array or retrieve a specific key.
-     *
-     * The keys returned are identical to the keys returned from PHP's
-     * stream_get_meta_data() function.
-     *
-     * @link http://php.net/manual/en/function.stream-get-meta-data.php
-     * @param string $key Specific metadata to retrieve.
-     * @return array|mixed|null Returns an associative array if no key is
-     *     provided. Returns a specific key value if a key is provided and the
-     *     value is found, or null if the key is not found.
-     */
-    public function getMetadata($key = null)
-    {
-        $metadata = stream_get_meta_data($this->stream);
-        return $key !== null ? @$metadata[$key] : $metadata;
-    }
-
-    /**
-     * Get the size of the stream if known.
-     *
-     * @return int|null Returns the size in bytes if known, or null if unknown.
-     */
-    public function getSize(): ?int
-    {
-        $stats = fstat($this->stream);
-        return $stats['size'];
-    }
-
-    /**
-     * Returns the current position of the file read/write pointer
-     *
-     * @return int Position of the file pointer
-     * @throws RuntimeException on error.
-     */
-    public function tell(): int
-    {
-        $position = ftell($this->stream);
-        if ($position === false) {
-            throw new RuntimeException();
-        }
-        return $position;
-    }
-
-    /**
-     * Returns true if the stream is at the end of the stream.
-     *
-     * @return bool
-     */
-    public function eof(): bool
-    {
-        return feof($this->stream);
-    }
-
-    /**
-     * Seek to the beginning of the stream.
-     *
-     * If the stream is not seekable, this method will raise an exception;
-     * otherwise, it will perform a seek(0).
-     *
-     * @see seek()
-     * @link http://www.php.net/manual/en/function.fseek.php
-     * @throws RuntimeException on failure.
-     */
-    public function rewind(): void
-    {
-        $this->seek(0);
-    }
-
-    /**
-     * Write data to the stream.
-     *
-     * @param string $string The string that is to be written.
-     * @return int Returns the number of bytes written to the stream.
-     * @throws RuntimeException on failure.
-     */
-    public function write($string): int
-    {
-        if (!$this->isWritable()) {
-            throw new RuntimeException();
-        }
-        if (fwrite($this->stream, $string) === false) {
-            throw new RuntimeException();
-        }
-        return mb_strlen($string);
-    }
-
-    /**
-     * Returns whether or not the stream is writable.
-     *
-     * @return bool
-     */
-    public function isWritable(): bool
-    {
-        $mode = $this->getMetadata('mode');
-        if (!is_string($mode)) {
-            throw new RuntimeException('Could not get stream mode from metadata!');
-        }
-        return preg_match('/[waxc+]/', $mode) === 1;
-    }
-
-    /**
-     * Read data from the stream.
-     *
-     * @param int $length Read up to $length bytes from the object and return
-     *     them. Fewer than $length bytes may be returned if underlying stream
-     *     call returns fewer bytes.
-     * @return string Returns the data read from the stream, or an empty string
-     *     if no bytes are available.
-     * @throws RuntimeException if an error occurs.
-     */
-    public function read($length): string
-    {
-        if (!$this->isReadable()) {
-            throw new RuntimeException();
-        }
-        $result = fread($this->stream, $length);
-        if ($result === false) {
-            throw new RuntimeException();
-        }
-        return $result;
-    }
-
-    /**
-     * Returns whether or not the stream is readable.
-     *
-     * @return bool
-     */
-    public function isReadable(): bool
-    {
-        $mode = $this->getMetadata('mode');
-        if (!is_string($mode)) {
-            throw new RuntimeException('Could not get stream mode from metadata!');
-        }
-        return preg_match('/[r+]/', $mode) === 1;
-    }
-
-    /**
-     * Returns the remaining contents in a string
-     *
-     * @return string
-     * @throws RuntimeException if unable to read or an error occurs while
-     *     reading.
-     */
-    public function getContents(): string
-    {
-        if (!$this->isReadable()) {
-            throw new RuntimeException();
-        }
-        $result = stream_get_contents($this->stream);
-        if ($result === false) {
-            throw new RuntimeException();
-        }
-        return $result;
-    }
-}
diff --git a/vendor/maennchen/zipstream-php/src/Time.php b/vendor/maennchen/zipstream-php/src/Time.php
new file mode 100644
index 0000000..4bfba3c
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Time.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+use DateInterval;
+use DateTimeImmutable;
+use DateTimeInterface;
+use ZipStream\Exception\DosTimeOverflowException;
+
+/**
+ * @internal
+ */
+abstract class Time
+{
+    private const DOS_MINIMUM_DATE = '1980-01-01 00:00:00Z';
+
+    public static function dateTimeToDosTime(DateTimeInterface $dateTime): int
+    {
+        $dosMinimumDate = new DateTimeImmutable(self::DOS_MINIMUM_DATE);
+
+        if ($dateTime->getTimestamp() < $dosMinimumDate->getTimestamp()) {
+            throw new DosTimeOverflowException(dateTime: $dateTime);
+        }
+
+        $dateTime = DateTimeImmutable::createFromInterface($dateTime)->sub(new DateInterval('P1980Y'));
+
+        ['year' => $year,
+            'mon' => $month,
+            'mday' => $day,
+            'hours' => $hour,
+            'minutes' => $minute,
+            'seconds' => $second
+        ] = getdate($dateTime->getTimestamp());
+
+        return
+            ($year << 25) |
+            ($month << 21) |
+            ($day << 16) |
+            ($hour << 11) |
+            ($minute << 5) |
+            ($second >> 1);
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Version.php b/vendor/maennchen/zipstream-php/src/Version.php
new file mode 100644
index 0000000..c014f8a
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Version.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream;
+
+enum Version: int
+{
+    case STORE = 0x000A; // 1.00
+    case DEFLATE = 0x0014; // 2.00
+    case ZIP64 = 0x002D; // 4.50
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/DataDescriptor.php b/vendor/maennchen/zipstream-php/src/Zip64/DataDescriptor.php
new file mode 100644
index 0000000..041c557
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/DataDescriptor.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class DataDescriptor
+{
+    private const SIGNATURE = 0x08074b50;
+
+    public static function generate(
+        int $crc32UncompressedData,
+        int $compressedSize,
+        int $uncompressedSize,
+    ): string {
+        return PackField::pack(
+            new PackField(format: 'V', value: self::SIGNATURE),
+            new PackField(format: 'V', value: $crc32UncompressedData),
+            new PackField(format: 'P', value: $compressedSize),
+            new PackField(format: 'P', value: $uncompressedSize),
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php
new file mode 100644
index 0000000..08588e4
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectory.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class EndOfCentralDirectory
+{
+    private const SIGNATURE = 0x06064b50;
+
+    public static function generate(
+        int $versionMadeBy,
+        int $versionNeededToExtract,
+        int $numberOfThisDisk,
+        int $numberOfTheDiskWithCentralDirectoryStart,
+        int $numberOfCentralDirectoryEntriesOnThisDisk,
+        int $numberOfCentralDirectoryEntries,
+        int $sizeOfCentralDirectory,
+        int $centralDirectoryStartOffsetOnDisk,
+        string $extensibleDataSector,
+    ): string {
+        $recordSize = 44 + strlen($extensibleDataSector); // (length of block - 12) = 44;
+
+        /** @psalm-suppress MixedArgument */
+        return PackField::pack(
+            new PackField(format: 'V', value: static::SIGNATURE),
+            new PackField(format: 'P', value: $recordSize),
+            new PackField(format: 'v', value: $versionMadeBy),
+            new PackField(format: 'v', value: $versionNeededToExtract),
+            new PackField(format: 'V', value: $numberOfThisDisk),
+            new PackField(format: 'V', value: $numberOfTheDiskWithCentralDirectoryStart),
+            new PackField(format: 'P', value: $numberOfCentralDirectoryEntriesOnThisDisk),
+            new PackField(format: 'P', value: $numberOfCentralDirectoryEntries),
+            new PackField(format: 'P', value: $sizeOfCentralDirectory),
+            new PackField(format: 'P', value: $centralDirectoryStartOffsetOnDisk),
+        ) . $extensibleDataSector;
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php
new file mode 100644
index 0000000..ef431c3
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/EndOfCentralDirectoryLocator.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class EndOfCentralDirectoryLocator
+{
+    private const SIGNATURE = 0x07064b50;
+
+    public static function generate(
+        int $numberOfTheDiskWithZip64CentralDirectoryStart,
+        int $zip64centralDirectoryStartOffsetOnDisk,
+        int $totalNumberOfDisks,
+    ): string {
+        /** @psalm-suppress MixedArgument */
+        return PackField::pack(
+            new PackField(format: 'V', value: static::SIGNATURE),
+            new PackField(format: 'V', value: $numberOfTheDiskWithZip64CentralDirectoryStart),
+            new PackField(format: 'P', value: $zip64centralDirectoryStartOffsetOnDisk),
+            new PackField(format: 'V', value: $totalNumberOfDisks),
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php b/vendor/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php
new file mode 100644
index 0000000..b647ce6
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zip64/ExtendedInformationExtraField.php
@@ -0,0 +1,46 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zip64;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class ExtendedInformationExtraField
+{
+    private const TAG = 0x0001;
+
+    public static function generate(
+        ?int $originalSize = null,
+        ?int $compressedSize = null,
+        ?int $relativeHeaderOffset = null,
+        ?int $diskStartNumber = null,
+    ): string {
+        return PackField::pack(
+            new PackField(format: 'v', value: self::TAG),
+            new PackField(
+                format: 'v',
+                value:
+                    ($originalSize === null ? 0 : 8) +
+                    ($compressedSize === null ? 0 : 8) +
+                    ($relativeHeaderOffset === null ? 0 : 8) +
+                    ($diskStartNumber === null ? 0 : 4)
+            ),
+            ...($originalSize === null ? [] : [
+                new PackField(format: 'P', value: $originalSize),
+            ]),
+            ...($compressedSize === null ? [] : [
+                new PackField(format: 'P', value: $compressedSize),
+            ]),
+            ...($relativeHeaderOffset === null ? [] : [
+                new PackField(format: 'P', value: $relativeHeaderOffset),
+            ]),
+            ...($diskStartNumber === null ? [] : [
+                new PackField(format: 'V', value: $diskStartNumber),
+            ]),
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/src/ZipStream.php b/vendor/maennchen/zipstream-php/src/ZipStream.php
index 71ea4ed..cd308a4 100644
--- a/vendor/maennchen/zipstream-php/src/ZipStream.php
+++ b/vendor/maennchen/zipstream-php/src/ZipStream.php
@@ -4,57 +4,66 @@ declare(strict_types=1);
 
 namespace ZipStream;
 
+use Closure;
+use DateTimeImmutable;
+use DateTimeInterface;
+use GuzzleHttp\Psr7\StreamWrapper;
 use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+use ZipStream\Exception\FileNotFoundException;
+use ZipStream\Exception\FileNotReadableException;
 use ZipStream\Exception\OverflowException;
-use ZipStream\Option\Archive as ArchiveOptions;
-use ZipStream\Option\File as FileOptions;
-use ZipStream\Option\Version;
+use ZipStream\Exception\ResourceActionException;
 
 /**
- * ZipStream
- *
  * Streamed, dynamically generated zip archives.
  *
- * Usage:
+ * ## Usage
  *
  * Streaming zip archives is a simple, three-step process:
  *
  * 1.  Create the zip stream:
  *
- *     $zip = new ZipStream('example.zip');
+ * ```php
+ * $zip = new ZipStream(outputName: 'example.zip');
+ * ```
  *
  * 2.  Add one or more files to the archive:
  *
- *      * add first file
- *     $data = file_get_contents('some_file.gif');
- *     $zip->addFile('some_file.gif', $data);
+ * ```php
+ * // add first file
+ * $zip->addFile(fileName: 'world.txt', data: 'Hello World');
  *
- *      * add second file
- *     $data = file_get_contents('some_file.gif');
- *     $zip->addFile('another_file.png', $data);
+ * // add second file
+ * $zip->addFile(fileName: 'moon.txt', data: 'Hello Moon');
+ * ```
  *
  * 3.  Finish the zip stream:
  *
- *     $zip->finish();
+ * ```php
+ * $zip->finish();
+ * ```
  *
  * You can also add an archive comment, add comments to individual files,
  * and adjust the timestamp of files. See the API documentation for each
  * method below for additional information.
  *
- * Example:
+ * ## Example
  *
- *   // create a new zip stream object
- *   $zip = new ZipStream('some_files.zip');
+ * ```php
+ * // create a new zip stream object
+ * $zip = new ZipStream(outputName: 'some_files.zip');
  *
- *   // list of local files
- *   $files = array('foo.txt', 'bar.jpg');
+ * // list of local files
+ * $files = array('foo.txt', 'bar.jpg');
  *
- *   // read and add each file to the archive
- *   foreach ($files as $path)
- *     $zip->addFile($path, file_get_contents($path));
+ * // read and add each file to the archive
+ * foreach ($files as $path)
+ *   $zip->addFileFormPath(fileName: $path, $path);
  *
- *   // write archive footer to stream
- *   $zip->finish();
+ * // write archive footer to stream
+ * $zip->finish();
+ * ```
  */
 class ZipStream
 {
@@ -80,529 +89,776 @@ class ZipStream
      * Here we are using 6 for the OS, indicating OS/2 H.P.F.S.
      * to prevent file permissions issues upon extract (see #84)
      * 0x603 is 00000110 00000011 in binary, so 6 and 3
+     *
+     * @internal
      */
     public const ZIP_VERSION_MADE_BY = 0x603;
 
-    /**
-     * The following signatures end with 0x4b50, which in ASCII is PK,
-     * the initials of the inventor Phil Katz.
-     * See https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
-     */
-    public const FILE_HEADER_SIGNATURE = 0x04034b50;
+    private bool $ready = true;
 
-    public const CDR_FILE_SIGNATURE = 0x02014b50;
-
-    public const CDR_EOF_SIGNATURE = 0x06054b50;
-
-    public const DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
-
-    public const ZIP64_CDR_EOF_SIGNATURE = 0x06064b50;
-
-    public const ZIP64_CDR_LOCATOR_SIGNATURE = 0x07064b50;
+    private int $offset = 0;
 
     /**
-     * Global Options
-     *
-     * @var ArchiveOptions
+     * @var string[]
      */
-    public $opt;
+    private array $centralDirectoryRecords = [];
 
     /**
-     * @var array
+     * @var resource
      */
-    public $files = [];
+    private $outputStream;
+
+    private readonly Closure $httpHeaderCallback;
 
     /**
-     * @var Bigint
+     * @var File[]
      */
-    public $cdr_ofs;
-
-    /**
-     * @var Bigint
-     */
-    public $ofs;
-
-    /**
-     * @var bool
-     */
-    protected $need_headers;
-
-    /**
-     * @var null|String
-     */
-    protected $output_name;
+    private array $recordedSimulation = [];
 
     /**
      * Create a new ZipStream object.
      *
-     * Parameters:
+     * ##### Examples
      *
-     * @param String $name - Name of output file (optional).
-     * @param ArchiveOptions $opt - Archive Options
+     * ```php
+     * // create a new zip file named 'foo.zip'
+     * $zip = new ZipStream(outputName: 'foo.zip');
      *
-     * Large File Support:
+     * // create a new zip file named 'bar.zip' with a comment
+     * $zip = new ZipStream(
+     *   outputName: 'bar.zip',
+     *   comment: 'this is a comment for the zip file.',
+     * );
+     * ```
      *
-     * By default, the method addFileFromPath() will send send files
-     * larger than 20 megabytes along raw rather than attempting to
-     * compress them.  You can change both the maximum size and the
-     * compression behavior using the largeFile* options above, with the
-     * following caveats:
+     * @param OperationMode $operationMode
+     * The mode can be used to switch between `NORMAL` and `SIMULATION_*` modes.
+     * For details see the `OperationMode` documentation.
      *
-     * * For "small" files (e.g. files smaller than largeFileSize), the
-     *   memory use can be up to twice that of the actual file.  In other
-     *   words, adding a 10 megabyte file to the archive could potentially
-     *   occupy 20 megabytes of memory.
+     * Default to `NORMAL`.
      *
-     * * Enabling compression on large files (e.g. files larger than
-     *   large_file_size) is extremely slow, because ZipStream has to pass
-     *   over the large file once to calculate header information, and then
-     *   again to compress and send the actual data.
+     * @param string $comment
+     * Archive Level Comment
      *
-     * Examples:
+     * @param StreamInterface|resource|null $outputStream
+     * Override the output of the archive to a different target.
      *
-     *   // create a new zip file named 'foo.zip'
-     *   $zip = new ZipStream('foo.zip');
+     * By default the archive is sent to `STDOUT`.
      *
-     *   // create a new zip file named 'bar.zip' with a comment
-     *   $opt->setComment = 'this is a comment for the zip file.';
-     *   $zip = new ZipStream('bar.zip', $opt);
+     * @param CompressionMethod $defaultCompressionMethod
+     * How to handle file compression. Legal values are
+     * `CompressionMethod::DEFLATE` (the default), or
+     * `CompressionMethod::STORE`. `STORE` sends the file raw and is
+     * significantly faster, while `DEFLATE` compresses the file and
+     * is much, much slower.
      *
-     * Notes:
+     * @param int $defaultDeflateLevel
+     * Default deflation level. Only relevant if `compressionMethod`
+     * is `DEFLATE`.
      *
-     * In order to let this library send HTTP headers, a filename must be given
-     * _and_ the option `sendHttpHeaders` must be `true`. This behavior is to
-     * allow software to send its own headers (including the filename), and
-     * still use this library.
+     * See details of [`deflate_init`](https://www.php.net/manual/en/function.deflate-init.php#refsect1-function.deflate-init-parameters)
+     *
+     * @param bool $enableZip64
+     * Enable Zip64 extension, supporting very large
+     * archives (any size > 4 GB or file count > 64k)
+     *
+     * @param bool $defaultEnableZeroHeader
+     * Enable streaming files with single read.
+     *
+     * When the zero header is set, the file is streamed into the output
+     * and the size & checksum are added at the end of the file. This is the
+     * fastest method and uses the least memory. Unfortunately not all
+     * ZIP clients fully support this and can lead to clients reporting
+     * the generated ZIP files as corrupted in combination with other
+     * circumstances. (Zip64 enabled, using UTF8 in comments / names etc.)
+     *
+     * When the zero header is not set, the length & checksum need to be
+     * defined before the file is actually added. To prevent loading all
+     * the data into memory, the data has to be read twice. If the data
+     * which is added is not seekable, this call will fail.
+     *
+     * @param bool $sendHttpHeaders
+     * Boolean indicating whether or not to send
+     * the HTTP headers for this file.
+     *
+     * @param ?Closure $httpHeaderCallback
+     * The method called to send HTTP headers
+     *
+     * @param string|null $outputName
+     * The name of the created archive.
+     *
+     * Only relevant if `$sendHttpHeaders = true`.
+     *
+     * @param string $contentDisposition
+     * HTTP Content-Disposition
+     *
+     * Only relevant if `sendHttpHeaders = true`.
+     *
+     * @param string $contentType
+     * HTTP Content Type
+     *
+     * Only relevant if `sendHttpHeaders = true`.
+     *
+     * @param bool $flushOutput
+     * Enable flush after every write to output stream.
+     *
+     * @return self
      */
-    public function __construct(?string $name = null, ?ArchiveOptions $opt = null)
-    {
-        $this->opt = $opt ?: new ArchiveOptions();
-
-        $this->output_name = $name;
-        $this->need_headers = $name && $this->opt->isSendHttpHeaders();
-
-        $this->cdr_ofs = new Bigint();
-        $this->ofs = new Bigint();
+    public function __construct(
+        private OperationMode $operationMode = OperationMode::NORMAL,
+        private readonly string $comment = '',
+        $outputStream = null,
+        private readonly CompressionMethod $defaultCompressionMethod = CompressionMethod::DEFLATE,
+        private readonly int $defaultDeflateLevel = 6,
+        private readonly bool $enableZip64 = true,
+        private readonly bool $defaultEnableZeroHeader = true,
+        private bool $sendHttpHeaders = true,
+        ?Closure $httpHeaderCallback = null,
+        private readonly ?string $outputName = null,
+        private readonly string $contentDisposition = 'attachment',
+        private readonly string $contentType = 'application/x-zip',
+        private bool $flushOutput = false,
+    ) {
+        $this->outputStream = self::normalizeStream($outputStream);
+        $this->httpHeaderCallback = $httpHeaderCallback ?? header(...);
     }
 
     /**
-     * addFile
-     *
      * Add a file to the archive.
      *
-     * @param String $name - path of file in archive (including directory).
-     * @param String $data - contents of file
-     * @param FileOptions $options
+     * ##### File Options
      *
-     * File Options:
-     *  time     - Last-modified timestamp (seconds since the epoch) of
-     *             this file.  Defaults to the current time.
-     *  comment  - Comment related to this file.
-     *  method   - Storage method for file ("store" or "deflate")
+     * See {@see addFileFromPsr7Stream()}
      *
-     * Examples:
+     * ##### Examples
      *
-     *   // add a file named 'foo.txt'
-     *   $data = file_get_contents('foo.txt');
-     *   $zip->addFile('foo.txt', $data);
+     * ```php
+     * // add a file named 'world.txt'
+     * $zip->addFile(fileName: 'world.txt', data: 'Hello World!');
      *
-     *   // add a file named 'bar.jpg' with a comment and a last-modified
-     *   // time of two hours ago
-     *   $data = file_get_contents('bar.jpg');
-     *   $opt->setTime = time() - 2 * 3600;
-     *   $opt->setComment = 'this is a comment about bar.jpg';
-     *   $zip->addFile('bar.jpg', $data, $opt);
+     * // add a file named 'bar.jpg' with a comment and a last-modified
+     * // time of two hours ago
+     * $zip->addFile(
+     *   fileName: 'bar.jpg',
+     *   data: $data,
+     *   comment: 'this is a comment about bar.jpg',
+     *   lastModificationDateTime: new DateTime('2 hours ago'),
+     * );
+     * ```
+     *
+     * @param string $data
+     *
+     * contents of file
      */
-    public function addFile(string $name, string $data, ?FileOptions $options = null): void
-    {
-        $options = $options ?: new FileOptions();
-        $options->defaultTo($this->opt);
-
-        $file = new File($this, $name, $options);
-        $file->processData($data);
+    public function addFile(
+        string $fileName,
+        string $data,
+        string $comment = '',
+        ?CompressionMethod $compressionMethod = null,
+        ?int $deflateLevel = null,
+        ?DateTimeInterface $lastModificationDateTime = null,
+        ?int $maxSize = null,
+        ?int $exactSize = null,
+        ?bool $enableZeroHeader = null,
+    ): void {
+        $this->addFileFromCallback(
+            fileName: $fileName,
+            callback: fn () => $data,
+            comment: $comment,
+            compressionMethod: $compressionMethod,
+            deflateLevel: $deflateLevel,
+            lastModificationDateTime: $lastModificationDateTime,
+            maxSize: $maxSize,
+            exactSize: $exactSize,
+            enableZeroHeader: $enableZeroHeader,
+        );
     }
 
     /**
-     * addFileFromPath
-     *
      * Add a file at path to the archive.
      *
-     * Note that large files may be compressed differently than smaller
-     * files; see the "Large File Support" section above for more
-     * information.
+     * ##### File Options
      *
-     * @param String $name - name of file in archive (including directory path).
-     * @param String $path - path to file on disk (note: paths should be encoded using
-     *          UNIX-style forward slashes -- e.g '/path/to/some/file').
-     * @param FileOptions $options
+     * See {@see addFileFromPsr7Stream()}
      *
-     * File Options:
-     *  time     - Last-modified timestamp (seconds since the epoch) of
-     *             this file.  Defaults to the current time.
-     *  comment  - Comment related to this file.
-     *  method   - Storage method for file ("store" or "deflate")
+     * ###### Examples
      *
-     * Examples:
+     * ```php
+     * // add a file named 'foo.txt' from the local file '/tmp/foo.txt'
+     * $zip->addFileFromPath(
+     *   fileName: 'foo.txt',
+     *   path: '/tmp/foo.txt',
+     * );
      *
-     *   // add a file named 'foo.txt' from the local file '/tmp/foo.txt'
-     *   $zip->addFileFromPath('foo.txt', '/tmp/foo.txt');
+     * // add a file named 'bigfile.rar' from the local file
+     * // '/usr/share/bigfile.rar' with a comment and a last-modified
+     * // time of two hours ago
+     * $zip->addFile(
+     *   fileName: 'bigfile.rar',
+     *   path: '/usr/share/bigfile.rar',
+     *   comment: 'this is a comment about bigfile.rar',
+     *   lastModificationDateTime: new DateTime('2 hours ago'),
+     * );
+     * ```
      *
-     *   // add a file named 'bigfile.rar' from the local file
-     *   // '/usr/share/bigfile.rar' with a comment and a last-modified
-     *   // time of two hours ago
-     *   $path = '/usr/share/bigfile.rar';
-     *   $opt->setTime = time() - 2 * 3600;
-     *   $opt->setComment = 'this is a comment about bar.jpg';
-     *   $zip->addFileFromPath('bigfile.rar', $path, $opt);
-     *
-     * @return void
      * @throws \ZipStream\Exception\FileNotFoundException
      * @throws \ZipStream\Exception\FileNotReadableException
      */
-    public function addFileFromPath(string $name, string $path, ?FileOptions $options = null): void
-    {
-        $options = $options ?: new FileOptions();
-        $options->defaultTo($this->opt);
+    public function addFileFromPath(
+        /**
+         * name of file in archive (including directory path).
+         */
+        string $fileName,
 
-        $file = new File($this, $name, $options);
-        $file->processPath($path);
+        /**
+         * path to file on disk (note: paths should be encoded using
+         * UNIX-style forward slashes -- e.g '/path/to/some/file').
+         */
+        string $path,
+        string $comment = '',
+        ?CompressionMethod $compressionMethod = null,
+        ?int $deflateLevel = null,
+        ?DateTimeInterface $lastModificationDateTime = null,
+        ?int $maxSize = null,
+        ?int $exactSize = null,
+        ?bool $enableZeroHeader = null,
+    ): void {
+        if (!is_readable($path)) {
+            if (!file_exists($path)) {
+                throw new FileNotFoundException($path);
+            }
+            throw new FileNotReadableException($path);
+        }
+
+        if ($fileTime = filemtime($path)) {
+            $lastModificationDateTime ??= (new DateTimeImmutable())->setTimestamp($fileTime);
+        }
+
+        $this->addFileFromCallback(
+            fileName: $fileName,
+            callback: function () use ($path) {
+
+                $stream =  fopen($path, 'rb');
+
+                if (!$stream) {
+                    // @codeCoverageIgnoreStart
+                    throw new ResourceActionException('fopen');
+                    // @codeCoverageIgnoreEnd
+                }
+
+                return $stream;
+            },
+            comment: $comment,
+            compressionMethod: $compressionMethod,
+            deflateLevel: $deflateLevel,
+            lastModificationDateTime: $lastModificationDateTime,
+            maxSize: $maxSize,
+            exactSize: $exactSize,
+            enableZeroHeader: $enableZeroHeader,
+        );
     }
 
     /**
-     * addFileFromStream
+     * Add an open stream (resource) to the archive.
      *
-     * Add an open stream to the archive.
+     * ##### File Options
      *
-     * @param String $name - path of file in archive (including directory).
-     * @param resource $stream - contents of file as a stream resource
-     * @param FileOptions $options
+     * See {@see addFileFromPsr7Stream()}
      *
-     * File Options:
-     *  time     - Last-modified timestamp (seconds since the epoch) of
-     *             this file.  Defaults to the current time.
-     *  comment  - Comment related to this file.
+     * ##### Examples
      *
-     * Examples:
+     * ```php
+     * // create a temporary file stream and write text to it
+     * $filePointer = tmpfile();
+     * fwrite($filePointer, 'The quick brown fox jumped over the lazy dog.');
      *
-     *   // create a temporary file stream and write text to it
-     *   $fp = tmpfile();
-     *   fwrite($fp, 'The quick brown fox jumped over the lazy dog.');
+     * // add a file named 'streamfile.txt' from the content of the stream
+     * $archive->addFileFromStream(
+     *   fileName: 'streamfile.txt',
+     *   stream: $filePointer,
+     * );
+     * ```
      *
-     *   // add a file named 'streamfile.txt' from the content of the stream
-     *   $x->addFileFromStream('streamfile.txt', $fp);
-     *
-     * @return void
+     * @param resource $stream contents of file as a stream resource
      */
-    public function addFileFromStream(string $name, $stream, ?FileOptions $options = null): void
-    {
-        $options = $options ?: new FileOptions();
-        $options->defaultTo($this->opt);
-
-        $file = new File($this, $name, $options);
-        $file->processStream(new DeflateStream($stream));
+    public function addFileFromStream(
+        string $fileName,
+        $stream,
+        string $comment = '',
+        ?CompressionMethod $compressionMethod = null,
+        ?int $deflateLevel = null,
+        ?DateTimeInterface $lastModificationDateTime = null,
+        ?int $maxSize = null,
+        ?int $exactSize = null,
+        ?bool $enableZeroHeader = null,
+    ): void {
+        $this->addFileFromCallback(
+            fileName: $fileName,
+            callback: fn () => $stream,
+            comment: $comment,
+            compressionMethod: $compressionMethod,
+            deflateLevel: $deflateLevel,
+            lastModificationDateTime: $lastModificationDateTime,
+            maxSize: $maxSize,
+            exactSize: $exactSize,
+            enableZeroHeader: $enableZeroHeader,
+        );
     }
 
     /**
-     * addFileFromPsr7Stream
-     *
      * Add an open stream to the archive.
      *
-     * @param String $name - path of file in archive (including directory).
-     * @param StreamInterface $stream - contents of file as a stream resource
-     * @param FileOptions $options
+     * ##### Examples
      *
-     * File Options:
-     *  time     - Last-modified timestamp (seconds since the epoch) of
-     *             this file.  Defaults to the current time.
-     *  comment  - Comment related to this file.
+     * ```php
+     * $stream = $response->getBody();
+     * // add a file named 'streamfile.txt' from the content of the stream
+     * $archive->addFileFromPsr7Stream(
+     *   fileName: 'streamfile.txt',
+     *   stream: $stream,
+     * );
+     * ```
      *
-     * Examples:
+     * @param string $fileName
+     * path of file in archive (including directory)
      *
-     *   $stream = $response->getBody();
-     *   // add a file named 'streamfile.txt' from the content of the stream
-     *   $x->addFileFromPsr7Stream('streamfile.txt', $stream);
+     * @param StreamInterface $stream
+     * contents of file as a stream resource
      *
-     * @return void
+     * @param string $comment
+     * ZIP comment for this file
+     *
+     * @param ?CompressionMethod $compressionMethod
+     * Override `defaultCompressionMethod`
+     *
+     * See {@see __construct()}
+     *
+     * @param ?int $deflateLevel
+     * Override `defaultDeflateLevel`
+     *
+     * See {@see __construct()}
+     *
+     * @param ?DateTimeInterface $lastModificationDateTime
+     * Set last modification time of file.
+     *
+     * Default: `now`
+     *
+     * @param ?int $maxSize
+     * Only read `maxSize` bytes from file.
+     *
+     * The file is considered done when either reaching `EOF`
+     * or the `maxSize`.
+     *
+     * @param ?int $exactSize
+     * Read exactly `exactSize` bytes from file.
+     * If `EOF` is reached before reading `exactSize` bytes, an error will be
+     * thrown. The parameter allows for faster size calculations if the `stream`
+     * does not support `fstat` size or is slow and otherwise known beforehand.
+     *
+     * @param ?bool $enableZeroHeader
+     * Override `defaultEnableZeroHeader`
+     *
+     * See {@see __construct()}
      */
     public function addFileFromPsr7Stream(
-        string $name,
+        string $fileName,
         StreamInterface $stream,
-        ?FileOptions $options = null
+        string $comment = '',
+        ?CompressionMethod $compressionMethod = null,
+        ?int $deflateLevel = null,
+        ?DateTimeInterface $lastModificationDateTime = null,
+        ?int $maxSize = null,
+        ?int $exactSize = null,
+        ?bool $enableZeroHeader = null,
     ): void {
-        $options = $options ?: new FileOptions();
-        $options->defaultTo($this->opt);
-
-        $file = new File($this, $name, $options);
-        $file->processStream($stream);
+        $this->addFileFromCallback(
+            fileName: $fileName,
+            callback: fn () => $stream,
+            comment: $comment,
+            compressionMethod: $compressionMethod,
+            deflateLevel: $deflateLevel,
+            lastModificationDateTime: $lastModificationDateTime,
+            maxSize: $maxSize,
+            exactSize: $exactSize,
+            enableZeroHeader: $enableZeroHeader,
+        );
     }
 
     /**
-     * finish
+     * Add a file based on a callback.
      *
+     * This is useful when you want to simulate a lot of files without keeping
+     * all of the file handles open at the same time.
+     *
+     * ##### Examples
+     *
+     * ```php
+     * foreach($files as $name => $size) {
+     *   $archive->addFileFromPsr7Stream(
+     *     fileName: 'streamfile.txt',
+     *     exactSize: $size,
+     *     callback: function() use($name): Psr\Http\Message\StreamInterface {
+     *       $response = download($name);
+     *       return $response->getBody();
+     *     }
+     *   );
+     * }
+     * ```
+     *
+     * @param string $fileName
+     * path of file in archive (including directory)
+     *
+     * @param Closure $callback
+     * @psalm-param Closure(): (resource|StreamInterface|string) $callback
+     * A callback to get the file contents in the shape of a PHP stream,
+     * a Psr StreamInterface implementation, or a string.
+     *
+     * @param string $comment
+     * ZIP comment for this file
+     *
+     * @param ?CompressionMethod $compressionMethod
+     * Override `defaultCompressionMethod`
+     *
+     * See {@see __construct()}
+     *
+     * @param ?int $deflateLevel
+     * Override `defaultDeflateLevel`
+     *
+     * See {@see __construct()}
+     *
+     * @param ?DateTimeInterface $lastModificationDateTime
+     * Set last modification time of file.
+     *
+     * Default: `now`
+     *
+     * @param ?int $maxSize
+     * Only read `maxSize` bytes from file.
+     *
+     * The file is considered done when either reaching `EOF`
+     * or the `maxSize`.
+     *
+     * @param ?int $exactSize
+     * Read exactly `exactSize` bytes from file.
+     * If `EOF` is reached before reading `exactSize` bytes, an error will be
+     * thrown. The parameter allows for faster size calculations if the `stream`
+     * does not support `fstat` size or is slow and otherwise known beforehand.
+     *
+     * @param ?bool $enableZeroHeader
+     * Override `defaultEnableZeroHeader`
+     *
+     * See {@see __construct()}
+     */
+    public function addFileFromCallback(
+        string $fileName,
+        Closure $callback,
+        string $comment = '',
+        ?CompressionMethod $compressionMethod = null,
+        ?int $deflateLevel = null,
+        ?DateTimeInterface $lastModificationDateTime = null,
+        ?int $maxSize = null,
+        ?int $exactSize = null,
+        ?bool $enableZeroHeader = null,
+    ): void {
+        $file = new File(
+            dataCallback: function () use ($callback, $maxSize) {
+                $data = $callback();
+
+                if(is_resource($data)) {
+                    return $data;
+                }
+
+                if($data instanceof StreamInterface) {
+                    return StreamWrapper::getResource($data);
+                }
+
+
+                $stream = fopen('php://memory', 'rw+');
+                if ($stream === false) {
+                    // @codeCoverageIgnoreStart
+                    throw new ResourceActionException('fopen');
+                    // @codeCoverageIgnoreEnd
+                }
+                if ($maxSize !== null && fwrite($stream, $data, $maxSize) === false) {
+                    // @codeCoverageIgnoreStart
+                    throw new ResourceActionException('fwrite', $stream);
+                    // @codeCoverageIgnoreEnd
+                } elseif (fwrite($stream, $data) === false) {
+                    // @codeCoverageIgnoreStart
+                    throw new ResourceActionException('fwrite', $stream);
+                    // @codeCoverageIgnoreEnd
+                }
+                if (rewind($stream) === false) {
+                    // @codeCoverageIgnoreStart
+                    throw new ResourceActionException('rewind', $stream);
+                    // @codeCoverageIgnoreEnd
+                }
+
+                return $stream;
+
+            },
+            send: $this->send(...),
+            recordSentBytes: $this->recordSentBytes(...),
+            operationMode: $this->operationMode,
+            fileName: $fileName,
+            startOffset: $this->offset,
+            compressionMethod: $compressionMethod ?? $this->defaultCompressionMethod,
+            comment: $comment,
+            deflateLevel: $deflateLevel ?? $this->defaultDeflateLevel,
+            lastModificationDateTime: $lastModificationDateTime ?? new DateTimeImmutable(),
+            maxSize: $maxSize,
+            exactSize: $exactSize,
+            enableZip64: $this->enableZip64,
+            enableZeroHeader: $enableZeroHeader ?? $this->defaultEnableZeroHeader,
+        );
+
+        if($this->operationMode !== OperationMode::NORMAL) {
+            $this->recordedSimulation[] = $file;
+        }
+
+        $this->centralDirectoryRecords[] = $file->process();
+    }
+
+    /**
+     * Add a directory to the archive.
+     *
+     * ##### File Options
+     *
+     * See {@see addFileFromPsr7Stream()}
+     *
+     * ##### Examples
+     *
+     * ```php
+     * // add a directory named 'world/'
+     * $zip->addFile(fileName: 'world/');
+     * ```
+     */
+    public function addDirectory(
+        string $fileName,
+        string $comment = '',
+        ?DateTimeInterface $lastModificationDateTime = null,
+    ): void {
+        if (!str_ends_with($fileName, '/')) {
+            $fileName .= '/';
+        }
+
+        $this->addFile(
+            fileName: $fileName,
+            data: '',
+            comment: $comment,
+            compressionMethod: CompressionMethod::STORE,
+            deflateLevel: null,
+            lastModificationDateTime: $lastModificationDateTime,
+            maxSize: 0,
+            exactSize: 0,
+            enableZeroHeader: false,
+        );
+    }
+
+    /**
+     * Executes a previously calculated simulation.
+     *
+     * ##### Example
+     *
+     * ```php
+     * $zip = new ZipStream(
+     *   outputName: 'foo.zip',
+     *   operationMode: OperationMode::SIMULATE_STRICT,
+     * );
+     *
+     * $zip->addFile('test.txt', 'Hello World');
+     *
+     * $size = $zip->finish();
+     *
+     * header('Content-Length: '. $size);
+     *
+     * $zip->executeSimulation();
+     * ```
+     */
+    public function executeSimulation(): void
+    {
+        if($this->operationMode !== OperationMode::NORMAL) {
+            throw new RuntimeException('Zip simulation is not finished.');
+        }
+
+        foreach($this->recordedSimulation as $file) {
+            $this->centralDirectoryRecords[] = $file->cloneSimulationExecution()->process();
+        }
+
+        $this->finish();
+    }
+
+    /**
      * Write zip footer to stream.
      *
-     *  Example:
+     * The clase is left in an unusable state after `finish`.
      *
-     *   // add a list of files to the archive
-     *   $files = array('foo.txt', 'bar.jpg');
-     *   foreach ($files as $path)
-     *     $zip->addFile($path, file_get_contents($path));
+     * ##### Example
      *
-     *   // write footer to stream
-     *   $zip->finish();
-     * @return void
-     *
-     * @throws OverflowException
+     * ```php
+     * // write footer to stream
+     * $zip->finish();
+     * ```
      */
-    public function finish(): void
+    public function finish(): int
     {
+        $centralDirectoryStartOffsetOnDisk = $this->offset;
+        $sizeOfCentralDirectory = 0;
+
         // add trailing cdr file records
-        foreach ($this->files as $cdrFile) {
-            $this->send($cdrFile);
-            $this->cdr_ofs = $this->cdr_ofs->add(Bigint::init(strlen($cdrFile)));
+        foreach ($this->centralDirectoryRecords as $centralDirectoryRecord) {
+            $this->send($centralDirectoryRecord);
+            $sizeOfCentralDirectory += strlen($centralDirectoryRecord);
         }
 
         // Add 64bit headers (if applicable)
-        if (count($this->files) >= 0xFFFF ||
-            $this->cdr_ofs->isOver32() ||
-            $this->ofs->isOver32()) {
-            if (!$this->opt->isEnableZip64()) {
+        if (count($this->centralDirectoryRecords) >= 0xFFFF ||
+            $centralDirectoryStartOffsetOnDisk > 0xFFFFFFFF ||
+            $sizeOfCentralDirectory > 0xFFFFFFFF) {
+            if (!$this->enableZip64) {
                 throw new OverflowException();
             }
 
-            $this->addCdr64Eof();
-            $this->addCdr64Locator();
+            $this->send(Zip64\EndOfCentralDirectory::generate(
+                versionMadeBy: self::ZIP_VERSION_MADE_BY,
+                versionNeededToExtract: Version::ZIP64->value,
+                numberOfThisDisk: 0,
+                numberOfTheDiskWithCentralDirectoryStart: 0,
+                numberOfCentralDirectoryEntriesOnThisDisk: count($this->centralDirectoryRecords),
+                numberOfCentralDirectoryEntries: count($this->centralDirectoryRecords),
+                sizeOfCentralDirectory: $sizeOfCentralDirectory,
+                centralDirectoryStartOffsetOnDisk: $centralDirectoryStartOffsetOnDisk,
+                extensibleDataSector: '',
+            ));
+
+            $this->send(Zip64\EndOfCentralDirectoryLocator::generate(
+                numberOfTheDiskWithZip64CentralDirectoryStart: 0x00,
+                zip64centralDirectoryStartOffsetOnDisk: $centralDirectoryStartOffsetOnDisk + $sizeOfCentralDirectory,
+                totalNumberOfDisks: 1,
+            ));
         }
 
         // add trailing cdr eof record
-        $this->addCdrEof();
+        $numberOfCentralDirectoryEntries = min(count($this->centralDirectoryRecords), 0xFFFF);
+        $this->send(EndOfCentralDirectory::generate(
+            numberOfThisDisk: 0x00,
+            numberOfTheDiskWithCentralDirectoryStart: 0x00,
+            numberOfCentralDirectoryEntriesOnThisDisk: $numberOfCentralDirectoryEntries,
+            numberOfCentralDirectoryEntries: $numberOfCentralDirectoryEntries,
+            sizeOfCentralDirectory: min($sizeOfCentralDirectory, 0xFFFFFFFF),
+            centralDirectoryStartOffsetOnDisk: min($centralDirectoryStartOffsetOnDisk, 0xFFFFFFFF),
+            zipFileComment: $this->comment,
+        ));
+
+        $size = $this->offset;
 
         // The End
         $this->clear();
+
+        return $size;
     }
 
     /**
-     * Create a format string and argument list for pack(), then call
-     * pack() and return the result.
-     *
-     * @param array $fields
-     * @return string
+     * @param StreamInterface|resource|null $outputStream
+     * @return resource
      */
-    public static function packFields(array $fields): string
+    private static function normalizeStream($outputStream)
     {
-        $fmt = '';
-        $args = [];
-
-        // populate format string and argument list
-        foreach ($fields as [$format, $value]) {
-            if ($format === 'P') {
-                $fmt .= 'VV';
-                if ($value instanceof Bigint) {
-                    $args[] = $value->getLow32();
-                    $args[] = $value->getHigh32();
-                } else {
-                    $args[] = $value;
-                    $args[] = 0;
-                }
-            } else {
-                if ($value instanceof Bigint) {
-                    $value = $value->getLow32();
-                }
-                $fmt .= $format;
-                $args[] = $value;
-            }
+        if ($outputStream instanceof StreamInterface) {
+            return StreamWrapper::getResource($outputStream);
         }
+        if (is_resource($outputStream)) {
+            return $outputStream;
+        }
+        return fopen('php://output', 'wb');
+    }
 
-        // prepend format string to argument list
-        array_unshift($args, $fmt);
-
-        // build output string from header and compressed data
-        return pack(...$args);
+    /**
+     * Record sent bytes
+     */
+    private function recordSentBytes(int $sentBytes): void
+    {
+        $this->offset += $sentBytes;
     }
 
     /**
      * Send string, sending HTTP headers if necessary.
      * Flush output after write if configure option is set.
-     *
-     * @param String $str
-     * @return void
      */
-    public function send(string $str): void
+    private function send(string $data): void
     {
-        if ($this->need_headers) {
+        if (!$this->ready) {
+            throw new RuntimeException('Archive is already finished');
+        }
+
+        if ($this->operationMode === OperationMode::NORMAL && $this->sendHttpHeaders) {
             $this->sendHttpHeaders();
-        }
-        $this->need_headers = false;
-
-        $outputStream = $this->opt->getOutputStream();
-
-        if ($outputStream instanceof StreamInterface) {
-            $outputStream->write($str);
-        } else {
-            fwrite($outputStream, $str);
+            $this->sendHttpHeaders = false;
         }
 
-        if ($this->opt->isFlushOutput()) {
-            // flush output buffer if it is on and flushable
-            $status = ob_get_status();
-            if (isset($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
-                ob_flush();
+        $this->recordSentBytes(strlen($data));
+
+        if ($this->operationMode === OperationMode::NORMAL) {
+            if (fwrite($this->outputStream, $data) === false) {
+                throw new ResourceActionException('fwrite', $this->outputStream);
             }
 
-            // Flush system buffers after flushing userspace output buffer
-            flush();
+            if ($this->flushOutput) {
+                // flush output buffer if it is on and flushable
+                $status = ob_get_status();
+                if (isset($status['flags']) && is_int($status['flags']) && ($status['flags'] & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
+                    ob_flush();
+                }
+
+                // Flush system buffers after flushing userspace output buffer
+                flush();
+            }
         }
     }
 
-    /**
-     * Is this file larger than large_file_size?
-     *
-     * @param string $path
-     * @return bool
-     */
-    public function isLargeFile(string $path): bool
-    {
-        if (!$this->opt->isStatFiles()) {
-            return false;
-        }
-        $stat = stat($path);
-        return $stat['size'] > $this->opt->getLargeFileSize();
-    }
-
-    /**
-     * Save file attributes for trailing CDR record.
-     *
-     * @param File $file
-     * @return void
-     */
-    public function addToCdr(File $file): void
-    {
-        $file->ofs = $this->ofs;
-        $this->ofs = $this->ofs->add($file->getTotalLength());
-        $this->files[] = $file->getCdrFile();
-    }
-
-    /**
-     * Send ZIP64 CDR EOF (Central Directory Record End-of-File) record.
-     *
-     * @return void
-     */
-    protected function addCdr64Eof(): void
-    {
-        $num_files = count($this->files);
-        $cdr_length = $this->cdr_ofs;
-        $cdr_offset = $this->ofs;
-
-        $fields = [
-            ['V', static::ZIP64_CDR_EOF_SIGNATURE],     // ZIP64 end of central file header signature
-            ['P', 44],                                  // Length of data below this header (length of block - 12) = 44
-            ['v', static::ZIP_VERSION_MADE_BY],         // Made by version
-            ['v', Version::ZIP64],                      // Extract by version
-            ['V', 0x00],                                // disk number
-            ['V', 0x00],                                // no of disks
-            ['P', $num_files],                          // no of entries on disk
-            ['P', $num_files],                          // no of entries in cdr
-            ['P', $cdr_length],                         // CDR size
-            ['P', $cdr_offset],                         // CDR offset
-        ];
-
-        $ret = static::packFields($fields);
-        $this->send($ret);
-    }
-
-    /**
+     /**
      * Send HTTP headers for this stream.
-     *
-     * @return void
      */
-    protected function sendHttpHeaders(): void
+    private function sendHttpHeaders(): void
     {
         // grab content disposition
-        $disposition = $this->opt->getContentDisposition();
+        $disposition = $this->contentDisposition;
 
-        if ($this->output_name) {
+        if ($this->outputName) {
             // Various different browsers dislike various characters here. Strip them all for safety.
-            $safe_output = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->output_name));
+            $safeOutput = trim(str_replace(['"', "'", '\\', ';', "\n", "\r"], '', $this->outputName));
 
             // Check if we need to UTF-8 encode the filename
-            $urlencoded = rawurlencode($safe_output);
+            $urlencoded = rawurlencode($safeOutput);
             $disposition .= "; filename*=UTF-8''{$urlencoded}";
         }
 
         $headers = [
-            'Content-Type' => $this->opt->getContentType(),
+            'Content-Type' => $this->contentType,
             'Content-Disposition' => $disposition,
             'Pragma' => 'public',
             'Cache-Control' => 'public, must-revalidate',
             'Content-Transfer-Encoding' => 'binary',
         ];
 
-        $call = $this->opt->getHttpHeaderCallback();
         foreach ($headers as $key => $val) {
-            $call("$key: $val");
+            ($this->httpHeaderCallback)("$key: $val");
         }
     }
 
-    /**
-     * Send ZIP64 CDR Locator (Central Directory Record Locator) record.
-     *
-     * @return void
-     */
-    protected function addCdr64Locator(): void
-    {
-        $cdr_offset = $this->ofs->add($this->cdr_ofs);
-
-        $fields = [
-            ['V', static::ZIP64_CDR_LOCATOR_SIGNATURE], // ZIP64 end of central file header signature
-            ['V', 0x00],                                // Disc number containing CDR64EOF
-            ['P', $cdr_offset],                         // CDR offset
-            ['V', 1],                                   // Total number of disks
-        ];
-
-        $ret = static::packFields($fields);
-        $this->send($ret);
-    }
-
-    /**
-     * Send CDR EOF (Central Directory Record End-of-File) record.
-     *
-     * @return void
-     */
-    protected function addCdrEof(): void
-    {
-        $num_files = count($this->files);
-        $cdr_length = $this->cdr_ofs;
-        $cdr_offset = $this->ofs;
-
-        // grab comment (if specified)
-        $comment = $this->opt->getComment();
-
-        $fields = [
-            ['V', static::CDR_EOF_SIGNATURE],   // end of central file header signature
-            ['v', 0x00],                        // disk number
-            ['v', 0x00],                        // no of disks
-            ['v', min($num_files, 0xFFFF)],     // no of entries on disk
-            ['v', min($num_files, 0xFFFF)],     // no of entries in cdr
-            ['V', $cdr_length->getLowFF()],     // CDR size
-            ['V', $cdr_offset->getLowFF()],     // CDR offset
-            ['v', strlen($comment)],            // Zip Comment size
-        ];
-
-        $ret = static::packFields($fields) . $comment;
-        $this->send($ret);
-    }
-
     /**
      * Clear all internal variables. Note that the stream object is not
      * usable after this.
-     *
-     * @return void
      */
-    protected function clear(): void
+    private function clear(): void
     {
-        $this->files = [];
-        $this->ofs = new Bigint();
-        $this->cdr_ofs = new Bigint();
-        $this->opt = new ArchiveOptions();
+        $this->centralDirectoryRecords = [];
+        $this->offset = 0;
+
+        if($this->operationMode === OperationMode::NORMAL) {
+            $this->ready = false;
+            $this->recordedSimulation = [];
+        } else {
+            $this->operationMode = OperationMode::NORMAL;
+        }
     }
 }
diff --git a/vendor/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php b/vendor/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php
new file mode 100644
index 0000000..bf621bc
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/src/Zs/ExtendedInformationExtraField.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Zs;
+
+use ZipStream\PackField;
+
+/**
+ * @internal
+ */
+abstract class ExtendedInformationExtraField
+{
+    private const TAG = 0x5653;
+
+    public static function generate(): string
+    {
+        return PackField::pack(
+            new PackField(format: 'v', value: self::TAG),
+            new PackField(format: 'v', value: 0x0000),
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Assertions.php b/vendor/maennchen/zipstream-php/test/Assertions.php
new file mode 100644
index 0000000..900b7cc
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Assertions.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+trait Assertions
+{
+    protected function assertFileContains(string $filePath, string $needle): void
+    {
+        $last = '';
+
+        $handle = fopen($filePath, 'r');
+        while (!feof($handle)) {
+            $line = fgets($handle, 1024);
+
+            if(str_contains($last . $line, $needle)) {
+                fclose($handle);
+                return;
+            }
+
+            $last = $line;
+        }
+
+        fclose($handle);
+
+        $this->fail("File {$filePath} must contain {$needle}");
+    }
+
+    protected function assertFileDoesNotContain(string $filePath, string $needle): void
+    {
+        $last = '';
+
+        $handle = fopen($filePath, 'r');
+        while (!feof($handle)) {
+            $line = fgets($handle, 1024);
+
+            if(str_contains($last . $line, $needle)) {
+                fclose($handle);
+
+                $this->fail("File {$filePath} must not contain {$needle}");
+            }
+
+            $last = $line;
+        }
+
+        fclose($handle);
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/BigintTest.php b/vendor/maennchen/zipstream-php/test/BigintTest.php
deleted file mode 100644
index 4d26fcd..0000000
--- a/vendor/maennchen/zipstream-php/test/BigintTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace BigintTest;
-
-use OverflowException;
-use PHPUnit\Framework\TestCase;
-use ZipStream\Bigint;
-
-class BigintTest extends TestCase
-{
-    public function testConstruct(): void
-    {
-        $bigint = new Bigint(0x12345678);
-        $this->assertSame('0x0000000012345678', $bigint->getHex64());
-        $this->assertSame(0x12345678, $bigint->getLow32());
-        $this->assertSame(0, $bigint->getHigh32());
-    }
-
-    public function testConstructLarge(): void
-    {
-        $bigint = new Bigint(0x87654321);
-        $this->assertSame('0x0000000087654321', $bigint->getHex64());
-        $this->assertSame('87654321', bin2hex(pack('N', $bigint->getLow32())));
-        $this->assertSame(0, $bigint->getHigh32());
-    }
-
-    public function testAddSmallValue(): void
-    {
-        $bigint = new Bigint(1);
-        $bigint = $bigint->add(Bigint::init(2));
-        $this->assertSame(3, $bigint->getLow32());
-        $this->assertFalse($bigint->isOver32());
-        $this->assertTrue($bigint->isOver32(true));
-        $this->assertSame($bigint->getLowFF(), (float)$bigint->getLow32());
-        $this->assertSame($bigint->getLowFF(true), (float)0xFFFFFFFF);
-    }
-
-    public function testAddWithOverflowAtLowestByte(): void
-    {
-        $bigint = new Bigint(0xFF);
-        $bigint = $bigint->add(Bigint::init(0x01));
-        $this->assertSame(0x100, $bigint->getLow32());
-    }
-
-    public function testAddWithOverflowAtInteger32(): void
-    {
-        $bigint = new Bigint(0xFFFFFFFE);
-        $this->assertFalse($bigint->isOver32());
-        $bigint = $bigint->add(Bigint::init(0x01));
-        $this->assertTrue($bigint->isOver32());
-        $bigint = $bigint->add(Bigint::init(0x01));
-        $this->assertSame('0x0000000100000000', $bigint->getHex64());
-        $this->assertTrue($bigint->isOver32());
-        $this->assertSame((float)0xFFFFFFFF, $bigint->getLowFF());
-    }
-
-    public function testAddWithOverflowAtInteger64(): void
-    {
-        $bigint = Bigint::fromLowHigh(0xFFFFFFFF, 0xFFFFFFFF);
-        $this->assertSame('0xFFFFFFFFFFFFFFFF', $bigint->getHex64());
-        $this->expectException(OverflowException::class);
-        $bigint->add(Bigint::init(1));
-    }
-}
diff --git a/vendor/maennchen/zipstream-php/test/CentralDirectoryFileHeaderTest.php b/vendor/maennchen/zipstream-php/test/CentralDirectoryFileHeaderTest.php
new file mode 100644
index 0000000..5457b4f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/CentralDirectoryFileHeaderTest.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use DateTimeImmutable;
+use PHPUnit\Framework\TestCase;
+use ZipStream\CentralDirectoryFileHeader;
+use ZipStream\CompressionMethod;
+
+class CentralDirectoryFileHeaderTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $dateTime = new DateTimeImmutable('2022-01-01 01:01:01Z');
+
+        $header = CentralDirectoryFileHeader::generate(
+            versionMadeBy: 0x603,
+            versionNeededToExtract: 0x002D,
+            generalPurposeBitFlag: 0x2222,
+            compressionMethod: CompressionMethod::DEFLATE,
+            lastModificationDateTime: $dateTime,
+            crc32: 0x11111111,
+            compressedSize: 0x77777777,
+            uncompressedSize: 0x99999999,
+            fileName: 'test.png',
+            extraField: 'some content',
+            fileComment: 'some comment',
+            diskNumberStart: 0,
+            internalFileAttributes: 0,
+            externalFileAttributes: 32,
+            relativeOffsetOfLocalHeader: 0x1234,
+        );
+
+        $this->assertSame(
+            bin2hex($header),
+            '504b0102' . // 4 bytes; central file header signature
+            '0306' . // 2 bytes; version made by
+            '2d00' . // 2 bytes; version needed to extract
+            '2222' . // 2 bytes; general purpose bit flag
+            '0800' . // 2 bytes; compression method
+            '2008' . // 2 bytes; last mod file time
+            '2154' . // 2 bytes; last mod file date
+            '11111111' . // 4 bytes; crc-32
+            '77777777' . // 4 bytes; compressed size
+            '99999999' . // 4 bytes; uncompressed size
+            '0800' . // 2 bytes; file name length (n)
+            '0c00' . // 2 bytes; extra field length (m)
+            '0c00' . // 2 bytes; file comment length (o)
+            '0000' . // 2 bytes; disk number start
+            '0000' . // 2 bytes; internal file attributes
+            '20000000' . // 4 bytes; external file attributes
+            '34120000' . // 4 bytes; relative offset of local header
+            '746573742e706e67' . // n bytes; file name
+            '736f6d6520636f6e74656e74' . // m bytes; extra field
+            '736f6d6520636f6d6d656e74' // o bytes; file comment
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/DataDescriptorTest.php b/vendor/maennchen/zipstream-php/test/DataDescriptorTest.php
new file mode 100644
index 0000000..cc886c7
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/DataDescriptorTest.php
@@ -0,0 +1,26 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\DataDescriptor;
+
+class DataDescriptorTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $this->assertSame(
+            bin2hex(DataDescriptor::generate(
+                crc32UncompressedData: 0x11111111,
+                compressedSize: 0x77777777,
+                uncompressedSize: 0x99999999,
+            )),
+            '504b0708' . // 4 bytes; Optional data descriptor signature = 0x08074b50
+            '11111111' . // 4 bytes; CRC-32 of uncompressed data
+            '77777777' . // 4 bytes; Compressed size
+            '99999999' // 4 bytes; Uncompressed size
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/EndOfCentralDirectoryTest.php b/vendor/maennchen/zipstream-php/test/EndOfCentralDirectoryTest.php
new file mode 100644
index 0000000..be0a907
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/EndOfCentralDirectoryTest.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\EndOfCentralDirectory;
+
+class EndOfCentralDirectoryTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $this->assertSame(
+            bin2hex(EndOfCentralDirectory::generate(
+                numberOfThisDisk: 0x00,
+                numberOfTheDiskWithCentralDirectoryStart: 0x00,
+                numberOfCentralDirectoryEntriesOnThisDisk: 0x10,
+                numberOfCentralDirectoryEntries: 0x10,
+                sizeOfCentralDirectory: 0x22,
+                centralDirectoryStartOffsetOnDisk: 0x33,
+                zipFileComment: 'foo',
+            )),
+            '504b0506' . // 4 bytes; end of central dir signature 0x06054b50
+            '0000' . // 2 bytes; number of this disk
+            '0000' . // 2 bytes; number of the disk with the start of the central directory
+            '1000' . // 2 bytes; total number of entries in the central directory on this disk
+            '1000' . // 2 bytes; total number of entries in the central directory
+            '22000000' . // 4 bytes; size of the central directory
+            '33000000' . // 4 bytes; offset of start of central directory with respect to the starting disk number
+            '0300' . // 2 bytes; .ZIP file comment length
+            bin2hex('foo')
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/EndlessCycleStream.php b/vendor/maennchen/zipstream-php/test/EndlessCycleStream.php
new file mode 100644
index 0000000..4f1cf5c
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/EndlessCycleStream.php
@@ -0,0 +1,106 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+
+class EndlessCycleStream implements StreamInterface
+{
+    private int $offset = 0;
+
+    public function __construct(private readonly string $toRepeat = '0')
+    {
+    }
+
+    public function __toString(): string
+    {
+        throw new RuntimeException('Infinite Stream!');
+    }
+
+    public function close(): void
+    {
+        $this->detach();
+    }
+
+    /**
+     * @return null
+     */
+    public function detach()
+    {
+        return;
+    }
+
+    public function getSize(): ?int
+    {
+        return null;
+    }
+
+    public function tell(): int
+    {
+        return $this->offset;
+    }
+
+    public function eof(): bool
+    {
+        return false;
+    }
+
+    public function isSeekable(): bool
+    {
+        return true;
+    }
+
+    public function seek(int $offset, int $whence = SEEK_SET): void
+    {
+        switch($whence) {
+            case SEEK_SET:
+                $this->offset = $offset;
+                break;
+            case SEEK_CUR:
+                $this->offset += $offset;
+                break;
+            case SEEK_END:
+                throw new RuntimeException('Infinite Stream!');
+                break;
+        }
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function isWritable(): bool
+    {
+        return false;
+    }
+
+    public function write(string $string): int
+    {
+        throw new RuntimeException('Not writeable');
+    }
+
+    public function isReadable(): bool
+    {
+        return true;
+    }
+
+    public function read(int $length): string
+    {
+        $this->offset += $length;
+        return substr(str_repeat($this->toRepeat, (int) ceil($length / strlen($this->toRepeat))), 0, $length);
+    }
+
+    public function getContents(): string
+    {
+        throw new RuntimeException('Infinite Stream!');
+    }
+
+    public function getMetadata(?string $key = null): array|null
+    {
+        return $key !== null ? null : [];
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/FaultInjectionResource.php b/vendor/maennchen/zipstream-php/test/FaultInjectionResource.php
new file mode 100644
index 0000000..3d4440e
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/FaultInjectionResource.php
@@ -0,0 +1,141 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+class FaultInjectionResource
+{
+    public const NAME = 'zipstream-php-test-broken-resource';
+
+    /** @var resource */
+    public $context;
+
+    private array $injectFaults;
+
+    private string $mode;
+
+    /**
+     * @return resource
+     */
+    public static function getResource(array $injectFaults)
+    {
+        self::register();
+
+        return fopen(self::NAME . '://foobar', 'rw+', false, self::createStreamContext($injectFaults));
+    }
+
+    public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool
+    {
+        $options = stream_context_get_options($this->context);
+
+        if (!isset($options[self::NAME]['injectFaults'])) {
+            return false;
+        }
+
+        $this->mode = $mode;
+        $this->injectFaults = $options[self::NAME]['injectFaults'];
+
+        if ($this->shouldFail(__FUNCTION__)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public function stream_write(string $data)
+    {
+        if ($this->shouldFail(__FUNCTION__)) {
+            return false;
+        }
+        return true;
+    }
+
+    public function stream_eof()
+    {
+        return true;
+    }
+
+    public function stream_seek(int $offset, int $whence): bool
+    {
+        if ($this->shouldFail(__FUNCTION__)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public function stream_tell(): int
+    {
+        if ($this->shouldFail(__FUNCTION__)) {
+            return false;
+        }
+
+        return 0;
+    }
+
+    public static function register(): void
+    {
+        if (!in_array(self::NAME, stream_get_wrappers(), true)) {
+            stream_wrapper_register(self::NAME, __CLASS__);
+        }
+    }
+
+    public function stream_stat(): array
+    {
+        static $modeMap = [
+            'r'  => 33060,
+            'rb' => 33060,
+            'r+' => 33206,
+            'w'  => 33188,
+            'wb' => 33188,
+        ];
+
+        return [
+            'dev'     => 0,
+            'ino'     => 0,
+            'mode'    => $modeMap[$this->mode],
+            'nlink'   => 0,
+            'uid'     => 0,
+            'gid'     => 0,
+            'rdev'    => 0,
+            'size'    => 0,
+            'atime'   => 0,
+            'mtime'   => 0,
+            'ctime'   => 0,
+            'blksize' => 0,
+            'blocks'  => 0,
+        ];
+    }
+
+    public function url_stat(string $path, int $flags): array
+    {
+        return [
+            'dev'     => 0,
+            'ino'     => 0,
+            'mode'    => 0,
+            'nlink'   => 0,
+            'uid'     => 0,
+            'gid'     => 0,
+            'rdev'    => 0,
+            'size'    => 0,
+            'atime'   => 0,
+            'mtime'   => 0,
+            'ctime'   => 0,
+            'blksize' => 0,
+            'blocks'  => 0,
+        ];
+    }
+
+    private static function createStreamContext(array $injectFaults)
+    {
+        return stream_context_create([
+            self::NAME => ['injectFaults' => $injectFaults],
+        ]);
+    }
+
+    private function shouldFail(string $function): bool
+    {
+        return in_array($function, $this->injectFaults, true);
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/LocalFileHeaderTest.php b/vendor/maennchen/zipstream-php/test/LocalFileHeaderTest.php
new file mode 100644
index 0000000..196dd0f
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/LocalFileHeaderTest.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use DateTimeImmutable;
+use PHPUnit\Framework\TestCase;
+use ZipStream\CompressionMethod;
+use ZipStream\LocalFileHeader;
+
+class LocalFileHeaderTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $dateTime = new DateTimeImmutable('2022-01-01 01:01:01Z');
+
+        $header = LocalFileHeader::generate(
+            versionNeededToExtract: 0x002D,
+            generalPurposeBitFlag: 0x2222,
+            compressionMethod: CompressionMethod::DEFLATE,
+            lastModificationDateTime: $dateTime,
+            crc32UncompressedData: 0x11111111,
+            compressedSize: 0x77777777,
+            uncompressedSize: 0x99999999,
+            fileName: 'test.png',
+            extraField: 'some content'
+        );
+
+        $this->assertSame(
+            bin2hex((string) $header),
+            '504b0304' . // 4 bytes; Local file header signature
+            '2d00' . // 2 bytes; Version needed to extract (minimum)
+            '2222' . // 2 bytes; General purpose bit flag
+            '0800' . // 2 bytes; Compression method; e.g. none = 0, DEFLATE = 8
+            '2008' . // 2 bytes; File last modification time
+            '2154' . // 2 bytes; File last modification date
+            '11111111' . // 4 bytes; CRC-32 of uncompressed data
+            '77777777' . // 4 bytes; Compressed size (or 0xffffffff for ZIP64)
+            '99999999' . // 4 bytes; Uncompressed size (or 0xffffffff for ZIP64)
+            '0800' . // 2 bytes; File name length (n)
+            '0c00' . // 2 bytes; Extra field length (m)
+            '746573742e706e67' . // n bytes; File name
+            '736f6d6520636f6e74656e74' // m bytes; Extra field
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/PackFieldTest.php b/vendor/maennchen/zipstream-php/test/PackFieldTest.php
new file mode 100644
index 0000000..ecd66ba
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/PackFieldTest.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use PHPUnit\Framework\TestCase;
+use RuntimeException;
+use ZipStream\PackField;
+
+class PackFieldTest extends TestCase
+{
+    public function testPacksFields(): void
+    {
+        $this->assertSame(
+            bin2hex(PackField::pack(new PackField(format: 'v', value: 0x1122))),
+            '2211',
+        );
+    }
+
+    public function testOverflow2(): void
+    {
+        $this->expectException(RuntimeException::class);
+
+        PackField::pack(new PackField(format: 'v', value: 0xFFFFF));
+    }
+
+    public function testOverflow4(): void
+    {
+        $this->expectException(RuntimeException::class);
+
+        PackField::pack(new PackField(format: 'V', value: 0xFFFFFFFFF));
+    }
+
+    public function testUnknownOperator(): void
+    {
+        $this->assertSame(
+            bin2hex(PackField::pack(new PackField(format: 'a', value: 0x1122))),
+            '34',
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/ResourceStream.php b/vendor/maennchen/zipstream-php/test/ResourceStream.php
new file mode 100644
index 0000000..8a41471
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/ResourceStream.php
@@ -0,0 +1,160 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
+
+/**
+ * @internal
+ */
+class ResourceStream implements StreamInterface
+{
+    public function __construct(
+        /**
+         * @var resource
+         */
+        private $stream
+    ) {
+    }
+
+    public function __toString(): string
+    {
+        if ($this->isSeekable()) {
+            $this->seek(0);
+        }
+        return (string) stream_get_contents($this->stream);
+    }
+
+    public function close(): void
+    {
+        $stream = $this->detach();
+        if ($stream) {
+            fclose($stream);
+        }
+    }
+
+    public function detach()
+    {
+        $result = $this->stream;
+        // According to the interface, the stream is left in an unusable state;
+        /** @psalm-suppress PossiblyNullPropertyAssignmentValue */
+        $this->stream = null;
+        return $result;
+    }
+
+    public function seek(int $offset, int $whence = SEEK_SET): void
+    {
+        if (!$this->isSeekable()) {
+            throw new RuntimeException();
+        }
+        if (fseek($this->stream, $offset, $whence) !== 0) {
+            // @codeCoverageIgnoreStart
+            throw new RuntimeException();
+            // @codeCoverageIgnoreEnd
+        }
+    }
+
+    public function isSeekable(): bool
+    {
+        return (bool)$this->getMetadata('seekable');
+    }
+
+    public function getMetadata(?string $key = null)
+    {
+        $metadata = stream_get_meta_data($this->stream);
+        return $key !== null ? @$metadata[$key] : $metadata;
+    }
+
+    public function getSize(): ?int
+    {
+        $stats = fstat($this->stream);
+        return $stats['size'];
+    }
+
+    public function tell(): int
+    {
+        $position = ftell($this->stream);
+        if ($position === false) {
+            // @codeCoverageIgnoreStart
+            throw new RuntimeException();
+            // @codeCoverageIgnoreEnd
+        }
+        return $position;
+    }
+
+    public function eof(): bool
+    {
+        return feof($this->stream);
+    }
+
+    public function rewind(): void
+    {
+        $this->seek(0);
+    }
+
+    public function write(string $string): int
+    {
+        if (!$this->isWritable()) {
+            throw new RuntimeException();
+        }
+        if (fwrite($this->stream, $string) === false) {
+            // @codeCoverageIgnoreStart
+            throw new RuntimeException();
+            // @codeCoverageIgnoreEnd
+        }
+        return strlen($string);
+    }
+
+    public function isWritable(): bool
+    {
+        $mode = $this->getMetadata('mode');
+        if (!is_string($mode)) {
+            // @codeCoverageIgnoreStart
+            throw new RuntimeException('Could not get stream mode from metadata!');
+            // @codeCoverageIgnoreEnd
+        }
+        return preg_match('/[waxc+]/', $mode) === 1;
+    }
+
+    public function read(int $length): string
+    {
+        if (!$this->isReadable()) {
+            throw new RuntimeException();
+        }
+        $result = fread($this->stream, $length);
+        if ($result === false) {
+            // @codeCoverageIgnoreStart
+            throw new RuntimeException();
+            // @codeCoverageIgnoreEnd
+        }
+        return $result;
+    }
+
+    public function isReadable(): bool
+    {
+        $mode = $this->getMetadata('mode');
+        if (!is_string($mode)) {
+            // @codeCoverageIgnoreStart
+            throw new RuntimeException('Could not get stream mode from metadata!');
+            // @codeCoverageIgnoreEnd
+        }
+        return preg_match('/[r+]/', $mode) === 1;
+    }
+
+    public function getContents(): string
+    {
+        if (!$this->isReadable()) {
+            throw new RuntimeException();
+        }
+        $result = stream_get_contents($this->stream);
+        if ($result === false) {
+            // @codeCoverageIgnoreStart
+            throw new RuntimeException();
+            // @codeCoverageIgnoreEnd
+        }
+        return $result;
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/TimeTest.php b/vendor/maennchen/zipstream-php/test/TimeTest.php
new file mode 100644
index 0000000..25aed27
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/TimeTest.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use DateTimeImmutable;
+use PHPUnit\Framework\TestCase;
+use ZipStream\Exception\DosTimeOverflowException;
+use ZipStream\Time;
+
+class TimeTest extends TestCase
+{
+    public function testNormalDateToDosTime(): void
+    {
+        $this->assertSame(
+            Time::dateTimeToDosTime(new DateTimeImmutable('2014-11-17T17:46:08Z')),
+            1165069764
+        );
+
+        // January 1 1980 - DOS Epoch.
+        $this->assertSame(
+            Time::dateTimeToDosTime(new DateTimeImmutable('1980-01-01T00:00:00+00:00')),
+            2162688
+        );
+    }
+
+    public function testTooEarlyDateToDosTime(): void
+    {
+        $this->expectException(DosTimeOverflowException::class);
+
+        // January 1 1980 is the minimum DOS Epoch.
+        Time::dateTimeToDosTime(new DateTimeImmutable('1970-01-01T00:00:00+00:00'));
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Util.php b/vendor/maennchen/zipstream-php/test/Util.php
new file mode 100644
index 0000000..4ec743e
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Util.php
@@ -0,0 +1,135 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test;
+
+use function fgets;
+use function pclose;
+use function popen;
+use function preg_match;
+
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
+
+use function strtolower;
+
+use ZipArchive;
+
+trait Util
+{
+    protected function getTmpFileStream(): array
+    {
+        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+        $stream = fopen($tmp, 'wb+');
+
+        return [$tmp, $stream];
+    }
+
+    protected function cmdExists(string $command): bool
+    {
+        if (strtolower(\substr(PHP_OS, 0, 3)) === 'win') {
+            $fp = popen("where $command", 'r');
+            $result = fgets($fp, 255);
+            $exists = !preg_match('#Could not find files#', $result);
+            pclose($fp);
+        } else { // non-Windows
+            $fp = popen("which $command", 'r');
+            $result = fgets($fp, 255);
+            $exists = !empty($result);
+            pclose($fp);
+        }
+
+        return $exists;
+    }
+
+    protected function dumpZipContents(string $path): string
+    {
+        if (!$this->cmdExists('hexdump')) {
+            return '';
+        }
+
+        $output = [];
+
+        if (!exec("hexdump -C \"$path\" | head -n 50", $output)) {
+            return '';
+        }
+
+        return "\nHexdump:\n" . implode("\n", $output);
+    }
+
+    protected function validateAndExtractZip(string $zipPath): string
+    {
+        $tmpDir = $this->getTmpDir();
+
+        $zipArchive = new ZipArchive();
+        $result = $zipArchive->open($zipPath);
+
+        if ($result !== true) {
+            $codeName = $this->zipArchiveOpenErrorCodeName($result);
+            $debugInformation = $this->dumpZipContents($zipPath);
+
+            $this->fail("Failed to open {$zipPath}. Code: $result ($codeName)$debugInformation");
+
+            return $tmpDir;
+        }
+
+        $this->assertSame(0, $zipArchive->status);
+        $this->assertSame(0, $zipArchive->statusSys);
+
+        $zipArchive->extractTo($tmpDir);
+        $zipArchive->close();
+
+        return $tmpDir;
+    }
+
+    protected function zipArchiveOpenErrorCodeName(int $code): string
+    {
+        switch($code) {
+            case ZipArchive::ER_EXISTS: return 'ER_EXISTS';
+            case ZipArchive::ER_INCONS: return 'ER_INCONS';
+            case ZipArchive::ER_INVAL: return 'ER_INVAL';
+            case ZipArchive::ER_MEMORY: return 'ER_MEMORY';
+            case ZipArchive::ER_NOENT: return 'ER_NOENT';
+            case ZipArchive::ER_NOZIP: return 'ER_NOZIP';
+            case ZipArchive::ER_OPEN: return 'ER_OPEN';
+            case ZipArchive::ER_READ: return 'ER_READ';
+            case ZipArchive::ER_SEEK: return 'ER_SEEK';
+            default: return 'unknown';
+        }
+    }
+
+    protected function getTmpDir(): string
+    {
+        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
+        unlink($tmp);
+        mkdir($tmp) or $this->fail('Failed to make directory');
+
+        return $tmp;
+    }
+
+    /**
+     * @return string[]
+     */
+    protected function getRecursiveFileList(string $path, bool $includeDirectories = false): array
+    {
+        $data = [];
+        $path = (string)realpath($path);
+        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
+
+        $pathLen = strlen($path);
+        foreach ($files as $file) {
+            $filePath = $file->getRealPath();
+
+            if (is_dir($filePath) && !$includeDirectories) {
+                continue;
+            }
+
+            $data[] = substr($filePath, $pathLen + 1);
+        }
+
+        sort($data);
+
+        return $data;
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/DataDescriptorTest.php b/vendor/maennchen/zipstream-php/test/Zip64/DataDescriptorTest.php
new file mode 100644
index 0000000..49fb2cc
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/DataDescriptorTest.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\DataDescriptor;
+
+class DataDescriptorTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $descriptor = DataDescriptor::generate(
+            crc32UncompressedData: 0x11111111,
+            compressedSize: (0x77777777 << 32) +  0x66666666,
+            uncompressedSize: (0x99999999 << 32) + 0x88888888,
+        );
+
+        $this->assertSame(
+            bin2hex($descriptor),
+            '504b0708' . // 4 bytes; Optional data descriptor signature = 0x08074b50
+            '11111111' . // 4 bytes; CRC-32 of uncompressed data
+            '6666666677777777' . // 8 bytes; Compressed size
+            '8888888899999999' // 8 bytes; Uncompressed size
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.php b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.php
new file mode 100644
index 0000000..271a298
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryLocatorTest.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\EndOfCentralDirectoryLocator;
+
+class EndOfCentralDirectoryLocatorTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $descriptor = EndOfCentralDirectoryLocator::generate(
+            numberOfTheDiskWithZip64CentralDirectoryStart: 0x11111111,
+            zip64centralDirectoryStartOffsetOnDisk: (0x22222222 << 32) + 0x33333333,
+            totalNumberOfDisks: 0x44444444,
+        );
+
+        $this->assertSame(
+            bin2hex($descriptor),
+            '504b0607' . // 4 bytes; zip64 end of central dir locator signature - 0x07064b50
+            '11111111' . // 4 bytes; number of the disk with the start of the zip64 end of central directory
+            '3333333322222222' . // 28 bytes; relative offset of the zip64 end of central directory record
+            '44444444' // 4 bytes;total number of disks
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryTest.php b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryTest.php
new file mode 100644
index 0000000..b86fb17
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/EndOfCentralDirectoryTest.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\EndOfCentralDirectory;
+
+class EndOfCentralDirectoryTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $descriptor = EndOfCentralDirectory::generate(
+            versionMadeBy: 0x3333,
+            versionNeededToExtract: 0x4444,
+            numberOfThisDisk: 0x55555555,
+            numberOfTheDiskWithCentralDirectoryStart: 0x66666666,
+            numberOfCentralDirectoryEntriesOnThisDisk: (0x77777777 << 32) + 0x88888888,
+            numberOfCentralDirectoryEntries: (0x99999999 << 32) + 0xAAAAAAAA,
+            sizeOfCentralDirectory: (0xBBBBBBBB << 32) + 0xCCCCCCCC,
+            centralDirectoryStartOffsetOnDisk: (0xDDDDDDDD << 32) + 0xEEEEEEEE,
+            extensibleDataSector: 'foo',
+        );
+
+        $this->assertSame(
+            bin2hex($descriptor),
+            '504b0606' . // 4 bytes;zip64 end of central dir signature - 0x06064b50
+            '2f00000000000000' . // 8 bytes; size of zip64 end of central directory record
+            '3333' . // 2 bytes; version made by
+            '4444' . // 2 bytes; version needed to extract
+            '55555555' . // 4 bytes; number of this disk
+            '66666666' . // 4 bytes; number of the disk with the start of the central directory
+            '8888888877777777' . // 8 bytes; total number of entries in the central directory on this disk
+            'aaaaaaaa99999999' . // 8 bytes; total number of entries in the central directory
+            'ccccccccbbbbbbbb' . // 8 bytes; size of the central directory
+            'eeeeeeeedddddddd' . // 8 bytes; offset of start of central directory with respect to the starting disk number
+            bin2hex('foo')
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.php b/vendor/maennchen/zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.php
new file mode 100644
index 0000000..904783d
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zip64/ExtendedInformationExtraFieldTest.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zip64;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zip64\ExtendedInformationExtraField;
+
+class ExtendedInformationExtraFieldTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $extraField = ExtendedInformationExtraField::generate(
+            originalSize: (0x77777777 << 32) + 0x66666666,
+            compressedSize: (0x99999999 << 32) + 0x88888888,
+            relativeHeaderOffset: (0x22222222 << 32) + 0x11111111,
+            diskStartNumber: 0x33333333,
+        );
+
+        $this->assertSame(
+            bin2hex($extraField),
+            '0100' . // 2 bytes; Tag for this "extra" block type
+            '1c00' . // 2 bytes; Size of this "extra" block
+            '6666666677777777' . // 8 bytes; Original uncompressed file size
+            '8888888899999999' . // 8 bytes; Size of compressed data
+            '1111111122222222' . // 8 bytes; Offset of local header record
+            '33333333' // 4 bytes; Number of the disk on which this file starts
+        );
+    }
+
+    public function testSerializesEmptyCorrectly(): void
+    {
+        $extraField = ExtendedInformationExtraField::generate();
+
+        $this->assertSame(
+            bin2hex($extraField),
+            '0100' . // 2 bytes; Tag for this "extra" block type
+            '0000' // 2 bytes; Size of this "extra" block
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/ZipStreamTest.php b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
index 945ccea..6e69020 100644
--- a/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
+++ b/vendor/maennchen/zipstream-php/test/ZipStreamTest.php
@@ -2,75 +2,42 @@
 
 declare(strict_types=1);
 
-namespace ZipStreamTest;
+namespace ZipStream\Test;
 
+use DateTimeImmutable;
 use GuzzleHttp\Psr7\Response;
+use GuzzleHttp\Psr7\StreamWrapper;
 use org\bovigo\vfs\vfsStream;
 use PHPUnit\Framework\TestCase;
-use RecursiveDirectoryIterator;
-use RecursiveIteratorIterator;
-use ReflectionClass;
+use Psr\Http\Message\StreamInterface;
+use RuntimeException;
 use ZipArchive;
-use ZipStream\File;
-use ZipStream\Option\Archive as ArchiveOptions;
-use ZipStream\Option\File as FileOptions;
-use ZipStream\Option\Method;
-use ZipStream\Stream;
+use ZipStream\CompressionMethod;
+use ZipStream\Exception\FileNotFoundException;
+use ZipStream\Exception\FileNotReadableException;
+use ZipStream\Exception\FileSizeIncorrectException;
+use ZipStream\Exception\OverflowException;
+use ZipStream\Exception\ResourceActionException;
+use ZipStream\Exception\SimulationFileUnknownException;
+use ZipStream\Exception\StreamNotReadableException;
+use ZipStream\Exception\StreamNotSeekableException;
+use ZipStream\OperationMode;
+use ZipStream\PackField;
 use ZipStream\ZipStream;
 
-/**
- * Test Class for the Main ZipStream CLass
- */
 class ZipStreamTest extends TestCase
 {
-    public const OSX_ARCHIVE_UTILITY =
-        '/System/Library/CoreServices/Applications/Archive Utility.app/Contents/MacOS/Archive Utility';
-
-    public function testFileNotFoundException(): void
-    {
-        $this->expectException(\ZipStream\Exception\FileNotFoundException::class);
-        // Get ZipStream Object
-        $zip = new ZipStream();
-
-        // Trigger error by adding a file which doesn't exist
-        $zip->addFileFromPath('foobar.php', '/foo/bar/foobar.php');
-    }
-
-    public function testFileNotReadableException(): void
-    {
-        // create new virtual filesystem
-        $root = vfsStream::setup('vfs');
-        // create a virtual file with no permissions
-        $file = vfsStream::newFile('foo.txt', 0)->at($root)->setContent('bar');
-        $zip = new ZipStream();
-        $this->expectException(\ZipStream\Exception\FileNotReadableException::class);
-        $zip->addFileFromPath('foo.txt', $file->url());
-    }
-
-    public function testDostime(): void
-    {
-        // Allows testing of protected method
-        $class = new ReflectionClass(File::class);
-        $method = $class->getMethod('dostime');
-        $method->setAccessible(true);
-
-        $this->assertSame($method->invoke(null, 1416246368), 1165069764);
-
-        // January 1 1980 - DOS Epoch.
-        $this->assertSame($method->invoke(null, 315532800), 2162688);
-
-        // January 1 1970 -> January 1 1980 due to minimum DOS Epoch.  @todo Throw Exception?
-        $this->assertSame($method->invoke(null, 0), 2162688);
-    }
+    use Util;
+    use Assertions;
 
     public function testAddFile(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         $zip->addFile('sample.txt', 'Sample String Data');
         $zip->addFile('test/sample.txt', 'More Simple Sample Data');
@@ -91,10 +58,10 @@ class ZipStreamTest extends TestCase
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         $name = 'árvíztűrő tükörfúrógép.txt';
         $content = 'Sample String Data';
@@ -103,10 +70,7 @@ class ZipStreamTest extends TestCase
             'from Hungarian language in lowercase. ' .
             'In uppercase: ÁÍŰŐÜÖÚÓÉ';
 
-        $fileOptions = new FileOptions();
-        $fileOptions->setComment($comment);
-
-        $zip->addFile($name, $content, $fileOptions);
+        $zip->addFile(fileName: $name, data: $content, comment: $comment);
         $zip->finish();
         fclose($stream);
 
@@ -116,19 +80,19 @@ class ZipStreamTest extends TestCase
         $this->assertSame([$name], $files);
         $this->assertStringEqualsFile($tmpDir . '/' . $name, $content);
 
-        $zipArch = new ZipArchive();
-        $zipArch->open($tmp);
-        $this->assertSame($comment, $zipArch->getCommentName($name));
+        $zipArchive = new ZipArchive();
+        $zipArchive->open($tmp);
+        $this->assertSame($comment, $zipArchive->getCommentName($name));
     }
 
     public function testAddFileUtf8NameNonUtfComment(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         $name = 'á.txt';
         $content = 'any';
@@ -140,10 +104,8 @@ class ZipStreamTest extends TestCase
         // nearly CP850 (DOS-Latin-1)
         $guessComment = mb_convert_encoding($comment, 'UTF-8', 'CP850');
 
-        $fileOptions = new FileOptions();
-        $fileOptions->setComment($comment);
+        $zip->addFile(fileName: $name, data: $content, comment: $comment);
 
-        $zip->addFile($name, $content, $fileOptions);
         $zip->finish();
         fclose($stream);
 
@@ -153,121 +115,49 @@ class ZipStreamTest extends TestCase
         $this->assertSame($comment, $zipArch->getCommentName($name, ZipArchive::FL_ENC_RAW));
     }
 
-    public function testAddFileNonUtf8NameUtfComment(): void
-    {
-        [$tmp, $stream] = $this->getTmpFileStream();
-
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
-
-        $name = mb_convert_encoding('á.txt', 'ISO-8859-2', 'UTF-8');
-        $content = 'any';
-        $comment = 'á';
-
-        // @see https://libzip.org/documentation/zip_get_name.html
-        //
-        // mb_convert_encoding hasn't CP437.
-        // nearly CP850 (DOS-Latin-1)
-        $guessName = mb_convert_encoding($name, 'UTF-8', 'CP850');
-
-        $fileOptions = new FileOptions();
-        $fileOptions->setComment($comment);
-
-        $zip->addFile($name, $content, $fileOptions);
-        $zip->finish();
-        fclose($stream);
-
-        $tmpDir = $this->validateAndExtractZip($tmp);
-
-        $files = $this->getRecursiveFileList($tmpDir);
-
-        $this->assertNotSame([$name], $files);
-        $this->assertSame([$guessName], $files);
-        $this->assertStringEqualsFile($tmpDir . '/' . $guessName, $content);
-
-        $zipArch = new ZipArchive();
-        $zipArch->open($tmp);
-        $this->assertSame($guessName, $zipArch->getNameIndex(0));
-        $this->assertSame($name, $zipArch->getNameIndex(0, ZipArchive::FL_ENC_RAW));
-        $this->assertSame($comment, $zipArch->getCommentName($guessName));
-    }
-
     public function testAddFileWithStorageMethod(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
-        $zip = new ZipStream(null, $options);
-
-        $fileOptions = new FileOptions();
-        $fileOptions->setMethod(Method::STORE());
-
-        $zip->addFile('sample.txt', 'Sample String Data', $fileOptions);
-        $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+        $zip->addFile(fileName: 'sample.txt', data: 'Sample String Data', compressionMethod: CompressionMethod::STORE);
+        $zip->addFile(fileName: 'test/sample.txt', data: 'More Simple Sample Data');
         $zip->finish();
         fclose($stream);
 
-        $zipArch = new ZipArchive();
-        $zipArch->open($tmp);
+        $zipArchive = new ZipArchive();
+        $zipArchive->open($tmp);
 
-        $sample1 = $zipArch->statName('sample.txt');
-        $sample12 = $zipArch->statName('test/sample.txt');
-        $this->assertSame($sample1['comp_method'], Method::STORE);
-        $this->assertSame($sample12['comp_method'], Method::DEFLATE);
+        $sample1 = $zipArchive->statName('sample.txt');
+        $sample12 = $zipArchive->statName('test/sample.txt');
+        $this->assertSame($sample1['comp_method'], CompressionMethod::STORE->value);
+        $this->assertSame($sample12['comp_method'], CompressionMethod::DEFLATE->value);
 
-        $zipArch->close();
-    }
-
-    public function testDecompressFileWithMacUnarchiver(): void
-    {
-        if (!file_exists(self::OSX_ARCHIVE_UTILITY)) {
-            $this->markTestSkipped('The Mac OSX Archive Utility is not available.');
-        }
-
-        [$tmp, $stream] = $this->getTmpFileStream();
-
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
-
-        $folder = uniqid('', true);
-
-        $zip->addFile($folder . '/sample.txt', 'Sample Data');
-        $zip->finish();
-        fclose($stream);
-
-        exec(escapeshellarg(self::OSX_ARCHIVE_UTILITY) . ' ' . escapeshellarg($tmp), $output, $returnStatus);
-
-        $this->assertSame(0, $returnStatus);
-        $this->assertCount(0, $output);
-
-        $this->assertFileExists(dirname($tmp) . '/' . $folder . '/sample.txt');
-        $this->assertStringEqualsFile(dirname($tmp) . '/' . $folder . '/sample.txt', 'Sample Data');
+        $zipArchive->close();
     }
 
     public function testAddFileFromPath(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         [$tmpExample, $streamExample] = $this->getTmpFileStream();
         fwrite($streamExample, 'Sample String Data');
         fclose($streamExample);
-        $zip->addFileFromPath('sample.txt', $tmpExample);
+        $zip->addFileFromPath(fileName: 'sample.txt', path: $tmpExample);
 
         [$tmpExample, $streamExample] = $this->getTmpFileStream();
         fwrite($streamExample, 'More Simple Sample Data');
         fclose($streamExample);
-        $zip->addFileFromPath('test/sample.txt', $tmpExample);
+        $zip->addFileFromPath(fileName: 'test/sample.txt', path: $tmpExample);
 
         $zip->finish();
         fclose($stream);
@@ -281,22 +171,57 @@ class ZipStreamTest extends TestCase
         $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
     }
 
+    public function testAddFileFromPathFileNotFoundException(): void
+    {
+        $this->expectException(FileNotFoundException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+
+        // Get ZipStream Object
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
+
+        // Trigger error by adding a file which doesn't exist
+        $zip->addFileFromPath(fileName: 'foobar.php', path: '/foo/bar/foobar.php');
+    }
+
+    public function testAddFileFromPathFileNotReadableException(): void
+    {
+        $this->expectException(FileNotReadableException::class);
+
+
+        [, $stream] = $this->getTmpFileStream();
+
+
+        // create new virtual filesystem
+        $root = vfsStream::setup('vfs');
+        // create a virtual file with no permissions
+        $file = vfsStream::newFile('foo.txt', 0)->at($root)->setContent('bar');
+
+        // Get ZipStream Object
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
+
+        $zip->addFileFromPath('foo.txt', $file->url());
+    }
+
     public function testAddFileFromPathWithStorageMethod(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
-
-        $fileOptions = new FileOptions();
-        $fileOptions->setMethod(Method::STORE());
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         [$tmpExample, $streamExample] = $this->getTmpFileStream();
         fwrite($streamExample, 'Sample String Data');
         fclose($streamExample);
-        $zip->addFileFromPath('sample.txt', $tmpExample, $fileOptions);
+        $zip->addFileFromPath(fileName: 'sample.txt', path: $tmpExample, compressionMethod: CompressionMethod::STORE);
 
         [$tmpExample, $streamExample] = $this->getTmpFileStream();
         fwrite($streamExample, 'More Simple Sample Data');
@@ -306,29 +231,31 @@ class ZipStreamTest extends TestCase
         $zip->finish();
         fclose($stream);
 
-        $zipArch = new ZipArchive();
-        $zipArch->open($tmp);
+        $zipArchive = new ZipArchive();
+        $zipArchive->open($tmp);
 
-        $sample1 = $zipArch->statName('sample.txt');
-        $this->assertSame(Method::STORE, $sample1['comp_method']);
+        $sample1 = $zipArchive->statName('sample.txt');
+        $this->assertSame(CompressionMethod::STORE->value, $sample1['comp_method']);
 
-        $sample2 = $zipArch->statName('test/sample.txt');
-        $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
+        $sample2 = $zipArchive->statName('test/sample.txt');
+        $this->assertSame(CompressionMethod::DEFLATE->value, $sample2['comp_method']);
 
-        $zipArch->close();
+        $zipArchive->close();
     }
 
     public function testAddLargeFileFromPath(): void
     {
-        $methods = [Method::DEFLATE(), Method::STORE()];
-        $falseTrue = [false, true];
-        foreach ($methods as $method) {
-            foreach ($falseTrue as $zeroHeader) {
-                foreach ($falseTrue as $zip64) {
-                    if ($zeroHeader && $method->equals(Method::DEFLATE())) {
+        foreach ([CompressionMethod::DEFLATE, CompressionMethod::STORE] as $compressionMethod) {
+            foreach ([false, true] as $zeroHeader) {
+                foreach ([false, true] as $zip64) {
+                    if ($zeroHeader && $compressionMethod === CompressionMethod::DEFLATE) {
                         continue;
                     }
-                    $this->addLargeFileFileFromPath($method, $zeroHeader, $zip64);
+                    $this->addLargeFileFileFromPath(
+                        compressionMethod: $compressionMethod,
+                        zeroHeader: $zeroHeader,
+                        zip64: $zip64
+                    );
                 }
             }
         }
@@ -338,26 +265,23 @@ class ZipStreamTest extends TestCase
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         // In this test we can't use temporary stream to feed data
         // because zlib.deflate filter gives empty string before PHP 7
         // it works fine with file stream
         $streamExample = fopen(__FILE__, 'rb');
         $zip->addFileFromStream('sample.txt', $streamExample);
-//        fclose($streamExample);
-
-        $fileOptions = new FileOptions();
-        $fileOptions->setMethod(Method::STORE());
+        fclose($streamExample);
 
         $streamExample2 = fopen('php://temp', 'wb+');
         fwrite($streamExample2, 'More Simple Sample Data');
         rewind($streamExample2); // move the pointer back to the beginning of file.
-        $zip->addFileFromStream('test/sample.txt', $streamExample2, $fileOptions);
-//        fclose($streamExample2);
+        $zip->addFileFromStream('test/sample.txt', $streamExample2); //, $fileOptions);
+        fclose($streamExample2);
 
         $zip->finish();
         fclose($stream);
@@ -371,61 +295,162 @@ class ZipStreamTest extends TestCase
         $this->assertStringEqualsFile($tmpDir . '/test/sample.txt', 'More Simple Sample Data');
     }
 
-    public function testAddFileFromStreamWithStorageMethod(): void
+    public function testAddFileFromStreamUnreadableInput(): void
+    {
+        $this->expectException(StreamNotReadableException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+        [$tmpInput] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
+
+        $streamUnreadable = fopen($tmpInput, 'w');
+
+        $zip->addFileFromStream('sample.json', $streamUnreadable);
+    }
+
+    public function testAddFileFromStreamBrokenOutputWrite(): void
+    {
+        $this->expectException(ResourceActionException::class);
+
+        $outputStream = FaultInjectionResource::getResource(['stream_write']);
+
+        $zip = new ZipStream(
+            outputStream: $outputStream,
+            sendHttpHeaders: false,
+        );
+
+        $zip->addFile('sample.txt', 'foobar');
+    }
+
+    public function testAddFileFromStreamBrokenInputRewind(): void
+    {
+        $this->expectException(ResourceActionException::class);
+
+        [,$stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            defaultEnableZeroHeader: false,
+        );
+
+        $fileStream = FaultInjectionResource::getResource(['stream_seek']);
+
+        $zip->addFileFromStream('sample.txt', $fileStream, maxSize: 0);
+    }
+
+    public function testAddFileFromStreamUnseekableInputWithoutZeroHeader(): void
+    {
+        $this->expectException(StreamNotSeekableException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            defaultEnableZeroHeader: false,
+        );
+
+        if (file_exists('/dev/null')) {
+            $streamUnseekable = fopen('/dev/null', 'w+');
+        } elseif (file_exists('NUL')) {
+            $streamUnseekable = fopen('NUL', 'w+');
+        } else {
+            $this->markTestSkipped('Needs file /dev/null');
+        }
+
+        $zip->addFileFromStream('sample.txt', $streamUnseekable, maxSize: 2);
+    }
+
+    public function testAddFileFromStreamUnseekableInputWithZeroHeader(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            defaultEnableZeroHeader: true,
+            defaultCompressionMethod: CompressionMethod::STORE,
+        );
 
-        $zip = new ZipStream(null, $options);
+        $streamUnseekable = StreamWrapper::getResource(new class ('test') extends EndlessCycleStream {
+            public function isSeekable(): bool
+            {
+                return false;
+            }
 
-        $fileOptions = new FileOptions();
-        $fileOptions->setMethod(Method::STORE());
+            public function seek(int $offset, int $whence = SEEK_SET): void
+            {
+                throw new RuntimeException('Not seekable');
+            }
+        });
 
-        $streamExample = fopen('php://temp', 'wb+');
-        fwrite($streamExample, 'Sample String Data');
-        rewind($streamExample); // move the pointer back to the beginning of file.
-        $zip->addFileFromStream('sample.txt', $streamExample, $fileOptions);
-//        fclose($streamExample);
-
-        $streamExample2 = fopen('php://temp', 'bw+');
-        fwrite($streamExample2, 'More Simple Sample Data');
-        rewind($streamExample2); // move the pointer back to the beginning of file.
-        $zip->addFileFromStream('test/sample.txt', $streamExample2);
-//        fclose($streamExample2);
+        $zip->addFileFromStream('sample.txt', $streamUnseekable, maxSize: 7);
 
         $zip->finish();
         fclose($stream);
 
-        $zipArch = new ZipArchive();
-        $zipArch->open($tmp);
+        $tmpDir = $this->validateAndExtractZip($tmp);
 
-        $sample1 = $zipArch->statName('sample.txt');
-        $this->assertSame(Method::STORE, $sample1['comp_method']);
+        $files = $this->getRecursiveFileList($tmpDir);
+        $this->assertSame(['sample.txt'], $files);
 
-        $sample2 = $zipArch->statName('test/sample.txt');
-        $this->assertSame(Method::DEFLATE, $sample2['comp_method']);
+        $this->assertSame(filesize($tmpDir . '/sample.txt'), 7);
+    }
 
-        $zipArch->close();
+    public function testAddFileFromStreamWithStorageMethod(): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
+
+        $streamExample = fopen('php://temp', 'wb+');
+        fwrite($streamExample, 'Sample String Data');
+        rewind($streamExample); // move the pointer back to the beginning of file.
+        $zip->addFileFromStream('sample.txt', $streamExample, compressionMethod: CompressionMethod::STORE);
+        fclose($streamExample);
+
+        $streamExample2 = fopen('php://temp', 'bw+');
+        fwrite($streamExample2, 'More Simple Sample Data');
+        rewind($streamExample2); // move the pointer back to the beginning of file.
+        $zip->addFileFromStream('test/sample.txt', $streamExample2, compressionMethod: CompressionMethod::DEFLATE);
+        fclose($streamExample2);
+
+        $zip->finish();
+        fclose($stream);
+
+        $zipArchive = new ZipArchive();
+        $zipArchive->open($tmp);
+
+        $sample1 = $zipArchive->statName('sample.txt');
+        $this->assertSame(CompressionMethod::STORE->value, $sample1['comp_method']);
+
+        $sample2 = $zipArchive->statName('test/sample.txt');
+        $this->assertSame(CompressionMethod::DEFLATE->value, $sample2['comp_method']);
+
+        $zipArchive->close();
     }
 
     public function testAddFileFromPsr7Stream(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         $body = 'Sample String Data';
         $response = new Response(200, [], $body);
 
-        $fileOptions = new FileOptions();
-        $fileOptions->setMethod(Method::STORE());
-
-        $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+        $zip->addFileFromPsr7Stream('sample.json', $response->getBody());
         $zip->finish();
         fclose($stream);
 
@@ -436,23 +461,295 @@ class ZipStreamTest extends TestCase
         $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
     }
 
+    /**
+     * @group slow
+     */
+    public function testAddLargeFileFromPsr7Stream(): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: true,
+        );
+
+        $zip->addFileFromPsr7Stream(
+            fileName: 'sample.json',
+            stream: new EndlessCycleStream('0'),
+            maxSize: 0x100000000,
+            compressionMethod: CompressionMethod::STORE,
+            lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+        );
+        $zip->finish();
+        fclose($stream);
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+
+        $files = $this->getRecursiveFileList($tmpDir);
+        $this->assertSame(['sample.json'], $files);
+        $this->assertFileIsReadable($tmpDir . '/sample.json');
+        $this->assertStringStartsWith('000000', file_get_contents(filename: $tmpDir . '/sample.json', length: 20));
+    }
+
+    public function testContinueFinishedZip(): void
+    {
+        $this->expectException(RuntimeException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
+        $zip->finish();
+
+        $zip->addFile('sample.txt', '1234');
+    }
+
+    /**
+     * @group slow
+     */
+    public function testManyFilesWithoutZip64(): void
+    {
+        $this->expectException(OverflowException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: false,
+        );
+
+        for ($i = 0; $i <= 0xFFFF; $i++) {
+            $zip->addFile('sample' . $i, '');
+        }
+
+        $zip->finish();
+    }
+
+    /**
+     * @group slow
+     */
+    public function testManyFilesWithZip64(): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: true,
+        );
+
+        for ($i = 0; $i <= 0xFFFF; $i++) {
+            $zip->addFile('sample' . $i, '');
+        }
+
+        $zip->finish();
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+
+        $files = $this->getRecursiveFileList($tmpDir);
+
+        $this->assertSame(count($files), 0x10000);
+    }
+
+    /**
+     * @group slow
+     */
+    public function testLongZipWithout64(): void
+    {
+        $this->expectException(OverflowException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: false,
+            defaultCompressionMethod: CompressionMethod::STORE,
+        );
+
+        for ($i = 0; $i < 4; $i++) {
+            $zip->addFileFromPsr7Stream(
+                fileName: 'sample' . $i,
+                stream: new EndlessCycleStream('0'),
+                maxSize: 0xFFFFFFFF,
+                compressionMethod: CompressionMethod::STORE,
+                lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+            );
+        }
+    }
+
+    /**
+     * @group slow
+     */
+    public function testLongZipWith64(): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: true,
+            defaultCompressionMethod: CompressionMethod::STORE,
+        );
+
+        for ($i = 0; $i < 4; $i++) {
+            $zip->addFileFromPsr7Stream(
+                fileName: 'sample' . $i,
+                stream: new EndlessCycleStream('0'),
+                maxSize: 0x5FFFFFFF,
+                compressionMethod: CompressionMethod::STORE,
+                lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+            );
+        }
+
+        $zip->finish();
+        fclose($stream);
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+
+        $files = $this->getRecursiveFileList($tmpDir);
+        $this->assertSame(['sample0', 'sample1', 'sample2', 'sample3'], $files);
+    }
+
+    /**
+     * @group slow
+     */
+    public function testAddLargeFileWithoutZip64WithZeroHeader(): void
+    {
+        $this->expectException(OverflowException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: false,
+            defaultEnableZeroHeader: true,
+        );
+
+        $zip->addFileFromPsr7Stream(
+            fileName: 'sample.json',
+            stream: new EndlessCycleStream('0'),
+            maxSize: 0x100000000,
+            compressionMethod: CompressionMethod::STORE,
+            lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+        );
+    }
+
+    /**
+     * @group slow
+     */
+    public function testAddsZip64HeaderWhenNeeded(): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: true,
+            defaultEnableZeroHeader: false,
+        );
+
+        $zip->addFileFromPsr7Stream(
+            fileName: 'sample.json',
+            stream: new EndlessCycleStream('0'),
+            maxSize: 0x100000000,
+            compressionMethod: CompressionMethod::STORE,
+            lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+        );
+
+        $zip->finish();
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+        $files = $this->getRecursiveFileList($tmpDir);
+
+        $this->assertSame(['sample.json'], $files);
+        $this->assertFileContains($tmp, PackField::pack(
+            new PackField(format: 'V', value: 0x06064b50)
+        ));
+    }
+
+    /**
+     * @group slow
+     */
+    public function testDoesNotAddZip64HeaderWhenNotNeeded(): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: true,
+            defaultEnableZeroHeader: false,
+        );
+
+        $zip->addFileFromPsr7Stream(
+            fileName: 'sample.json',
+            stream: new EndlessCycleStream('0'),
+            maxSize: 0x10,
+            compressionMethod: CompressionMethod::STORE,
+            lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+        );
+
+        $zip->finish();
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+        $files = $this->getRecursiveFileList($tmpDir);
+
+        $this->assertSame(['sample.json'], $files);
+        $this->assertFileDoesNotContain($tmp, PackField::pack(
+            new PackField(format: 'V', value: 0x06064b50)
+        ));
+    }
+
+    /**
+     * @group slow
+     */
+    public function testAddLargeFileWithoutZip64WithoutZeroHeader(): void
+    {
+        $this->expectException(OverflowException::class);
+
+        [, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            enableZip64: false,
+            defaultEnableZeroHeader: false,
+        );
+
+        $zip->addFileFromPsr7Stream(
+            fileName: 'sample.json',
+            stream: new EndlessCycleStream('0'),
+            maxSize: 0x100000000,
+            compressionMethod: CompressionMethod::STORE,
+            lastModificationDateTime: new DateTimeImmutable('2022-01-01 01:01:01Z'),
+        );
+    }
+
     public function testAddFileFromPsr7StreamWithOutputToPsr7Stream(): void
     {
         [$tmp, $resource] = $this->getTmpFileStream();
-        $psr7OutputStream = new Stream($resource);
+        $psr7OutputStream = new ResourceStream($resource);
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($psr7OutputStream);
 
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $psr7OutputStream,
+            sendHttpHeaders: false,
+        );
 
         $body = 'Sample String Data';
         $response = new Response(200, [], $body);
 
-        $fileOptions = new FileOptions();
-        $fileOptions->setMethod(Method::STORE());
-
-        $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+        $zip->addFileFromPsr7Stream(
+            fileName: 'sample.json',
+            stream: $response->getBody(),
+            compressionMethod: CompressionMethod::STORE,
+        );
         $zip->finish();
         $psr7OutputStream->close();
 
@@ -467,10 +764,10 @@ class ZipStreamTest extends TestCase
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
         $body = 'Sample String Data';
         $fileSize = strlen($body);
@@ -478,10 +775,12 @@ class ZipStreamTest extends TestCase
         $fakePadding = "\0\0\0\0\0\0";
         $response = new Response(200, [], $body . $fakePadding);
 
-        $fileOptions = new FileOptions();
-        $fileOptions->setMethod(Method::STORE());
-        $fileOptions->setSize($fileSize);
-        $zip->addFileFromPsr7Stream('sample.json', $response->getBody(), $fileOptions);
+        $zip->addFileFromPsr7Stream(
+            fileName: 'sample.json',
+            stream: $response->getBody(),
+            compressionMethod: CompressionMethod::STORE,
+            maxSize: $fileSize
+        );
         $zip->finish();
         fclose($stream);
 
@@ -492,15 +791,46 @@ class ZipStreamTest extends TestCase
         $this->assertStringEqualsFile($tmpDir . '/sample.json', $body);
     }
 
+    public function testCreateArchiveHeaders(): void
+    {
+        [, $stream] = $this->getTmpFileStream();
+
+        $headers = [];
+
+        $httpHeaderCallback = function (string $header) use (&$headers) {
+            $headers[] = $header;
+        };
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: true,
+            outputName: 'example.zip',
+            httpHeaderCallback: $httpHeaderCallback,
+        );
+
+        $zip->addFile(
+            fileName: 'sample.json',
+            data: 'foo',
+        );
+        $zip->finish();
+        fclose($stream);
+
+        $this->assertContains('Content-Type: application/x-zip', $headers);
+        $this->assertContains("Content-Disposition: attachment; filename*=UTF-8''example.zip", $headers);
+        $this->assertContains('Pragma: public', $headers);
+        $this->assertContains('Cache-Control: public, must-revalidate', $headers);
+        $this->assertContains('Content-Transfer-Encoding: binary', $headers);
+    }
+
     public function testCreateArchiveWithFlushOptionSet(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-        $options->setFlushOutput(true);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            flushOutput: true,
+            sendHttpHeaders: false,
+        );
 
         $zip->addFile('sample.txt', 'Sample String Data');
         $zip->addFile('test/sample.txt', 'More Simple Sample Data');
@@ -525,11 +855,11 @@ class ZipStreamTest extends TestCase
 
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-        $options->setFlushOutput(true);
-
-        $zip = new ZipStream(null, $options);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            flushOutput: true,
+            sendHttpHeaders: false,
+        );
 
         $zip->addFile('sample.txt', 'Sample String Data');
 
@@ -543,87 +873,391 @@ class ZipStreamTest extends TestCase
         ob_start();
     }
 
-    /**
-     * @return array
-     */
-    protected function getTmpFileStream(): array
-    {
-        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
-        $stream = fopen($tmp, 'wb+');
-
-        return [$tmp, $stream];
-    }
-
-    /**
-     * @param string $tmp
-     * @return string
-     */
-    protected function validateAndExtractZip($tmp): string
-    {
-        $tmpDir = $this->getTmpDir();
-
-        $zipArch = new ZipArchive();
-        $res = $zipArch->open($tmp);
-
-        if ($res !== true) {
-            $this->fail("Failed to open {$tmp}. Code: $res");
-
-            return $tmpDir;
-        }
-
-        $this->assertSame(0, $zipArch->status);
-        $this->assertSame(0, $zipArch->statusSys);
-
-        $zipArch->extractTo($tmpDir);
-        $zipArch->close();
-
-        return $tmpDir;
-    }
-
-    protected function getTmpDir(): string
-    {
-        $tmp = tempnam(sys_get_temp_dir(), 'zipstreamtest');
-        unlink($tmp);
-        mkdir($tmp) or $this->fail('Failed to make directory');
-
-        return $tmp;
-    }
-
-    /**
-     * @param string $path
-     * @return string[]
-     */
-    protected function getRecursiveFileList(string $path): array
-    {
-        $data = [];
-        $path = (string)realpath($path);
-        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
-
-        $pathLen = strlen($path);
-        foreach ($files as $file) {
-            $filePath = $file->getRealPath();
-            if (!is_dir($filePath)) {
-                $data[] = substr($filePath, $pathLen + 1);
-            }
-        }
-
-        sort($data);
-
-        return $data;
-    }
-
-    protected function addLargeFileFileFromPath($method, $zeroHeader, $zip64): void
+    public function testAddEmptyDirectory(): void
     {
         [$tmp, $stream] = $this->getTmpFileStream();
 
-        $options = new ArchiveOptions();
-        $options->setOutputStream($stream);
-        $options->setLargeFileMethod($method);
-        $options->setLargeFileSize(5);
-        $options->setZeroHeader($zeroHeader);
-        $options->setEnableZip64($zip64);
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+        );
 
-        $zip = new ZipStream(null, $options);
+        $zip->addDirectory('foo');
+
+        $zip->finish();
+        fclose($stream);
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+
+        $files = $this->getRecursiveFileList($tmpDir, includeDirectories: true);
+
+        $this->assertContains('foo', $files);
+
+        $this->assertFileExists($tmpDir . DIRECTORY_SEPARATOR . 'foo');
+        $this->assertDirectoryExists($tmpDir . DIRECTORY_SEPARATOR . 'foo');
+    }
+
+    public function testAddFileSimulate(): void
+    {
+        [, $stream] = $this->getTmpFileStream();
+
+        $create = function (OperationMode $operationMode) use ($stream): int {
+            $zip = new ZipStream(
+                sendHttpHeaders: false,
+                operationMode: $operationMode,
+                defaultEnableZeroHeader: true,
+                outputStream: $stream,
+            );
+
+            $zip->addFile('sample.txt', 'Sample String Data');
+            $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+            return $zip->finish();
+        };
+
+
+        $sizeExpected = $create(OperationMode::NORMAL);
+        $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+        $this->assertEquals($sizeExpected, $sizeActual);
+    }
+
+    public function testAddFileSimulateWithMaxSize(): void
+    {
+        [, $stream] = $this->getTmpFileStream();
+
+        $create = function (OperationMode $operationMode) use ($stream): int {
+            $zip = new ZipStream(
+                sendHttpHeaders: false,
+                operationMode: $operationMode,
+                defaultCompressionMethod: CompressionMethod::STORE,
+                defaultEnableZeroHeader: true,
+                outputStream: $stream,
+            );
+
+            $zip->addFile('sample.txt', 'Sample String Data', maxSize: 0);
+
+            return $zip->finish();
+        };
+
+
+        $sizeExpected = $create(OperationMode::NORMAL);
+        $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+        $this->assertEquals($sizeExpected, $sizeActual);
+    }
+
+    public function testAddFileSimulateWithFstat(): void
+    {
+        [, $stream] = $this->getTmpFileStream();
+
+        $create = function (OperationMode $operationMode) use ($stream): int {
+            $zip = new ZipStream(
+                sendHttpHeaders: false,
+                operationMode: $operationMode,
+                defaultCompressionMethod: CompressionMethod::STORE,
+                defaultEnableZeroHeader: true,
+                outputStream: $stream,
+            );
+
+            $zip->addFile('sample.txt', 'Sample String Data');
+            $zip->addFile('test/sample.txt', 'More Simple Sample Data');
+
+            return $zip->finish();
+        };
+
+
+        $sizeExpected = $create(OperationMode::NORMAL);
+        $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+        $this->assertEquals($sizeExpected, $sizeActual);
+    }
+
+    public function testAddFileSimulateWithExactSizeZero(): void
+    {
+        [, $stream] = $this->getTmpFileStream();
+
+        $create = function (OperationMode $operationMode) use ($stream): int {
+            $zip = new ZipStream(
+                sendHttpHeaders: false,
+                operationMode: $operationMode,
+                defaultCompressionMethod: CompressionMethod::STORE,
+                defaultEnableZeroHeader: true,
+                outputStream: $stream,
+            );
+
+            $zip->addFile('sample.txt', 'Sample String Data', exactSize: 18);
+
+            return $zip->finish();
+        };
+
+
+        $sizeExpected = $create(OperationMode::NORMAL);
+        $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+        $this->assertEquals($sizeExpected, $sizeActual);
+    }
+
+    public function testAddFileSimulateWithExactSizeInitial(): void
+    {
+        [, $stream] = $this->getTmpFileStream();
+
+        $create = function (OperationMode $operationMode) use ($stream): int {
+            $zip = new ZipStream(
+                sendHttpHeaders: false,
+                operationMode: $operationMode,
+                defaultCompressionMethod: CompressionMethod::STORE,
+                defaultEnableZeroHeader: false,
+                outputStream: $stream,
+            );
+
+            $zip->addFile('sample.txt', 'Sample String Data', exactSize: 18);
+
+            return $zip->finish();
+        };
+
+        $sizeExpected = $create(OperationMode::NORMAL);
+        $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+        $this->assertEquals($sizeExpected, $sizeActual);
+    }
+
+    public function testAddFileSimulateWithZeroSizeInFstat(): void
+    {
+        [, $stream] = $this->getTmpFileStream();
+
+        $create = function (OperationMode $operationMode) use ($stream): int {
+            $zip = new ZipStream(
+                sendHttpHeaders: false,
+                operationMode: $operationMode,
+                defaultCompressionMethod: CompressionMethod::STORE,
+                defaultEnableZeroHeader: false,
+                outputStream: $stream,
+            );
+
+            $zip->addFileFromPsr7Stream('sample.txt', new class () implements StreamInterface {
+                public $pos = 0;
+
+                public function __toString(): string
+                {
+                    return 'test';
+                }
+
+                public function close(): void
+                {
+                }
+
+                public function detach()
+                {
+                }
+
+                public function getSize(): ?int
+                {
+                    return null;
+                }
+
+                public function tell(): int
+                {
+                    return $this->pos;
+                }
+
+                public function eof(): bool
+                {
+                    return $this->pos >= 4;
+                }
+
+                public function isSeekable(): bool
+                {
+                    return true;
+                }
+
+                public function seek(int $offset, int $whence = SEEK_SET): void
+                {
+                    $this->pos = $offset;
+                }
+
+                public function rewind(): void
+                {
+                    $this->pos = 0;
+                }
+
+                public function isWritable(): bool
+                {
+                    return false;
+                }
+
+                public function write(string $string): int
+                {
+                    return 0;
+                }
+
+                public function isReadable(): bool
+                {
+                    return true;
+                }
+
+                public function read(int $length): string
+                {
+                    $data = substr('test', $this->pos, $length);
+                    $this->pos += strlen($data);
+                    return $data;
+                }
+
+                public function getContents(): string
+                {
+                    return $this->read(4);
+                }
+
+                public function getMetadata(?string $key = null)
+                {
+                    return $key !== null ? null : [];
+                }
+            });
+
+            return $zip->finish();
+        };
+
+        $sizeExpected = $create(OperationMode::NORMAL);
+        $sizeActual = $create(OperationMode::SIMULATE_LAX);
+
+
+        $this->assertEquals($sizeExpected, $sizeActual);
+    }
+
+    public function testAddFileSimulateWithWrongExactSize(): void
+    {
+        $this->expectException(FileSizeIncorrectException::class);
+
+        $zip = new ZipStream(
+            sendHttpHeaders: false,
+            operationMode: OperationMode::SIMULATE_LAX,
+        );
+
+        $zip->addFile('sample.txt', 'Sample String Data', exactSize: 1000);
+    }
+
+    public function testAddFileSimulateStrictZero(): void
+    {
+        $this->expectException(SimulationFileUnknownException::class);
+
+        $zip = new ZipStream(
+            sendHttpHeaders: false,
+            operationMode: OperationMode::SIMULATE_STRICT,
+            defaultEnableZeroHeader: true
+        );
+
+        $zip->addFile('sample.txt', 'Sample String Data');
+    }
+
+    public function testAddFileSimulateStrictInitial(): void
+    {
+        $this->expectException(SimulationFileUnknownException::class);
+
+        $zip = new ZipStream(
+            sendHttpHeaders: false,
+            operationMode: OperationMode::SIMULATE_STRICT,
+            defaultEnableZeroHeader: false
+        );
+
+        $zip->addFile('sample.txt', 'Sample String Data');
+    }
+
+    public function testAddFileCallbackStrict(): void
+    {
+        $this->expectException(SimulationFileUnknownException::class);
+
+        $zip = new ZipStream(
+            sendHttpHeaders: false,
+            operationMode: OperationMode::SIMULATE_STRICT,
+            defaultEnableZeroHeader: false
+        );
+
+        $zip->addFileFromCallback('sample.txt', callback: function () {
+            return '';
+        });
+    }
+
+    public function testAddFileCallbackLax(): void
+    {
+
+        $zip = new ZipStream(
+            operationMode: OperationMode::SIMULATE_LAX,
+            defaultEnableZeroHeader: false,
+            sendHttpHeaders: false,
+        );
+
+        $zip->addFileFromCallback('sample.txt', callback: function () {
+            return 'Sample String Data';
+        });
+
+        $size = $zip->finish();
+
+        $this->assertEquals($size, 142);
+    }
+
+    public function testExecuteSimulation(): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            operationMode: OperationMode::SIMULATE_LAX,
+            defaultEnableZeroHeader: false,
+            sendHttpHeaders: false,
+            outputStream: $stream,
+        );
+
+        $zip->addFileFromCallback(
+            'sample.txt',
+            exactSize: 18,
+            callback: function () {
+                return 'Sample String Data';
+            }
+        );
+
+        $size = $zip->finish();
+
+        $this->assertEquals(filesize($tmp), 0);
+
+        $zip->executeSimulation();
+        fclose($stream);
+
+        clearstatcache();
+
+        $this->assertEquals(filesize($tmp), $size);
+
+        $tmpDir = $this->validateAndExtractZip($tmp);
+
+        $files = $this->getRecursiveFileList($tmpDir);
+        $this->assertSame(['sample.txt'], $files);
+    }
+
+    public function testExecuteSimulationBeforeFinish(): void
+    {
+        $this->expectException(RuntimeException::class);
+
+
+        [, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            operationMode: OperationMode::SIMULATE_LAX,
+            defaultEnableZeroHeader: false,
+            sendHttpHeaders: false,
+            outputStream: $stream,
+        );
+
+        $zip->executeSimulation();
+    }
+
+    private function addLargeFileFileFromPath(CompressionMethod $compressionMethod, $zeroHeader, $zip64): void
+    {
+        [$tmp, $stream] = $this->getTmpFileStream();
+
+        $zip = new ZipStream(
+            outputStream: $stream,
+            sendHttpHeaders: false,
+            defaultEnableZeroHeader: $zeroHeader,
+            enableZip64: $zip64,
+        );
 
         [$tmpExample, $streamExample] = $this->getTmpFileStream();
         for ($i = 0; $i <= 10000; $i++) {
@@ -645,6 +1279,6 @@ class ZipStreamTest extends TestCase
         $files = $this->getRecursiveFileList($tmpDir);
         $this->assertSame(['sample.txt'], $files);
 
-        $this->assertSame(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$method}");
+        $this->assertSame(sha1_file($tmpDir . '/sample.txt'), $shaExample, "SHA-1 Mismatch Method: {$compressionMethod->value}");
     }
 }
diff --git a/vendor/maennchen/zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.php b/vendor/maennchen/zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.php
new file mode 100644
index 0000000..2b8dbed
--- /dev/null
+++ b/vendor/maennchen/zipstream-php/test/Zs/ExtendedInformationExtraFieldTest.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace ZipStream\Test\Zs;
+
+use PHPUnit\Framework\TestCase;
+use ZipStream\Zs\ExtendedInformationExtraField;
+
+class ExtendedInformationExtraFieldTest extends TestCase
+{
+    public function testSerializesCorrectly(): void
+    {
+        $extraField = ExtendedInformationExtraField::generate();
+
+        $this->assertSame(
+            bin2hex((string) $extraField),
+            '5356' . // 2 bytes; Tag for this "extra" block type
+            '0000' // 2 bytes; TODO: Document
+        );
+    }
+}
diff --git a/vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php b/vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php
deleted file mode 100644
index 05de4fe..0000000
--- a/vendor/maennchen/zipstream-php/test/bug/BugHonorFileTimeTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace BugHonorFileTimeTest;
-
-use DateTime;
-
-use function fopen;
-
-use PHPUnit\Framework\TestCase;
-use ZipStream\Option\Archive;
-use ZipStream\Option\File;
-
-use ZipStream\ZipStream;
-
-/**
- * Asserts that specified last-modified timestamps are not overwritten when a
- * file is added
- */
-class BugHonorFileTimeTest extends TestCase
-{
-    public function testHonorsFileTime(): void
-    {
-        $archiveOpt = new Archive();
-        $fileOpt = new File();
-        $expectedTime = new DateTime('2019-04-21T19:25:00-0800');
-
-        $archiveOpt->setOutputStream(fopen('php://memory', 'wb'));
-        $fileOpt->setTime(clone $expectedTime);
-
-        $zip = new ZipStream(null, $archiveOpt);
-
-        $zip->addFile('sample.txt', 'Sample', $fileOpt);
-
-        $zip->finish();
-
-        $this->assertEquals($expectedTime, $fileOpt->getTime());
-    }
-}
diff --git a/vendor/myclabs/deep-copy/.github/FUNDING.yml b/vendor/myclabs/deep-copy/.github/FUNDING.yml
deleted file mode 100644
index b8da664..0000000
--- a/vendor/myclabs/deep-copy/.github/FUNDING.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# These are supported funding model platforms
-
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: "packagist/myclabs/deep-copy"
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/vendor/myclabs/deep-copy/.github/workflows/ci.yaml b/vendor/myclabs/deep-copy/.github/workflows/ci.yaml
deleted file mode 100644
index eac2812..0000000
--- a/vendor/myclabs/deep-copy/.github/workflows/ci.yaml
+++ /dev/null
@@ -1,101 +0,0 @@
-name: "Continuous Integration"
-
-on:
-  - pull_request
-  - push
-
-env:
-  COMPOSER_ROOT_VERSION: 1.99
-
-jobs:
-  composer-json-lint:
-    name: "Lint composer.json"
-
-    runs-on: "ubuntu-latest"
-
-    strategy:
-      matrix:
-        php-version:
-          - "8.1"
-
-    steps:
-      - name: "Checkout"
-        uses: "actions/checkout@v2"
-
-      - name: "Install PHP"
-        uses: "shivammathur/setup-php@v2"
-        with:
-          coverage: "none"
-          php-version: "${{ matrix.php-version }}"
-          tools: composer-normalize
-
-      - name: "Get composer cache directory"
-        id: composercache
-        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
-
-      - name: "Cache dependencies"
-        uses: actions/cache@v2
-        with:
-          path: ${{ steps.composercache.outputs.dir }}
-          key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-${{ hashFiles('**/composer.json') }}
-          restore-keys: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-
-
-      - name: "Install dependencies"
-        run: "composer update --no-interaction --no-progress"
-
-      - name: "Validate composer.json"
-        run: "composer validate --strict"
-
-      - name: "Normalize composer.json"
-        run: "composer-normalize --dry-run"
-
-  tests:
-    name: "Tests"
-
-    runs-on: "ubuntu-latest"
-
-    strategy:
-      matrix:
-        php-version:
-          - "7.1"
-          - "7.2"
-          - "7.3"
-          - "7.4"
-          - "8.0"
-          - "8.1"
-        dependencies:
-          - "lowest"
-          - "highest"
-
-    steps:
-      - name: "Checkout"
-        uses: "actions/checkout@v2"
-
-      - name: "Install PHP"
-        uses: "shivammathur/setup-php@v2"
-        with:
-          php-version: "${{ matrix.php-version }}"
-          ini-values: zend.assertions=1
-
-      - name: "Get composer cache directory"
-        id: composercache
-        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
-
-      - name: "Cache dependencies"
-        uses: actions/cache@v2
-        with:
-          path: ${{ steps.composercache.outputs.dir }}
-          key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-${{ hashFiles('**/composer.json') }}
-          restore-keys: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-
-
-      - name: "Install lowest dependencies"
-        if: ${{ matrix.dependencies == 'lowest' }}
-        run: "composer update --no-interaction --no-progress --prefer-lowest"
-
-      - name: "Install highest dependencies"
-        if: ${{ matrix.dependencies == 'highest' }}
-        run: "composer update --no-interaction --no-progress"
-
-      - name: "Run tests"
-        timeout-minutes: 3
-        run: "vendor/bin/phpunit"
diff --git a/vendor/myclabs/deep-copy/README.md b/vendor/myclabs/deep-copy/README.md
index 503e93d..88ae14c 100644
--- a/vendor/myclabs/deep-copy/README.md
+++ b/vendor/myclabs/deep-copy/README.md
@@ -3,7 +3,7 @@
 DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.
 
 [![Total Downloads](https://poser.pugx.org/myclabs/deep-copy/downloads.svg)](https://packagist.org/packages/myclabs/deep-copy)
-[![Integrate](https://github.com/myclabs/DeepCopy/workflows/ci/badge.svg?branch=1.x)](https://github.com/myclabs/DeepCopy/actions)
+[![Integrate](https://github.com/myclabs/DeepCopy/actions/workflows/ci.yaml/badge.svg?branch=1.x)](https://github.com/myclabs/DeepCopy/actions/workflows/ci.yaml)
 
 ## Table of Contents
 
@@ -186,6 +186,9 @@ $matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
 - `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`
 - `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`
 
+By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied).
+Using the ([`ChainableFilter`](#chainablefilter-filter)) won't stop the chain of filters.
+
 
 #### `SetNullFilter` (filter)
 
@@ -226,6 +229,34 @@ $copy = $copier->copy($object);
 ```
 
 
+#### `ChainableFilter` (filter)
+
+If you use cloning on proxy classes, you might want to apply two filters for:
+1. loading the data
+2. applying a transformation
+
+You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e. 
+the next ones may be applied).
+
+
+```php
+use DeepCopy\DeepCopy;
+use DeepCopy\Filter\ChainableFilter;
+use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
+use DeepCopy\Filter\SetNullFilter;
+use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
+use DeepCopy\Matcher\PropertyNameMatcher;
+
+$copier = new DeepCopy();
+$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
+$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
+
+$copy = $copier->copy($object);
+
+echo $copy->id; // null
+```
+
+
 #### `DoctrineCollectionFilter` (filter)
 
 If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
@@ -268,6 +299,8 @@ Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
 You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
 **Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded
 before other filters are applied!**
+We recommend to decorate the `DoctrineProxyFilter` with the `ChainableFilter` to allow applying other filters to the
+cloned lazy loaded entities.
 
 ```php
 use DeepCopy\DeepCopy;
@@ -275,7 +308,7 @@ use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
 use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
 
 $copier = new DeepCopy();
-$copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
+$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
 
 $copy = $copier->copy($object);
 
diff --git a/vendor/myclabs/deep-copy/composer.json b/vendor/myclabs/deep-copy/composer.json
index 66fb34a..f115fff 100644
--- a/vendor/myclabs/deep-copy/composer.json
+++ b/vendor/myclabs/deep-copy/composer.json
@@ -16,11 +16,12 @@
     "require-dev": {
         "doctrine/collections": "^1.6.8",
         "doctrine/common": "^2.13.3 || ^3.2.2",
+        "phpspec/prophecy": "^1.10",
         "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
     },
     "conflict": {
         "doctrine/collections": "<1.6.8",
-        "doctrine/common": "<2.13.3 || >=3,<3.2.2"
+        "doctrine/common": "<2.13.3 || >=3 <3.2.2"
     },
     "autoload": {
         "psr-4": {
@@ -32,8 +33,8 @@
     },
     "autoload-dev": {
         "psr-4": {
-            "DeepCopy\\": "fixtures/",
-            "DeepCopyTest\\": "tests/DeepCopyTest/"
+            "DeepCopyTest\\": "tests/DeepCopyTest/",
+            "DeepCopy\\": "fixtures/"
         }
     },
     "config": {
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php b/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
index 5e68c64..084858e 100644
--- a/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
@@ -7,6 +7,7 @@ use DateInterval;
 use DateTimeInterface;
 use DateTimeZone;
 use DeepCopy\Exception\CloneException;
+use DeepCopy\Filter\ChainableFilter;
 use DeepCopy\Filter\Filter;
 use DeepCopy\Matcher\Matcher;
 use DeepCopy\Reflection\ReflectionHelper;
@@ -223,6 +224,11 @@ class DeepCopy
             return;
         }
 
+        // Ignore readonly properties
+        if (method_exists($property, 'isReadOnly') && $property->isReadOnly()) {
+            return;
+        }
+
         // Apply the filters
         foreach ($this->filters as $item) {
             /** @var Matcher $matcher */
@@ -239,6 +245,10 @@ class DeepCopy
                     }
                 );
 
+                if ($filter instanceof ChainableFilter) {
+                    continue;
+                }
+
                 // If a filter matches, we stop processing this property
                 return;
             }
diff --git a/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
new file mode 100644
index 0000000..4e3f7bb
--- /dev/null
+++ b/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace DeepCopy\Filter;
+
+/**
+ * Defines a decorator filter that will not stop the chain of filters.
+ */
+class ChainableFilter implements Filter
+{
+    /**
+     * @var Filter
+     */
+    protected $filter;
+
+    public function __construct(Filter $filter)
+    {
+        $this->filter = $filter;
+    }
+
+    public function apply($object, $property, $objectCopier)
+    {
+        $this->filter->apply($object, $property, $objectCopier);
+    }
+}
diff --git a/vendor/myclabs/php-enum/LICENSE b/vendor/myclabs/php-enum/LICENSE
deleted file mode 100644
index 2a8cf22..0000000
--- a/vendor/myclabs/php-enum/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2015 My C-Labs
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial
-portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
-NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/myclabs/php-enum/README.md b/vendor/myclabs/php-enum/README.md
deleted file mode 100644
index 681d55e..0000000
--- a/vendor/myclabs/php-enum/README.md
+++ /dev/null
@@ -1,194 +0,0 @@
-# PHP Enum implementation inspired from SplEnum
-
-[![GitHub Actions][GA Image]][GA Link]
-[![Latest Stable Version](https://poser.pugx.org/myclabs/php-enum/version.png)](https://packagist.org/packages/myclabs/php-enum)
-[![Total Downloads](https://poser.pugx.org/myclabs/php-enum/downloads.png)](https://packagist.org/packages/myclabs/php-enum)
-[![Psalm Shepherd][Shepherd Image]][Shepherd Link]
-
-Maintenance for this project is [supported via Tidelift](https://tidelift.com/subscription/pkg/packagist-myclabs-php-enum?utm_source=packagist-myclabs-php-enum&utm_medium=referral&utm_campaign=readme).
-
-## Why?
-
-First, and mainly, `SplEnum` is not integrated to PHP, you have to install the extension separately.
-
-Using an enum instead of class constants provides the following advantages:
-
-- You can use an enum as a parameter type: `function setAction(Action $action) {`
-- You can use an enum as a return type: `function getAction() : Action {`
-- You can enrich the enum with methods (e.g. `format`, `parse`, …)
-- You can extend the enum to add new values (make your enum `final` to prevent it)
-- You can get a list of all the possible values (see below)
-
-This Enum class is not intended to replace class constants, but only to be used when it makes sense.
-
-## Installation
-
-```
-composer require myclabs/php-enum
-```
-
-## Declaration
-
-```php
-use MyCLabs\Enum\Enum;
-
-/**
- * Action enum
- */
-final class Action extends Enum
-{
-    private const VIEW = 'view';
-    private const EDIT = 'edit';
-}
-```
-
-## Usage
-
-```php
-$action = Action::VIEW();
-
-// or with a dynamic key:
-$action = Action::$key();
-// or with a dynamic value:
-$action = Action::from($value);
-// or
-$action = new Action($value);
-```
-
-As you can see, static methods are automatically implemented to provide quick access to an enum value.
-
-One advantage over using class constants is to be able to use an enum as a parameter type:
-
-```php
-function setAction(Action $action) {
-    // ...
-}
-```
-
-## Documentation
-
-- `__construct()` The constructor checks that the value exist in the enum
-- `__toString()` You can `echo $myValue`, it will display the enum value (value of the constant)
-- `getValue()` Returns the current value of the enum
-- `getKey()` Returns the key of the current value on Enum
-- `equals()` Tests whether enum instances are equal (returns `true` if enum values are equal, `false` otherwise)
-
-Static methods:
-
-- `from()` Creates an Enum instance, checking that the value exist in the enum
-- `toArray()` method Returns all possible values as an array (constant name in key, constant value in value)
-- `keys()` Returns the names (keys) of all constants in the Enum class
-- `values()` Returns instances of the Enum class of all Enum constants (constant name in key, Enum instance in value)
-- `isValid()` Check if tested value is valid on enum set
-- `isValidKey()` Check if tested key is valid on enum set
-- `assertValidValue()` Assert the value is valid on enum set, throwing exception otherwise
-- `search()` Return key for searched value
-
-### Static methods
-
-```php
-final class Action extends Enum
-{
-    private const VIEW = 'view';
-    private const EDIT = 'edit';
-}
-
-// Static method:
-$action = Action::VIEW();
-$action = Action::EDIT();
-```
-
-Static method helpers are implemented using [`__callStatic()`](http://www.php.net/manual/en/language.oop5.overloading.php#object.callstatic).
-
-If you care about IDE autocompletion, you can either implement the static methods yourself:
-
-```php
-final class Action extends Enum
-{
-    private const VIEW = 'view';
-
-    /**
-     * @return Action
-     */
-    public static function VIEW() {
-        return new Action(self::VIEW);
-    }
-}
-```
-
-or you can use phpdoc (this is supported in PhpStorm for example):
-
-```php
-/**
- * @method static Action VIEW()
- * @method static Action EDIT()
- */
-final class Action extends Enum
-{
-    private const VIEW = 'view';
-    private const EDIT = 'edit';
-}
-```
-
-## Native enums and migration
-Native enum arrived to PHP in version 8.1: https://www.php.net/enumerations  
-If your project is running PHP 8.1+ or your library has it as a minimum requirement you should use it instead of this library.
-
-When migrating from `myclabs/php-enum`, the effort should be small if the usage was in the recommended way:
-- private constants
-- final classes
-- no method overridden
-
-Changes for migration:
-- Class definition should be changed from
-```php
-/**
- * @method static Action VIEW()
- * @method static Action EDIT()
- */
-final class Action extends Enum
-{
-    private const VIEW = 'view';
-    private const EDIT = 'edit';
-}
-```
- to
-```php
-enum Action: string
-{
-    case VIEW = 'view';
-    case EDIT = 'edit';
-}
-```
-All places where the class was used as a type will continue to work.
-
-Usages and the change needed:
-
-| Operation                                                      | myclabs/php-enum                                                           | native enum                                                                                                                                                                                                                              |
-|----------------------------------------------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Obtain an instance will change from                            | `$enumCase = Action::VIEW()`                                               | `$enumCase = Action::VIEW`                                                                                                                                                                                                               |
-| Create an enum from a backed value                             | `$enumCase = new Action('view')`                                           | `$enumCase = Action::from('view')`                                                                                                                                                                                                       |
-| Get the backed value of the enum instance                      | `$enumCase->getValue()`                                                    | `$enumCase->value`                                                                                                                                                                                                                       |
-| Compare two enum instances                                     | `$enumCase1 == $enumCase2` <br/> or <br/> `$enumCase1->equals($enumCase2)` | `$enumCase1 === $enumCase2`                                                                                                                                                                                                              |
-| Get the key/name of the enum instance                          | `$enumCase->getKey()`                                                      | `$enumCase->name`                                                                                                                                                                                                                        |
-| Get a list of all the possible instances of the enum           | `Action::values()`                                                         | `Action::cases()`                                                                                                                                                                                                                        |
-| Get a map of possible instances of the enum mapped by name     | `Action::values()`                                                         | `array_combine(array_map(fn($case) => $case->name, Action::cases()), Action::cases())` <br/> or <br/> `(new ReflectionEnum(Action::class))->getConstants()`                                                                              |
-| Get a list of all possible names of the enum                   | `Action::keys()`                                                           | `array_map(fn($case) => $case->name, Action::cases())`                                                                                                                                                                                   |
-| Get a list of all possible backed values of the enum           | `Action::toArray()`                                                        | `array_map(fn($case) => $case->value, Action::cases())`                                                                                                                                                                                  |
-| Get a map of possible backed values of the enum mapped by name | `Action::toArray()`                                                        | `array_combine(array_map(fn($case) => $case->name, Action::cases()), array_map(fn($case) => $case->value, Action::cases()))` <br/> or <br/> `array_map(fn($case) => $case->value, (new ReflectionEnum(Action::class))->getConstants()))` |
-
-## Related projects
-
-- [PHP 8.1+ native enum](https://www.php.net/enumerations)
-- [Doctrine enum mapping](https://github.com/acelaya/doctrine-enum-type)
-- [Symfony ParamConverter integration](https://github.com/Ex3v/MyCLabsEnumParamConverter)
-- [PHPStan integration](https://github.com/timeweb/phpstan-enum)
-
-
-[GA Image]: https://github.com/myclabs/php-enum/workflows/CI/badge.svg
-
-[GA Link]: https://github.com/myclabs/php-enum/actions?query=workflow%3A%22CI%22+branch%3Amaster
-
-[Shepherd Image]: https://shepherd.dev/github/myclabs/php-enum/coverage.svg
-
-[Shepherd Link]: https://shepherd.dev/github/myclabs/php-enum
diff --git a/vendor/myclabs/php-enum/SECURITY.md b/vendor/myclabs/php-enum/SECURITY.md
deleted file mode 100644
index 84fd4e3..0000000
--- a/vendor/myclabs/php-enum/SECURITY.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Security Policy
-
-## Supported Versions
-
-Only the latest stable release is supported.
-
-## Reporting a Vulnerability
-
-To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
-
-Tidelift will coordinate the fix and disclosure.
diff --git a/vendor/myclabs/php-enum/composer.json b/vendor/myclabs/php-enum/composer.json
deleted file mode 100644
index 978cb19..0000000
--- a/vendor/myclabs/php-enum/composer.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
-    "name": "myclabs/php-enum",
-    "type": "library",
-    "description": "PHP Enum implementation",
-    "keywords": ["enum"],
-    "homepage": "http://github.com/myclabs/php-enum",
-    "license": "MIT",
-    "authors": [
-        {
-            "name": "PHP Enum contributors",
-            "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
-        }
-    ],
-    "autoload": {
-        "psr-4": {
-            "MyCLabs\\Enum\\": "src/"
-        },
-        "classmap": [
-            "stubs/Stringable.php"
-        ]
-    },
-    "autoload-dev": {
-        "psr-4": {
-            "MyCLabs\\Tests\\Enum\\": "tests/"
-        }
-    },
-    "require": {
-        "php": "^7.3 || ^8.0",
-        "ext-json": "*"
-    },
-    "require-dev": {
-        "phpunit/phpunit": "^9.5",
-        "squizlabs/php_codesniffer": "1.*",
-        "vimeo/psalm": "^4.6.2"
-    }
-}
diff --git a/vendor/myclabs/php-enum/src/Enum.php b/vendor/myclabs/php-enum/src/Enum.php
deleted file mode 100644
index 4c94cf6..0000000
--- a/vendor/myclabs/php-enum/src/Enum.php
+++ /dev/null
@@ -1,318 +0,0 @@
-<?php
-/**
- * @link    http://github.com/myclabs/php-enum
- * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
- */
-
-namespace MyCLabs\Enum;
-
-/**
- * Base Enum class
- *
- * Create an enum by implementing this class and adding class constants.
- *
- * @author Matthieu Napoli <matthieu@mnapoli.fr>
- * @author Daniel Costa <danielcosta@gmail.com>
- * @author Mirosław Filip <mirfilip@gmail.com>
- *
- * @psalm-template T
- * @psalm-immutable
- * @psalm-consistent-constructor
- */
-abstract class Enum implements \JsonSerializable, \Stringable
-{
-    /**
-     * Enum value
-     *
-     * @var mixed
-     * @psalm-var T
-     */
-    protected $value;
-
-    /**
-     * Enum key, the constant name
-     *
-     * @var string
-     */
-    private $key;
-
-    /**
-     * Store existing constants in a static cache per object.
-     *
-     *
-     * @var array
-     * @psalm-var array<class-string, array<string, mixed>>
-     */
-    protected static $cache = [];
-
-    /**
-     * Cache of instances of the Enum class
-     *
-     * @var array
-     * @psalm-var array<class-string, array<string, static>>
-     */
-    protected static $instances = [];
-
-    /**
-     * Creates a new value of some type
-     *
-     * @psalm-pure
-     * @param mixed $value
-     *
-     * @psalm-param T $value
-     * @throws \UnexpectedValueException if incompatible type is given.
-     */
-    public function __construct($value)
-    {
-        if ($value instanceof static) {
-           /** @psalm-var T */
-            $value = $value->getValue();
-        }
-
-        /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */
-        $this->key = static::assertValidValueReturningKey($value);
-
-        /** @psalm-var T */
-        $this->value = $value;
-    }
-
-    /**
-     * This method exists only for the compatibility reason when deserializing a previously serialized version
-     * that didn't had the key property
-     */
-    public function __wakeup()
-    {
-        /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */
-        if ($this->key === null) {
-            /**
-             * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm
-             * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed
-             */
-            $this->key = static::search($this->value);
-        }
-    }
-
-    /**
-     * @param mixed $value
-     * @return static
-     */
-    public static function from($value): self
-    {
-        $key = static::assertValidValueReturningKey($value);
-
-        return self::__callStatic($key, []);
-    }
-
-    /**
-     * @psalm-pure
-     * @return mixed
-     * @psalm-return T
-     */
-    public function getValue()
-    {
-        return $this->value;
-    }
-
-    /**
-     * Returns the enum key (i.e. the constant name).
-     *
-     * @psalm-pure
-     * @return string
-     */
-    public function getKey()
-    {
-        return $this->key;
-    }
-
-    /**
-     * @psalm-pure
-     * @psalm-suppress InvalidCast
-     * @return string
-     */
-    public function __toString()
-    {
-        return (string)$this->value;
-    }
-
-    /**
-     * Determines if Enum should be considered equal with the variable passed as a parameter.
-     * Returns false if an argument is an object of different class or not an object.
-     *
-     * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4
-     *
-     * @psalm-pure
-     * @psalm-param mixed $variable
-     * @return bool
-     */
-    final public function equals($variable = null): bool
-    {
-        return $variable instanceof self
-            && $this->getValue() === $variable->getValue()
-            && static::class === \get_class($variable);
-    }
-
-    /**
-     * Returns the names (keys) of all constants in the Enum class
-     *
-     * @psalm-pure
-     * @psalm-return list<string>
-     * @return array
-     */
-    public static function keys()
-    {
-        return \array_keys(static::toArray());
-    }
-
-    /**
-     * Returns instances of the Enum class of all Enum constants
-     *
-     * @psalm-pure
-     * @psalm-return array<string, static>
-     * @return static[] Constant name in key, Enum instance in value
-     */
-    public static function values()
-    {
-        $values = array();
-
-        /** @psalm-var T $value */
-        foreach (static::toArray() as $key => $value) {
-            $values[$key] = new static($value);
-        }
-
-        return $values;
-    }
-
-    /**
-     * Returns all possible values as an array
-     *
-     * @psalm-pure
-     * @psalm-suppress ImpureStaticProperty
-     *
-     * @psalm-return array<string, mixed>
-     * @return array Constant name in key, constant value in value
-     */
-    public static function toArray()
-    {
-        $class = static::class;
-
-        if (!isset(static::$cache[$class])) {
-            /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
-            $reflection            = new \ReflectionClass($class);
-            /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
-            static::$cache[$class] = $reflection->getConstants();
-        }
-
-        return static::$cache[$class];
-    }
-
-    /**
-     * Check if is valid enum value
-     *
-     * @param $value
-     * @psalm-param mixed $value
-     * @psalm-pure
-     * @psalm-assert-if-true T $value
-     * @return bool
-     */
-    public static function isValid($value)
-    {
-        return \in_array($value, static::toArray(), true);
-    }
-
-    /**
-     * Asserts valid enum value
-     *
-     * @psalm-pure
-     * @psalm-assert T $value
-     * @param mixed $value
-     */
-    public static function assertValidValue($value): void
-    {
-        self::assertValidValueReturningKey($value);
-    }
-
-    /**
-     * Asserts valid enum value
-     *
-     * @psalm-pure
-     * @psalm-assert T $value
-     * @param mixed $value
-     * @return string
-     */
-    private static function assertValidValueReturningKey($value): string
-    {
-        if (false === ($key = static::search($value))) {
-            throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class);
-        }
-
-        return $key;
-    }
-
-    /**
-     * Check if is valid enum key
-     *
-     * @param $key
-     * @psalm-param string $key
-     * @psalm-pure
-     * @return bool
-     */
-    public static function isValidKey($key)
-    {
-        $array = static::toArray();
-
-        return isset($array[$key]) || \array_key_exists($key, $array);
-    }
-
-    /**
-     * Return key for value
-     *
-     * @param mixed $value
-     *
-     * @psalm-param mixed $value
-     * @psalm-pure
-     * @return string|false
-     */
-    public static function search($value)
-    {
-        return \array_search($value, static::toArray(), true);
-    }
-
-    /**
-     * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant
-     *
-     * @param string $name
-     * @param array  $arguments
-     *
-     * @return static
-     * @throws \BadMethodCallException
-     *
-     * @psalm-pure
-     */
-    public static function __callStatic($name, $arguments)
-    {
-        $class = static::class;
-        if (!isset(self::$instances[$class][$name])) {
-            $array = static::toArray();
-            if (!isset($array[$name]) && !\array_key_exists($name, $array)) {
-                $message = "No static method or enum constant '$name' in class " . static::class;
-                throw new \BadMethodCallException($message);
-            }
-            return self::$instances[$class][$name] = new static($array[$name]);
-        }
-        return clone self::$instances[$class][$name];
-    }
-
-    /**
-     * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode()
-     * natively.
-     *
-     * @return mixed
-     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
-     * @psalm-pure
-     */
-    #[\ReturnTypeWillChange]
-    public function jsonSerialize()
-    {
-        return $this->getValue();
-    }
-}
diff --git a/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php b/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php
deleted file mode 100644
index 302bf80..0000000
--- a/vendor/myclabs/php-enum/src/PHPUnit/Comparator.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-namespace MyCLabs\Enum\PHPUnit;
-
-use MyCLabs\Enum\Enum;
-use SebastianBergmann\Comparator\ComparisonFailure;
-
-/**
- * Use this Comparator to get nice output when using PHPUnit assertEquals() with Enums.
- *
- * Add this to your PHPUnit bootstrap PHP file:
- *
- * \SebastianBergmann\Comparator\Factory::getInstance()->register(new \MyCLabs\Enum\PHPUnit\Comparator());
- */
-final class Comparator extends \SebastianBergmann\Comparator\Comparator
-{
-    public function accepts($expected, $actual)
-    {
-        return $expected instanceof Enum && (
-                $actual instanceof Enum || $actual === null
-            );
-    }
-
-    /**
-     * @param Enum $expected
-     * @param Enum|null $actual
-     *
-     * @return void
-     */
-    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
-    {
-        if ($expected->equals($actual)) {
-            return;
-        }
-
-        throw new ComparisonFailure(
-            $expected,
-            $actual,
-            $this->formatEnum($expected),
-            $this->formatEnum($actual),
-            false,
-            'Failed asserting that two Enums are equal.'
-        );
-    }
-
-    private function formatEnum(Enum $enum = null)
-    {
-        if ($enum === null) {
-            return "null";
-        }
-
-        return get_class($enum)."::{$enum->getKey()}()";
-    }
-}
diff --git a/vendor/myclabs/php-enum/stubs/Stringable.php b/vendor/myclabs/php-enum/stubs/Stringable.php
deleted file mode 100644
index 4811af7..0000000
--- a/vendor/myclabs/php-enum/stubs/Stringable.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-
-if (\PHP_VERSION_ID < 80000 && !interface_exists('Stringable')) {
-    interface Stringable
-    {
-        /**
-         * @return string
-         */
-        public function __toString();
-    }
-}
diff --git a/vendor/paragonie/sodium_compat/LICENSE b/vendor/paragonie/sodium_compat/LICENSE
index 84b3e00..af760a7 100644
--- a/vendor/paragonie/sodium_compat/LICENSE
+++ b/vendor/paragonie/sodium_compat/LICENSE
@@ -1,6 +1,6 @@
 ISC License
 
-Copyright (c) 2016-2022, Paragon Initiative Enterprises <security at paragonie dot com>
+Copyright (c) 2016-2023, Paragon Initiative Enterprises <security at paragonie dot com>
 Copyright (c) 2013-2019, Frank Denis <j at pureftpd dot org>
 
 Permission to use, copy, modify, and/or distribute this software for any
diff --git a/vendor/paragonie/sodium_compat/README.md b/vendor/paragonie/sodium_compat/README.md
index 88e7629..36cc3c2 100644
--- a/vendor/paragonie/sodium_compat/README.md
+++ b/vendor/paragonie/sodium_compat/README.md
@@ -11,12 +11,47 @@
 Sodium Compat is a pure PHP polyfill for the Sodium cryptography library 
 (libsodium), a core extension in PHP 7.2.0+ and otherwise [available in PECL](https://pecl.php.net/package/libsodium).
 
-This library tentatively supports PHP 5.2.4 - 8.x (latest), but officially
-only supports [non-EOL'd versions of PHP](https://secure.php.net/supported-versions.php).
-
 If you have the PHP extension installed, Sodium Compat will opportunistically
 and transparently use the PHP extension instead of our implementation.
 
+## Major Versions and Branches
+
+sodium_compat v1.21.0 was the last v1.x release from the master branch. From now
+on, all future releases that support PHP 5.2 - 5.6 and 32-bit integers will be
+[in the `v1.x` branch](v1.x).
+
+Newer versions of sodium_compat (i.e., v2.0.0) will continue to live in the master
+branch, unless a new major version is needed. The goal of this work is to improve
+code readability and performance, while reducing boilerplate code.
+
+When in doubt, refer to the README file in [the master branch](https://github.com/paragonie/sodium_compat/blob/master/README.md)
+for the latest in version information.
+
+### Which version should I use?
+
+| sodium_compat version | PHP versions supported | 32-bit support? | Branch                                                        |
+|-----------------------|------------------------|-----------------|---------------------------------------------------------------|
+| `v1.x.y`              | 5.2.4 - LATEST         | YES             | [v1.x](https://github.com/paragonie/sodium_compat/tree/v1.x)  |
+| `v2.x.y`              | 7.2 - LATEST           | NO              | **master**                                                    |
+
+If you need 32-bit PHP support (`PHP_INT_SIZE == 4`), continue using sodium_compat v1.x.
+If you want improved performance and smaller dependencies, use v2.x.
+
+We recommend libraries and frameworks set a Composer version constraint as follows:
+
+```javascript
+{
+    "require": {
+        /* ... */
+        "paragonie/sodium_compat": ">= 1"
+        /* ... */
+    }
+}
+```
+
+Applications should, conversely, specify the actual version that matters to them 
+and their deployments.
+
 ## IMPORTANT!
 
 This cryptography library has not been formally audited by an independent third 
@@ -266,6 +301,10 @@ insightful technical information you may find helpful.
     * `crypto_sign()`
     * `crypto_sign_open()`
 * PECL Libsodium Features
+    * `crypto_aead_aegis128l_encrypt()`
+    * `crypto_aead_aegis128l_decrypt()`
+    * `crypto_aead_aegis256_encrypt()`
+    * `crypto_aead_aegis256_decrypt()`
     * `crypto_aead_aes256gcm_encrypt()`
     * `crypto_aead_aes256gcm_decrypt()`
     * `crypto_aead_chacha20poly1305_encrypt()`
@@ -343,13 +382,13 @@ insightful technical information you may find helpful.
 
 ### Features Excluded from this Polyfill
 
-* `\Sodium\memzero()` - Although we expose this API endpoint, we can't reliably
+* `sodium_memzero()` - Although we expose this API endpoint, we can't reliably
   zero buffers from PHP.
   
   If you have the PHP extension installed, sodium_compat
   will use the native implementation to zero out the string provided. Otherwise
   it will throw a `SodiumException`.
-* `\Sodium\crypto_pwhash()` - It's not feasible to polyfill scrypt or Argon2
+* `sodium_crypto_pwhash()` - It's not feasible to polyfill scrypt or Argon2
   into PHP and get reasonable performance. Users would feel motivated to select
   parameters that downgrade security to avoid denial of service (DoS) attacks.
   
@@ -361,6 +400,8 @@ insightful technical information you may find helpful.
   To detect support for Argon2i at runtime, use
   `ParagonIE_Sodium_Compat::crypto_pwhash_is_available()`, which returns a
    boolean value (`TRUE` or `FALSE`).
+* Libsodium's HKDF API (`crypto_kdf_hkdf_*()`) is not included because PHP has
+  its own [HMAC features](https://php.met/hash_hmac) amd it was not deemed necessary.
 
 ### PHPCompatibility Ruleset
 
diff --git a/vendor/paragonie/sodium_compat/autoload.php b/vendor/paragonie/sodium_compat/autoload.php
index fd12f87..bfd9e4a 100644
--- a/vendor/paragonie/sodium_compat/autoload.php
+++ b/vendor/paragonie/sodium_compat/autoload.php
@@ -54,6 +54,9 @@ if (PHP_VERSION_ID >= 50300) {
     // unless PHP >= 5.3.0
     require_once dirname(__FILE__) . '/lib/namespaced.php';
     require_once dirname(__FILE__) . '/lib/sodium_compat.php';
+    if (!defined('SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES')) {
+        require_once dirname(__FILE__) . '/lib/php84compat_const.php';
+    }
 } else {
     require_once dirname(__FILE__) . '/src/PHP52/SplFixedArray.php';
 }
@@ -71,5 +74,8 @@ if (PHP_VERSION_ID < 70200 || !extension_loaded('sodium')) {
     // Older versions of {PHP, ext/sodium} will not define these
     require_once(dirname(__FILE__) . '/lib/php72compat.php');
 }
+if (PHP_VERSION_ID < 80400 || !extension_loaded('sodium')) {
+    require_once dirname(__FILE__) . '/lib/php84compat.php';
+}
 require_once(dirname(__FILE__) . '/lib/stream-xchacha20.php');
 require_once(dirname(__FILE__) . '/lib/ristretto255.php');
diff --git a/vendor/paragonie/sodium_compat/lib/php72compat.php b/vendor/paragonie/sodium_compat/lib/php72compat.php
index bf1e05e..b9da5e0 100644
--- a/vendor/paragonie/sodium_compat/lib/php72compat.php
+++ b/vendor/paragonie/sodium_compat/lib/php72compat.php
@@ -14,14 +14,14 @@ foreach (array(
     'BASE64_VARIANT_ORIGINAL_NO_PADDING',
     'BASE64_VARIANT_URLSAFE',
     'BASE64_VARIANT_URLSAFE_NO_PADDING',
-    'CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES',
-    'CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES',
-    'CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES',
-    'CRYPTO_AEAD_CHACHA20POLY1305_ABYTES',
     'CRYPTO_AEAD_AES256GCM_KEYBYTES',
     'CRYPTO_AEAD_AES256GCM_NSECBYTES',
     'CRYPTO_AEAD_AES256GCM_NPUBBYTES',
     'CRYPTO_AEAD_AES256GCM_ABYTES',
+    'CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES',
+    'CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES',
+    'CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES',
+    'CRYPTO_AEAD_CHACHA20POLY1305_ABYTES',
     'CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES',
     'CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES',
     'CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES',
@@ -115,8 +115,12 @@ if (!is_callable('sodium_add')) {
      * @return void
      * @throws SodiumException
      */
-    function sodium_add(&$string1, $string2)
-    {
+    function sodium_add(
+        #[\SensitiveParameter]
+        &$string1,
+        #[\SensitiveParameter]
+        $string2
+    ) {
         ParagonIE_Sodium_Compat::add($string1, $string2);
     }
 }
@@ -130,8 +134,12 @@ if (!is_callable('sodium_base642bin')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_base642bin($string, $variant, $ignore ='')
-    {
+    function sodium_base642bin(
+        #[\SensitiveParameter]
+        $string,
+        $variant,
+        $ignore =''
+    ) {
         return ParagonIE_Sodium_Compat::base642bin($string, $variant, $ignore);
     }
 }
@@ -144,8 +152,11 @@ if (!is_callable('sodium_bin2base64')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_bin2base64($string, $variant)
-    {
+    function sodium_bin2base64(
+        #[\SensitiveParameter]
+        $string,
+        $variant
+    ) {
         return ParagonIE_Sodium_Compat::bin2base64($string, $variant);
     }
 }
@@ -157,8 +168,10 @@ if (!is_callable('sodium_bin2hex')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_bin2hex($string)
-    {
+    function sodium_bin2hex(
+        #[\SensitiveParameter]
+        $string
+    ) {
         return ParagonIE_Sodium_Compat::bin2hex($string);
     }
 }
@@ -171,8 +184,12 @@ if (!is_callable('sodium_compare')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_compare($string1, $string2)
-    {
+    function sodium_compare(
+        #[\SensitiveParameter]
+        $string1,
+        #[\SensitiveParameter]
+        $string2
+    ) {
         return ParagonIE_Sodium_Compat::compare($string1, $string2);
     }
 }
@@ -185,8 +202,13 @@ if (!is_callable('sodium_crypto_aead_aes256gcm_decrypt')) {
      * @param string $key
      * @return string|bool
      */
-    function sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $additional_data, $nonce, $key)
-    {
+    function sodium_crypto_aead_aes256gcm_decrypt(
+        $ciphertext,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_aead_aes256gcm_decrypt(
                 $ciphertext,
@@ -215,8 +237,14 @@ if (!is_callable('sodium_crypto_aead_aes256gcm_encrypt')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_aead_aes256gcm_encrypt($message, $additional_data, $nonce, $key)
-    {
+    function sodium_crypto_aead_aes256gcm_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_aead_aes256gcm_encrypt($message, $additional_data, $nonce, $key);
     }
 }
@@ -239,8 +267,13 @@ if (!is_callable('sodium_crypto_aead_chacha20poly1305_decrypt')) {
      * @param string $key
      * @return string|bool
      */
-    function sodium_crypto_aead_chacha20poly1305_decrypt($ciphertext, $additional_data, $nonce, $key)
-    {
+    function sodium_crypto_aead_chacha20poly1305_decrypt(
+        $ciphertext,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_decrypt(
                 $ciphertext,
@@ -266,8 +299,14 @@ if (!is_callable('sodium_crypto_aead_chacha20poly1305_encrypt')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_aead_chacha20poly1305_encrypt($message, $additional_data, $nonce, $key)
-    {
+    function sodium_crypto_aead_chacha20poly1305_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_encrypt(
             $message,
             $additional_data,
@@ -296,8 +335,13 @@ if (!is_callable('sodium_crypto_aead_chacha20poly1305_ietf_decrypt')) {
      * @param string $key
      * @return string|bool
      */
-    function sodium_crypto_aead_chacha20poly1305_ietf_decrypt($message, $additional_data, $nonce, $key)
-    {
+    function sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
+        $message,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_decrypt(
                 $message,
@@ -323,8 +367,14 @@ if (!is_callable('sodium_crypto_aead_chacha20poly1305_ietf_encrypt')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_aead_chacha20poly1305_ietf_encrypt($message, $additional_data, $nonce, $key)
-    {
+    function sodium_crypto_aead_chacha20poly1305_ietf_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt(
             $message,
             $additional_data,
@@ -353,8 +403,13 @@ if (!is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) {
      * @param string $key
      * @return string|bool
      */
-    function sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($ciphertext, $additional_data, $nonce, $key)
-    {
+    function sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
+        $ciphertext,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_decrypt(
                 $ciphertext,
@@ -382,9 +437,11 @@ if (!is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) {
      * @throws TypeError
      */
     function sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
+        #[\SensitiveParameter]
         $message,
         $additional_data,
         $nonce,
+        #[\SensitiveParameter]
         $key
     ) {
         return ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_encrypt(
@@ -416,8 +473,11 @@ if (!is_callable('sodium_crypto_auth')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_auth($message, $key)
-    {
+    function sodium_crypto_auth(
+        $message,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_auth($message, $key);
     }
 }
@@ -442,8 +502,12 @@ if (!is_callable('sodium_crypto_auth_verify')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_auth_verify($mac, $message, $key)
-    {
+    function sodium_crypto_auth_verify(
+        $mac,
+        $message,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_auth_verify($mac, $message, $key);
     }
 }
@@ -457,8 +521,13 @@ if (!is_callable('sodium_crypto_box')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_box($message, $nonce, $key_pair)
-    {
+    function sodium_crypto_box(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box($message, $nonce, $key_pair);
     }
 }
@@ -483,8 +552,11 @@ if (!is_callable('sodium_crypto_box_keypair_from_secretkey_and_publickey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_box_keypair_from_secretkey_and_publickey($secret_key, $public_key)
-    {
+    function sodium_crypto_box_keypair_from_secretkey_and_publickey(
+        #[\SensitiveParameter]
+        $secret_key,
+        $public_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey($secret_key, $public_key);
     }
 }
@@ -496,8 +568,12 @@ if (!is_callable('sodium_crypto_box_open')) {
      * @param string $key_pair
      * @return string|bool
      */
-    function sodium_crypto_box_open($ciphertext, $nonce, $key_pair)
-    {
+    function sodium_crypto_box_open(
+        $ciphertext,
+        $nonce,
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_box_open($ciphertext, $nonce, $key_pair);
         } catch (Error $ex) {
@@ -515,8 +591,10 @@ if (!is_callable('sodium_crypto_box_publickey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_box_publickey($key_pair)
-    {
+    function sodium_crypto_box_publickey(
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_publickey($key_pair);
     }
 }
@@ -528,8 +606,10 @@ if (!is_callable('sodium_crypto_box_publickey_from_secretkey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_box_publickey_from_secretkey($secret_key)
-    {
+    function sodium_crypto_box_publickey_from_secretkey(
+        #[\SensitiveParameter]
+        $secret_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_publickey_from_secretkey($secret_key);
     }
 }
@@ -542,8 +622,11 @@ if (!is_callable('sodium_crypto_box_seal')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_box_seal($message, $public_key)
-    {
+    function sodium_crypto_box_seal(
+        #[\SensitiveParameter]
+        $message,
+        $public_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_seal($message, $public_key);
     }
 }
@@ -555,8 +638,11 @@ if (!is_callable('sodium_crypto_box_seal_open')) {
      * @return string|bool
      * @throws SodiumException
      */
-    function sodium_crypto_box_seal_open($message, $key_pair)
-    {
+    function sodium_crypto_box_seal_open(
+        $message,
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_box_seal_open($message, $key_pair);
         } catch (SodiumException $ex) {
@@ -575,8 +661,10 @@ if (!is_callable('sodium_crypto_box_secretkey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_box_secretkey($key_pair)
-    {
+    function sodium_crypto_box_secretkey(
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_secretkey($key_pair);
     }
 }
@@ -588,8 +676,10 @@ if (!is_callable('sodium_crypto_box_seed_keypair')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_box_seed_keypair($seed)
-    {
+    function sodium_crypto_box_seed_keypair(
+        #[\SensitiveParameter]
+        $seed
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_seed_keypair($seed);
     }
 }
@@ -603,8 +693,12 @@ if (!is_callable('sodium_crypto_generichash')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_generichash($message, $key = null, $length = 32)
-    {
+    function sodium_crypto_generichash(
+        $message,
+        #[\SensitiveParameter]
+        $key = null,
+        $length = 32
+    ) {
         return ParagonIE_Sodium_Compat::crypto_generichash($message, $key, $length);
     }
 }
@@ -631,8 +725,11 @@ if (!is_callable('sodium_crypto_generichash_init')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_generichash_init($key = null, $length = 32)
-    {
+    function sodium_crypto_generichash_init(
+        #[\SensitiveParameter]
+        $key = null,
+        $length = 32
+    ) {
         return ParagonIE_Sodium_Compat::crypto_generichash_init($key, $length);
     }
 }
@@ -656,8 +753,11 @@ if (!is_callable('sodium_crypto_generichash_update')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_generichash_update(&$state, $message = '')
-    {
+    function sodium_crypto_generichash_update(
+        #[\SensitiveParameter]
+        &$state,
+        $message = ''
+    ) {
         ParagonIE_Sodium_Compat::crypto_generichash_update($state, $message);
     }
 }
@@ -682,8 +782,13 @@ if (!is_callable('sodium_crypto_kdf_derive_from_key')) {
      * @return string
      * @throws Exception
      */
-    function sodium_crypto_kdf_derive_from_key($subkey_length, $subkey_id, $context, $key)
-    {
+    function sodium_crypto_kdf_derive_from_key(
+        $subkey_length,
+        $subkey_id,
+        $context,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kdf_derive_from_key(
             $subkey_length,
             $subkey_id,
@@ -703,8 +808,13 @@ if (!is_callable('sodium_crypto_kx')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_kx($my_secret, $their_public, $client_public, $server_public)
-    {
+    function sodium_crypto_kx(
+        #[\SensitiveParameter]
+        $my_secret,
+        $their_public,
+        $client_public,
+        $server_public
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kx(
             $my_secret,
             $their_public,
@@ -719,8 +829,10 @@ if (!is_callable('sodium_crypto_kx_seed_keypair')) {
      * @return string
      * @throws Exception
      */
-    function sodium_crypto_kx_seed_keypair($seed)
-    {
+    function sodium_crypto_kx_seed_keypair(
+        #[\SensitiveParameter]
+        $seed
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kx_seed_keypair($seed);
     }
 }
@@ -741,8 +853,11 @@ if (!is_callable('sodium_crypto_kx_client_session_keys')) {
      * @return array{0: string, 1: string}
      * @throws SodiumException
      */
-    function sodium_crypto_kx_client_session_keys($client_key_pair, $server_key)
-    {
+    function sodium_crypto_kx_client_session_keys(
+        #[\SensitiveParameter]
+        $client_key_pair,
+        $server_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kx_client_session_keys($client_key_pair, $server_key);
     }
 }
@@ -753,8 +868,11 @@ if (!is_callable('sodium_crypto_kx_server_session_keys')) {
      * @return array{0: string, 1: string}
      * @throws SodiumException
      */
-    function sodium_crypto_kx_server_session_keys($server_key_pair, $client_key)
-    {
+    function sodium_crypto_kx_server_session_keys(
+        #[\SensitiveParameter]
+        $server_key_pair,
+        $client_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kx_server_session_keys($server_key_pair, $client_key);
     }
 }
@@ -764,8 +882,10 @@ if (!is_callable('sodium_crypto_kx_secretkey')) {
      * @return string
      * @throws Exception
      */
-    function sodium_crypto_kx_secretkey($key_pair)
-    {
+    function sodium_crypto_kx_secretkey(
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kx_secretkey($key_pair);
     }
 }
@@ -775,8 +895,10 @@ if (!is_callable('sodium_crypto_kx_publickey')) {
      * @return string
      * @throws Exception
      */
-    function sodium_crypto_kx_publickey($key_pair)
-    {
+    function sodium_crypto_kx_publickey(
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kx_publickey($key_pair);
     }
 }
@@ -793,8 +915,15 @@ if (!is_callable('sodium_crypto_pwhash')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_pwhash($length, $passwd, $salt, $opslimit, $memlimit, $algo = null)
-    {
+    function sodium_crypto_pwhash(
+        $length,
+        #[\SensitiveParameter]
+        $passwd,
+        $salt,
+        $opslimit,
+        $memlimit,
+        $algo = null
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash($length, $passwd, $salt, $opslimit, $memlimit, $algo);
     }
 }
@@ -808,8 +937,12 @@ if (!is_callable('sodium_crypto_pwhash_str')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_pwhash_str($passwd, $opslimit, $memlimit)
-    {
+    function sodium_crypto_pwhash_str(
+        #[\SensitiveParameter]
+        $passwd,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_str($passwd, $opslimit, $memlimit);
     }
 }
@@ -823,8 +956,12 @@ if (!is_callable('sodium_crypto_pwhash_str_needs_rehash')) {
      *
      * @throws SodiumException
      */
-    function sodium_crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit)
-    {
+    function sodium_crypto_pwhash_str_needs_rehash(
+        #[\SensitiveParameter]
+        $hash,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit);
     }
 }
@@ -837,8 +974,12 @@ if (!is_callable('sodium_crypto_pwhash_str_verify')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_pwhash_str_verify($passwd, $hash)
-    {
+    function sodium_crypto_pwhash_str_verify(
+        #[\SensitiveParameter]
+        $passwd,
+        #[\SensitiveParameter]
+        $hash
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_str_verify($passwd, $hash);
     }
 }
@@ -854,8 +995,14 @@ if (!is_callable('sodium_crypto_pwhash_scryptsalsa208sha256')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_pwhash_scryptsalsa208sha256($length, $passwd, $salt, $opslimit, $memlimit)
-    {
+    function sodium_crypto_pwhash_scryptsalsa208sha256(
+        $length,
+        #[\SensitiveParameter]
+        $passwd,
+        $salt,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_scryptsalsa208sha256(
             $length,
             $passwd,
@@ -875,8 +1022,12 @@ if (!is_callable('sodium_crypto_pwhash_scryptsalsa208sha256_str')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit)
-    {
+    function sodium_crypto_pwhash_scryptsalsa208sha256_str(
+        #[\SensitiveParameter]
+        $passwd,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit);
     }
 }
@@ -889,8 +1040,12 @@ if (!is_callable('sodium_crypto_pwhash_scryptsalsa208sha256_str_verify')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash)
-    {
+    function sodium_crypto_pwhash_scryptsalsa208sha256_str_verify(
+        #[\SensitiveParameter]
+        $passwd,
+        #[\SensitiveParameter]
+        $hash
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash);
     }
 }
@@ -903,8 +1058,11 @@ if (!is_callable('sodium_crypto_scalarmult')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_scalarmult($n, $p)
-    {
+    function sodium_crypto_scalarmult(
+        #[\SensitiveParameter]
+        $n,
+        $p
+    ) {
         return ParagonIE_Sodium_Compat::crypto_scalarmult($n, $p);
     }
 }
@@ -916,8 +1074,10 @@ if (!is_callable('sodium_crypto_scalarmult_base')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_scalarmult_base($n)
-    {
+    function sodium_crypto_scalarmult_base(
+        #[\SensitiveParameter]
+        $n
+    ) {
         return ParagonIE_Sodium_Compat::crypto_scalarmult_base($n);
     }
 }
@@ -931,8 +1091,13 @@ if (!is_callable('sodium_crypto_secretbox')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_secretbox($message, $nonce, $key)
-    {
+    function sodium_crypto_secretbox(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key);
     }
 }
@@ -955,8 +1120,12 @@ if (!is_callable('sodium_crypto_secretbox_open')) {
      * @param string $key
      * @return string|bool
      */
-    function sodium_crypto_secretbox_open($ciphertext, $nonce, $key)
-    {
+    function sodium_crypto_secretbox_open(
+        $ciphertext,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_secretbox_open($ciphertext, $nonce, $key);
         } catch (Error $ex) {
@@ -972,8 +1141,10 @@ if (!is_callable('sodium_crypto_secretstream_xchacha20poly1305_init_push')) {
      * @return array<int, string>
      * @throws SodiumException
      */
-    function sodium_crypto_secretstream_xchacha20poly1305_init_push($key)
-    {
+    function sodium_crypto_secretstream_xchacha20poly1305_init_push(
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_init_push($key);
     }
 }
@@ -987,7 +1158,9 @@ if (!is_callable('sodium_crypto_secretstream_xchacha20poly1305_push')) {
      * @throws SodiumException
      */
     function sodium_crypto_secretstream_xchacha20poly1305_push(
+        #[\SensitiveParameter]
         &$state,
+        #[\SensitiveParameter]
         $message,
         $additional_data = '',
         $tag = 0
@@ -1007,8 +1180,11 @@ if (!is_callable('sodium_crypto_secretstream_xchacha20poly1305_init_pull')) {
      * @return string
      * @throws Exception
      */
-    function sodium_crypto_secretstream_xchacha20poly1305_init_pull($header, $key)
-    {
+    function sodium_crypto_secretstream_xchacha20poly1305_init_pull(
+        $header,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_init_pull($header, $key);
     }
 }
@@ -1020,8 +1196,12 @@ if (!is_callable('sodium_crypto_secretstream_xchacha20poly1305_pull')) {
      * @return bool|array{0: string, 1: int}
      * @throws SodiumException
      */
-    function sodium_crypto_secretstream_xchacha20poly1305_pull(&$state, $ciphertext, $additional_data = '')
-    {
+    function sodium_crypto_secretstream_xchacha20poly1305_pull(
+        #[\SensitiveParameter]
+        &$state,
+        $ciphertext,
+        $additional_data = ''
+    ) {
         return ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_pull(
             $state,
             $ciphertext,
@@ -1035,8 +1215,10 @@ if (!is_callable('sodium_crypto_secretstream_xchacha20poly1305_rekey')) {
      * @return void
      * @throws SodiumException
      */
-    function sodium_crypto_secretstream_xchacha20poly1305_rekey(&$state)
-    {
+    function sodium_crypto_secretstream_xchacha20poly1305_rekey(
+        #[\SensitiveParameter]
+        &$state
+    ) {
         ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_rekey($state);
     }
 }
@@ -1059,8 +1241,11 @@ if (!is_callable('sodium_crypto_shorthash')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_shorthash($message, $key = '')
-    {
+    function sodium_crypto_shorthash(
+        $message,
+        #[\SensitiveParameter]
+        $key = ''
+    ) {
         return ParagonIE_Sodium_Compat::crypto_shorthash($message, $key);
     }
 }
@@ -1084,8 +1269,11 @@ if (!is_callable('sodium_crypto_sign')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign($message, $secret_key)
-    {
+    function sodium_crypto_sign(
+        $message,
+        #[\SensitiveParameter]
+        $secret_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign($message, $secret_key);
     }
 }
@@ -1098,8 +1286,11 @@ if (!is_callable('sodium_crypto_sign_detached')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign_detached($message, $secret_key)
-    {
+    function sodium_crypto_sign_detached(
+        $message,
+        #[\SensitiveParameter]
+        $secret_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_detached($message, $secret_key);
     }
 }
@@ -1112,8 +1303,11 @@ if (!is_callable('sodium_crypto_sign_keypair_from_secretkey_and_publickey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign_keypair_from_secretkey_and_publickey($secret_key, $public_key)
-    {
+    function sodium_crypto_sign_keypair_from_secretkey_and_publickey(
+        #[\SensitiveParameter]
+        $secret_key,
+        $public_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_keypair_from_secretkey_and_publickey($secret_key, $public_key);
     }
 }
@@ -1155,8 +1349,10 @@ if (!is_callable('sodium_crypto_sign_publickey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign_publickey($key_pair)
-    {
+    function sodium_crypto_sign_publickey(
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_publickey($key_pair);
     }
 }
@@ -1168,8 +1364,10 @@ if (!is_callable('sodium_crypto_sign_publickey_from_secretkey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign_publickey_from_secretkey($secret_key)
-    {
+    function sodium_crypto_sign_publickey_from_secretkey(
+        #[\SensitiveParameter]
+        $secret_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_publickey_from_secretkey($secret_key);
     }
 }
@@ -1181,8 +1379,10 @@ if (!is_callable('sodium_crypto_sign_secretkey')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign_secretkey($key_pair)
-    {
+    function sodium_crypto_sign_secretkey(
+        #[\SensitiveParameter]
+        $key_pair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_secretkey($key_pair);
     }
 }
@@ -1194,8 +1394,10 @@ if (!is_callable('sodium_crypto_sign_seed_keypair')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign_seed_keypair($seed)
-    {
+    function sodium_crypto_sign_seed_keypair(
+        #[\SensitiveParameter]
+        $seed
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_seed_keypair($seed);
     }
 }
@@ -1235,8 +1437,10 @@ if (!is_callable('sodium_crypto_sign_ed25519_sk_to_curve25519')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_sign_ed25519_sk_to_curve25519($secret_key)
-    {
+    function sodium_crypto_sign_ed25519_sk_to_curve25519(
+        #[\SensitiveParameter]
+        $secret_key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_ed25519_sk_to_curve25519($secret_key);
     }
 }
@@ -1250,8 +1454,12 @@ if (!is_callable('sodium_crypto_stream')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_stream($length, $nonce, $key)
-    {
+    function sodium_crypto_stream(
+        $length,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_stream($length, $nonce, $key);
     }
 }
@@ -1276,8 +1484,13 @@ if (!is_callable('sodium_crypto_stream_xor')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_stream_xor($message, $nonce, $key)
-    {
+    function sodium_crypto_stream_xor(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_stream_xor($message, $nonce, $key);
     }
 }
@@ -1291,8 +1504,11 @@ if (!is_callable('sodium_hex2bin')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_hex2bin($string, $ignore = '')
-    {
+    function sodium_hex2bin(
+        #[\SensitiveParameter]
+        $string,
+        $ignore = ''
+    ) {
         return ParagonIE_Sodium_Compat::hex2bin($string, $ignore);
     }
 }
@@ -1304,8 +1520,10 @@ if (!is_callable('sodium_increment')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_increment(&$string)
-    {
+    function sodium_increment(
+        #[\SensitiveParameter]
+        &$string
+    ) {
         ParagonIE_Sodium_Compat::increment($string);
     }
 }
@@ -1348,8 +1566,12 @@ if (!is_callable('sodium_memcmp')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_memcmp($string1, $string2)
-    {
+    function sodium_memcmp(
+        #[\SensitiveParameter]
+        $string1,
+        #[\SensitiveParameter]
+        $string2
+    ) {
         return ParagonIE_Sodium_Compat::memcmp($string1, $string2);
     }
 }
@@ -1360,9 +1582,13 @@ if (!is_callable('sodium_memzero')) {
      * @return void
      * @throws SodiumException
      * @throws TypeError
+     *
+     * @psalm-suppress ReferenceConstraintViolation
      */
-    function sodium_memzero(&$string)
-    {
+    function sodium_memzero(
+        #[\SensitiveParameter]
+        &$string
+    ) {
         ParagonIE_Sodium_Compat::memzero($string);
     }
 }
@@ -1375,8 +1601,11 @@ if (!is_callable('sodium_pad')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_pad($unpadded, $block_size)
-    {
+    function sodium_pad(
+        #[\SensitiveParameter]
+        $unpadded,
+        $block_size
+    ) {
         return ParagonIE_Sodium_Compat::pad($unpadded, $block_size, true);
     }
 }
@@ -1389,8 +1618,11 @@ if (!is_callable('sodium_unpad')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_unpad($padded, $block_size)
-    {
+    function sodium_unpad(
+        #[\SensitiveParameter]
+        $padded,
+        $block_size
+    ) {
         return ParagonIE_Sodium_Compat::unpad($padded, $block_size, true);
     }
 }
diff --git a/vendor/paragonie/sodium_compat/lib/php84compat.php b/vendor/paragonie/sodium_compat/lib/php84compat.php
new file mode 100644
index 0000000..ee172a0
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/lib/php84compat.php
@@ -0,0 +1,130 @@
+<?php
+
+require_once dirname(dirname(__FILE__)) . '/autoload.php';
+
+/**
+ * This file will monkey patch the pure-PHP implementation in place of the
+ * PECL functions and constants, but only if they do not already exist.
+ *
+ * Thus, the functions or constants just proxy to the appropriate
+ * ParagonIE_Sodium_Compat method or class constant, respectively.
+ */
+foreach (array(
+    'CRYPTO_AEAD_AESGIS128L_KEYBYTES',
+    'CRYPTO_AEAD_AESGIS128L_NSECBYTES',
+    'CRYPTO_AEAD_AESGIS128L_NPUBBYTES',
+    'CRYPTO_AEAD_AESGIS128L_ABYTES',
+    'CRYPTO_AEAD_AESGIS256_KEYBYTES',
+    'CRYPTO_AEAD_AESGIS256_NSECBYTES',
+    'CRYPTO_AEAD_AESGIS256_NPUBBYTES',
+    'CRYPTO_AEAD_AESGIS256_ABYTES',
+    ) as $constant
+) {
+    if (!defined("SODIUM_$constant") && defined("ParagonIE_Sodium_Compat::$constant")) {
+        define("SODIUM_$constant", constant("ParagonIE_Sodium_Compat::$constant"));
+    }
+}
+if (!is_callable('sodium_crypto_aead_aegis128l_decrypt')) {
+    /**
+     * @see ParagonIE_Sodium_Compat::crypto_aead_aegis128l_decrypt()
+     * @param string $ciphertext
+     * @param string $additional_data
+     * @param string $nonce
+     * @param string $key
+     * @return string
+     * @throws SodiumException
+     */
+    function sodium_crypto_aead_aegis128l_decrypt(
+        $ciphertext,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
+        return ParagonIE_Sodium_Compat::crypto_aead_aegis128l_decrypt(
+            $ciphertext,
+            $additional_data,
+            $nonce,
+            $key
+        );
+    }
+}
+if (!is_callable('sodium_crypto_aead_aegis128l_encrypt')) {
+    /**
+     * @see ParagonIE_Sodium_Compat::crypto_aead_aegis128l_encrypt()
+     * @param string $message
+     * @param string $additional_data
+     * @param string $nonce
+     * @param string $key
+     * @return string
+     * @throws SodiumException
+     * @throws TypeError
+     */
+    function sodium_crypto_aead_aegis128l_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
+        return ParagonIE_Sodium_Compat::crypto_aead_aegis128l_encrypt(
+            $message,
+            $additional_data,
+            $nonce,
+            $key
+        );
+    }
+}
+if (!is_callable('sodium_crypto_aead_aegis256_decrypt')) {
+    /**
+     * @see ParagonIE_Sodium_Compat::crypto_aead_aegis256_encrypt()
+     * @param string $ciphertext
+     * @param string $additional_data
+     * @param string $nonce
+     * @param string $key
+     * @return string
+     * @throws SodiumException
+     */
+    function sodium_crypto_aead_aegis256_decrypt(
+        $ciphertext,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
+        return ParagonIE_Sodium_Compat::crypto_aead_aegis256_decrypt(
+            $ciphertext,
+            $additional_data,
+            $nonce,
+            $key
+        );
+    }
+}
+if (!is_callable('sodium_crypto_aead_aegis256_encrypt')) {
+    /**
+     * @see ParagonIE_Sodium_Compat::crypto_aead_aegis256_encrypt()
+     * @param string $message
+     * @param string $additional_data
+     * @param string $nonce
+     * @param string $key
+     * @return string
+     * @throws SodiumException
+     * @throws TypeError
+     */
+    function sodium_crypto_aead_aegis256_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $additional_data,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
+        return ParagonIE_Sodium_Compat::crypto_aead_aegis256_encrypt(
+            $message,
+            $additional_data,
+            $nonce,
+            $key
+        );
+    }
+}
diff --git a/vendor/paragonie/sodium_compat/lib/php84compat_const.php b/vendor/paragonie/sodium_compat/lib/php84compat_const.php
new file mode 100644
index 0000000..ee1ed91
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/lib/php84compat_const.php
@@ -0,0 +1,10 @@
+<?php
+const SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES = 16;
+const SODIUM_CRYPTO_AEAD_AEGIS128L_NSECBYTES = 0;
+const SODIUM_CRYPTO_AEAD_AEGIS128L_NPUBBYTES = 32;
+const SODIUM_CRYPTO_AEAD_AEGIS128L_ABYTES = 32;
+
+const SODIUM_CRYPTO_AEAD_AEGIS256_KEYBYTES = 32;
+const SODIUM_CRYPTO_AEAD_AEGIS256_NSECBYTES = 0;
+const SODIUM_CRYPTO_AEAD_AEGIS256_NPUBBYTES = 32;
+const SODIUM_CRYPTO_AEAD_AEGIS256_ABYTES = 32;
diff --git a/vendor/paragonie/sodium_compat/lib/ristretto255.php b/vendor/paragonie/sodium_compat/lib/ristretto255.php
index 5a0c6dc..587b4f6 100644
--- a/vendor/paragonie/sodium_compat/lib/ristretto255.php
+++ b/vendor/paragonie/sodium_compat/lib/ristretto255.php
@@ -47,8 +47,12 @@ if (!is_callable('sodium_crypto_core_ristretto255_add')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_add($p, $q)
-    {
+    function sodium_crypto_core_ristretto255_add(
+        #[\SensitiveParameter]
+        $p,
+        #[\SensitiveParameter]
+        $q
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_add($p, $q, true);
     }
 }
@@ -60,8 +64,10 @@ if (!is_callable('sodium_crypto_core_ristretto255_from_hash')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_from_hash($s)
-    {
+    function sodium_crypto_core_ristretto255_from_hash(
+        #[\SensitiveParameter]
+        $s
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_from_hash($s, true);
     }
 }
@@ -73,8 +79,10 @@ if (!is_callable('sodium_crypto_core_ristretto255_is_valid_point')) {
      * @return bool
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_is_valid_point($s)
-    {
+    function sodium_crypto_core_ristretto255_is_valid_point(
+        #[\SensitiveParameter]
+        $s
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_is_valid_point($s, true);
     }
 }
@@ -99,8 +107,12 @@ if (!is_callable('sodium_crypto_core_ristretto255_scalar_add')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_scalar_add($x, $y)
-    {
+    function sodium_crypto_core_ristretto255_scalar_add(
+        #[\SensitiveParameter]
+        $x,
+        #[\SensitiveParameter]
+        $y
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_scalar_add($x, $y, true);
     }
 }
@@ -112,8 +124,10 @@ if (!is_callable('sodium_crypto_core_ristretto255_scalar_complement')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_scalar_complement($s)
-    {
+    function sodium_crypto_core_ristretto255_scalar_complement(
+        #[\SensitiveParameter]
+        $s
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_scalar_complement($s, true);
     }
 }
@@ -125,8 +139,10 @@ if (!is_callable('sodium_crypto_core_ristretto255_scalar_invert')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_scalar_invert($p)
-    {
+    function sodium_crypto_core_ristretto255_scalar_invert(
+        #[\SensitiveParameter]
+        $p
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_scalar_invert($p, true);
     }
 }
@@ -139,8 +155,12 @@ if (!is_callable('sodium_crypto_core_ristretto255_scalar_mul')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_scalar_mul($x, $y)
-    {
+    function sodium_crypto_core_ristretto255_scalar_mul(
+        #[\SensitiveParameter]
+        $x,
+        #[\SensitiveParameter]
+        $y
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_scalar_mul($x, $y, true);
     }
 }
@@ -152,8 +172,10 @@ if (!is_callable('sodium_crypto_core_ristretto255_scalar_negate')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_scalar_negate($s)
-    {
+    function sodium_crypto_core_ristretto255_scalar_negate(
+        #[\SensitiveParameter]
+        $s
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_scalar_negate($s, true);
     }
 }
@@ -177,8 +199,10 @@ if (!is_callable('sodium_crypto_core_ristretto255_scalar_reduce')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_scalar_reduce($s)
-    {
+    function sodium_crypto_core_ristretto255_scalar_reduce(
+        #[\SensitiveParameter]
+        $s
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_scalar_reduce($s, true);
     }
 }
@@ -191,8 +215,12 @@ if (!is_callable('sodium_crypto_core_ristretto255_scalar_sub')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_scalar_sub($x, $y)
-    {
+    function sodium_crypto_core_ristretto255_scalar_sub(
+        #[\SensitiveParameter]
+        $x,
+        #[\SensitiveParameter]
+        $y
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_scalar_sub($x, $y, true);
     }
 }
@@ -205,8 +233,12 @@ if (!is_callable('sodium_crypto_core_ristretto255_sub')) {
      * @return string
      * @throws SodiumException
      */
-    function sodium_crypto_core_ristretto255_sub($p, $q)
-    {
+    function sodium_crypto_core_ristretto255_sub(
+        #[\SensitiveParameter]
+        $p,
+        #[\SensitiveParameter]
+        $q
+    ) {
         return ParagonIE_Sodium_Compat::ristretto255_sub($p, $q, true);
     }
 }
@@ -219,8 +251,12 @@ if (!is_callable('sodium_crypto_scalarmult_ristretto255')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_scalarmult_ristretto255($n, $p)
-    {
+    function sodium_crypto_scalarmult_ristretto255(
+        #[\SensitiveParameter]
+        $n,
+        #[\SensitiveParameter]
+        $p
+    ) {
         return ParagonIE_Sodium_Compat::scalarmult_ristretto255($n, $p, true);
     }
 }
@@ -232,8 +268,10 @@ if (!is_callable('sodium_crypto_scalarmult_ristretto255_base')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_scalarmult_ristretto255_base($n)
-    {
+    function sodium_crypto_scalarmult_ristretto255_base(
+        #[\SensitiveParameter]
+        $n
+    ) {
         return ParagonIE_Sodium_Compat::scalarmult_ristretto255_base($n, true);
     }
 }
\ No newline at end of file
diff --git a/vendor/paragonie/sodium_compat/lib/sodium_compat.php b/vendor/paragonie/sodium_compat/lib/sodium_compat.php
index 04f4bc7..72a561d 100644
--- a/vendor/paragonie/sodium_compat/lib/sodium_compat.php
+++ b/vendor/paragonie/sodium_compat/lib/sodium_compat.php
@@ -20,8 +20,10 @@ if (!is_callable('\\Sodium\\bin2hex')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function bin2hex($string)
-    {
+    function bin2hex(
+        #[\SensitiveParameter]
+        $string
+    ) {
         return ParagonIE_Sodium_Compat::bin2hex($string);
     }
 }
@@ -34,8 +36,12 @@ if (!is_callable('\\Sodium\\compare')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function compare($a, $b)
-    {
+    function compare(
+        #[\SensitiveParameter]
+        $a,
+        #[\SensitiveParameter]
+        $b
+    ) {
         return ParagonIE_Sodium_Compat::compare($a, $b);
     }
 }
@@ -48,8 +54,13 @@ if (!is_callable('\\Sodium\\crypto_aead_aes256gcm_decrypt')) {
      * @param string $key
      * @return string|bool
      */
-    function crypto_aead_aes256gcm_decrypt($message, $assocData, $nonce, $key)
-    {
+    function crypto_aead_aes256gcm_decrypt(
+        $message,
+        $assocData,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_aead_aes256gcm_decrypt($message, $assocData, $nonce, $key);
         } catch (\TypeError $ex) {
@@ -70,8 +81,14 @@ if (!is_callable('\\Sodium\\crypto_aead_aes256gcm_encrypt')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_aead_aes256gcm_encrypt($message, $assocData, $nonce, $key)
-    {
+    function crypto_aead_aes256gcm_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $assocData,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_aead_aes256gcm_encrypt($message, $assocData, $nonce, $key);
     }
 }
@@ -94,8 +111,13 @@ if (!is_callable('\\Sodium\\crypto_aead_chacha20poly1305_decrypt')) {
      * @param string $key
      * @return string|bool
      */
-    function crypto_aead_chacha20poly1305_decrypt($message, $assocData, $nonce, $key)
-    {
+    function crypto_aead_chacha20poly1305_decrypt(
+        $message,
+        $assocData,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_decrypt($message, $assocData, $nonce, $key);
         } catch (\TypeError $ex) {
@@ -116,8 +138,14 @@ if (!is_callable('\\Sodium\\crypto_aead_chacha20poly1305_encrypt')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_aead_chacha20poly1305_encrypt($message, $assocData, $nonce, $key)
-    {
+    function crypto_aead_chacha20poly1305_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $assocData,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_encrypt($message, $assocData, $nonce, $key);
     }
 }
@@ -130,8 +158,13 @@ if (!is_callable('\\Sodium\\crypto_aead_chacha20poly1305_ietf_decrypt')) {
      * @param string $key
      * @return string|bool
      */
-    function crypto_aead_chacha20poly1305_ietf_decrypt($message, $assocData, $nonce, $key)
-    {
+    function crypto_aead_chacha20poly1305_ietf_decrypt(
+        $message,
+        $assocData,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_decrypt($message, $assocData, $nonce, $key);
         } catch (\TypeError $ex) {
@@ -152,8 +185,14 @@ if (!is_callable('\\Sodium\\crypto_aead_chacha20poly1305_ietf_encrypt')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_aead_chacha20poly1305_ietf_encrypt($message, $assocData, $nonce, $key)
-    {
+    function crypto_aead_chacha20poly1305_ietf_encrypt(
+        #[\SensitiveParameter]
+        $message,
+        $assocData,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt($message, $assocData, $nonce, $key);
     }
 }
@@ -166,8 +205,11 @@ if (!is_callable('\\Sodium\\crypto_auth')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_auth($message, $key)
-    {
+    function crypto_auth(
+        $message,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_auth($message, $key);
     }
 }
@@ -181,8 +223,12 @@ if (!is_callable('\\Sodium\\crypto_auth_verify')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_auth_verify($mac, $message, $key)
-    {
+    function crypto_auth_verify(
+        $mac,
+        $message,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_auth_verify($mac, $message, $key);
     }
 }
@@ -196,8 +242,13 @@ if (!is_callable('\\Sodium\\crypto_box')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_box($message, $nonce, $kp)
-    {
+    function crypto_box(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $kp
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box($message, $nonce, $kp);
     }
 }
@@ -222,8 +273,11 @@ if (!is_callable('\\Sodium\\crypto_box_keypair_from_secretkey_and_publickey')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_box_keypair_from_secretkey_and_publickey($sk, $pk)
-    {
+    function crypto_box_keypair_from_secretkey_and_publickey(
+        #[\SensitiveParameter]
+        $sk,
+        $pk
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey($sk, $pk);
     }
 }
@@ -235,8 +289,13 @@ if (!is_callable('\\Sodium\\crypto_box_open')) {
      * @param string $kp
      * @return string|bool
      */
-    function crypto_box_open($message, $nonce, $kp)
-    {
+    function crypto_box_open(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $kp
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_box_open($message, $nonce, $kp);
         } catch (\TypeError $ex) {
@@ -254,8 +313,10 @@ if (!is_callable('\\Sodium\\crypto_box_publickey')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_box_publickey($keypair)
-    {
+    function crypto_box_publickey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_publickey($keypair);
     }
 }
@@ -267,8 +328,10 @@ if (!is_callable('\\Sodium\\crypto_box_publickey_from_secretkey')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_box_publickey_from_secretkey($sk)
-    {
+    function crypto_box_publickey_from_secretkey(
+        #[\SensitiveParameter]
+        $sk
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_publickey_from_secretkey($sk);
     }
 }
@@ -281,8 +344,11 @@ if (!is_callable('\\Sodium\\crypto_box_seal')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_box_seal($message, $publicKey)
-    {
+    function crypto_box_seal(
+        #[\SensitiveParameter]
+        $message,
+        $publicKey
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_seal($message, $publicKey);
     }
 }
@@ -293,8 +359,11 @@ if (!is_callable('\\Sodium\\crypto_box_seal_open')) {
      * @param string $kp
      * @return string|bool
      */
-    function crypto_box_seal_open($message, $kp)
-    {
+    function crypto_box_seal_open(
+        $message,
+        #[\SensitiveParameter]
+        $kp
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_box_seal_open($message, $kp);
         } catch (\TypeError $ex) {
@@ -312,8 +381,10 @@ if (!is_callable('\\Sodium\\crypto_box_secretkey')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_box_secretkey($keypair)
-    {
+    function crypto_box_secretkey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_box_secretkey($keypair);
     }
 }
@@ -327,8 +398,12 @@ if (!is_callable('\\Sodium\\crypto_generichash')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_generichash($message, $key = null, $outLen = 32)
-    {
+    function crypto_generichash(
+        $message,
+        #[\SensitiveParameter]
+        $key = null,
+        $outLen = 32
+    ) {
         return ParagonIE_Sodium_Compat::crypto_generichash($message, $key, $outLen);
     }
 }
@@ -341,8 +416,11 @@ if (!is_callable('\\Sodium\\crypto_generichash_final')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_generichash_final(&$ctx, $outputLength = 32)
-    {
+    function crypto_generichash_final(
+        #[\SensitiveParameter]
+        &$ctx,
+        $outputLength = 32
+    ) {
         return ParagonIE_Sodium_Compat::crypto_generichash_final($ctx, $outputLength);
     }
 }
@@ -355,8 +433,11 @@ if (!is_callable('\\Sodium\\crypto_generichash_init')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_generichash_init($key = null, $outLen = 32)
-    {
+    function crypto_generichash_init(
+        #[\SensitiveParameter]
+        $key = null,
+        $outLen = 32
+    ) {
         return ParagonIE_Sodium_Compat::crypto_generichash_init($key, $outLen);
     }
 }
@@ -369,8 +450,11 @@ if (!is_callable('\\Sodium\\crypto_generichash_update')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_generichash_update(&$ctx, $message = '')
-    {
+    function crypto_generichash_update(
+        #[\SensitiveParameter]
+        &$ctx,
+        $message = ''
+    ) {
         ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, $message);
     }
 }
@@ -385,8 +469,13 @@ if (!is_callable('\\Sodium\\crypto_kx')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_kx($my_secret, $their_public, $client_public, $server_public)
-    {
+    function crypto_kx(
+        #[\SensitiveParameter]
+        $my_secret,
+        $their_public,
+        $client_public,
+        $server_public
+    ) {
         return ParagonIE_Sodium_Compat::crypto_kx(
             $my_secret,
             $their_public,
@@ -408,8 +497,14 @@ if (!is_callable('\\Sodium\\crypto_pwhash')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit)
-    {
+    function crypto_pwhash(
+        $outlen,
+        #[\SensitiveParameter]
+        $passwd,
+        $salt,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit);
     }
 }
@@ -423,8 +518,12 @@ if (!is_callable('\\Sodium\\crypto_pwhash_str')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_pwhash_str($passwd, $opslimit, $memlimit)
-    {
+    function crypto_pwhash_str(
+        #[\SensitiveParameter]
+        $passwd,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_str($passwd, $opslimit, $memlimit);
     }
 }
@@ -437,8 +536,12 @@ if (!is_callable('\\Sodium\\crypto_pwhash_str_verify')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_pwhash_str_verify($passwd, $hash)
-    {
+    function crypto_pwhash_str_verify(
+        #[\SensitiveParameter]
+        $passwd,
+        #[\SensitiveParameter]
+        $hash
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_str_verify($passwd, $hash);
     }
 }
@@ -454,8 +557,15 @@ if (!is_callable('\\Sodium\\crypto_pwhash_scryptsalsa208sha256')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_pwhash_scryptsalsa208sha256($outlen, $passwd, $salt, $opslimit, $memlimit)
-    {
+    function crypto_pwhash_scryptsalsa208sha256(
+        $outlen,
+        #[\SensitiveParameter]
+        $passwd,
+        #[\SensitiveParameter]
+        $salt,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_scryptsalsa208sha256($outlen, $passwd, $salt, $opslimit, $memlimit);
     }
 }
@@ -469,8 +579,12 @@ if (!is_callable('\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit)
-    {
+    function crypto_pwhash_scryptsalsa208sha256_str(
+        #[\SensitiveParameter]
+        $passwd,
+        $opslimit,
+        $memlimit
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit);
     }
 }
@@ -483,8 +597,12 @@ if (!is_callable('\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str_verify')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash)
-    {
+    function crypto_pwhash_scryptsalsa208sha256_str_verify(
+        #[\SensitiveParameter]
+        $passwd,
+        #[\SensitiveParameter]
+        $hash
+    ) {
         return ParagonIE_Sodium_Compat::crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash);
     }
 }
@@ -497,8 +615,11 @@ if (!is_callable('\\Sodium\\crypto_scalarmult')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_scalarmult($n, $p)
-    {
+    function crypto_scalarmult(
+        #[\SensitiveParameter]
+        $n,
+        $p
+    ) {
         return ParagonIE_Sodium_Compat::crypto_scalarmult($n, $p);
     }
 }
@@ -510,8 +631,10 @@ if (!is_callable('\\Sodium\\crypto_scalarmult_base')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_scalarmult_base($n)
-    {
+    function crypto_scalarmult_base(
+        #[\SensitiveParameter]
+        $n
+    ) {
         return ParagonIE_Sodium_Compat::crypto_scalarmult_base($n);
     }
 }
@@ -525,8 +648,13 @@ if (!is_callable('\\Sodium\\crypto_secretbox')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_secretbox($message, $nonce, $key)
-    {
+    function crypto_secretbox(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key);
     }
 }
@@ -538,8 +666,12 @@ if (!is_callable('\\Sodium\\crypto_secretbox_open')) {
      * @param string $key
      * @return string|bool
      */
-    function crypto_secretbox_open($message, $nonce, $key)
-    {
+    function crypto_secretbox_open(
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         try {
             return ParagonIE_Sodium_Compat::crypto_secretbox_open($message, $nonce, $key);
         } catch (\TypeError $ex) {
@@ -558,8 +690,11 @@ if (!is_callable('\\Sodium\\crypto_shorthash')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_shorthash($message, $key = '')
-    {
+    function crypto_shorthash(
+        $message,
+        #[\SensitiveParameter]
+        $key = ''
+    ) {
         return ParagonIE_Sodium_Compat::crypto_shorthash($message, $key);
     }
 }
@@ -572,8 +707,11 @@ if (!is_callable('\\Sodium\\crypto_sign')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_sign($message, $sk)
-    {
+    function crypto_sign(
+        $message,
+        #[\SensitiveParameter]
+        $sk
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign($message, $sk);
     }
 }
@@ -586,8 +724,11 @@ if (!is_callable('\\Sodium\\crypto_sign_detached')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_sign_detached($message, $sk)
-    {
+    function crypto_sign_detached(
+        $message,
+        #[\SensitiveParameter]
+        $sk
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_detached($message, $sk);
     }
 }
@@ -629,8 +770,10 @@ if (!is_callable('\\Sodium\\crypto_sign_publickey')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_sign_publickey($keypair)
-    {
+    function crypto_sign_publickey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_publickey($keypair);
     }
 }
@@ -642,8 +785,10 @@ if (!is_callable('\\Sodium\\crypto_sign_publickey_from_secretkey')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_sign_publickey_from_secretkey($sk)
-    {
+    function crypto_sign_publickey_from_secretkey(
+        #[\SensitiveParameter]
+        $sk
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_publickey_from_secretkey($sk);
     }
 }
@@ -655,8 +800,10 @@ if (!is_callable('\\Sodium\\crypto_sign_secretkey')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_sign_secretkey($keypair)
-    {
+    function crypto_sign_secretkey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_secretkey($keypair);
     }
 }
@@ -668,8 +815,10 @@ if (!is_callable('\\Sodium\\crypto_sign_seed_keypair')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_sign_seed_keypair($seed)
-    {
+    function crypto_sign_seed_keypair(
+        #[\SensitiveParameter]
+        $seed
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_seed_keypair($seed);
     }
 }
@@ -709,8 +858,10 @@ if (!is_callable('\\Sodium\\crypto_sign_ed25519_sk_to_curve25519')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_sign_ed25519_sk_to_curve25519($sk)
-    {
+    function crypto_sign_ed25519_sk_to_curve25519(
+        #[\SensitiveParameter]
+        $sk
+    ) {
         return ParagonIE_Sodium_Compat::crypto_sign_ed25519_sk_to_curve25519($sk);
     }
 }
@@ -724,8 +875,12 @@ if (!is_callable('\\Sodium\\crypto_stream')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_stream($len, $nonce, $key)
-    {
+    function crypto_stream(
+        $len,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_stream($len, $nonce, $key);
     }
 }
@@ -739,8 +894,13 @@ if (!is_callable('\\Sodium\\crypto_stream_xor')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function crypto_stream_xor($message, $nonce, $key)
-    {
+    function crypto_stream_xor(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_stream_xor($message, $nonce, $key);
     }
 }
@@ -752,8 +912,10 @@ if (!is_callable('\\Sodium\\hex2bin')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function hex2bin($string)
-    {
+    function hex2bin(
+        #[\SensitiveParameter]
+        $string
+    ) {
         return ParagonIE_Sodium_Compat::hex2bin($string);
     }
 }
@@ -766,8 +928,12 @@ if (!is_callable('\\Sodium\\memcmp')) {
      * @throws \SodiumException
      * @throws \TypeError
      */
-    function memcmp($a, $b)
-    {
+    function memcmp(
+        #[\SensitiveParameter]
+        $a,
+        #[\SensitiveParameter]
+        $b
+    ) {
         return ParagonIE_Sodium_Compat::memcmp($a, $b);
     }
 }
@@ -783,8 +949,10 @@ if (!is_callable('\\Sodium\\memzero')) {
      * @psalm-suppress MissingReturnType
      * @psalm-suppress ReferenceConstraintViolation
      */
-    function memzero(&$str)
-    {
+    function memzero(
+        #[\SensitiveParameter]
+        &$str
+    ) {
         ParagonIE_Sodium_Compat::memzero($str);
     }
 }
diff --git a/vendor/paragonie/sodium_compat/lib/stream-xchacha20.php b/vendor/paragonie/sodium_compat/lib/stream-xchacha20.php
index ffeae33..f1c551f 100644
--- a/vendor/paragonie/sodium_compat/lib/stream-xchacha20.php
+++ b/vendor/paragonie/sodium_compat/lib/stream-xchacha20.php
@@ -10,8 +10,12 @@ if (!is_callable('sodium_crypto_stream_xchacha20')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_stream_xchacha20($len, $nonce, $key)
-    {
+    function sodium_crypto_stream_xchacha20(
+        $len,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_stream_xchacha20($len, $nonce, $key, true);
     }
 }
@@ -36,8 +40,13 @@ if (!is_callable('sodium_crypto_stream_xchacha20_xor')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_stream_xchacha20_xor($message, $nonce, $key)
-    {
+    function sodium_crypto_stream_xchacha20_xor(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_stream_xchacha20_xor($message, $nonce, $key, true);
     }
 }
@@ -52,8 +61,14 @@ if (!is_callable('sodium_crypto_stream_xchacha20_xor_ic')) {
      * @throws SodiumException
      * @throws TypeError
      */
-    function sodium_crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key)
-    {
+    function sodium_crypto_stream_xchacha20_xor_ic(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        $counter,
+        #[\SensitiveParameter]
+        $key
+    ) {
         return ParagonIE_Sodium_Compat::crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key, true);
     }
 }
diff --git a/vendor/paragonie/sodium_compat/src/Compat.php b/vendor/paragonie/sodium_compat/src/Compat.php
index 3afe97c..7c4cd70 100644
--- a/vendor/paragonie/sodium_compat/src/Compat.php
+++ b/vendor/paragonie/sodium_compat/src/Compat.php
@@ -59,6 +59,14 @@ class ParagonIE_Sodium_Compat
     const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
     const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
     const CRYPTO_AEAD_AES256GCM_ABYTES = 16;
+    const CRYPTO_AEAD_AEGIS128L_KEYBYTES = 16;
+    const CRYPTO_AEAD_AEGIS128L_NSECBYTES = 0;
+    const CRYPTO_AEAD_AEGIS128L_NPUBBYTES = 16;
+    const CRYPTO_AEAD_AEGIS128L_ABYTES = 32;
+    const CRYPTO_AEAD_AEGIS256_KEYBYTES = 32;
+    const CRYPTO_AEAD_AEGIS256_NSECBYTES = 0;
+    const CRYPTO_AEAD_AEGIS256_NPUBBYTES = 32;
+    const CRYPTO_AEAD_AEGIS256_ABYTES = 32;
     const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32;
     const CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES = 0;
     const CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES = 8;
@@ -155,8 +163,12 @@ class ParagonIE_Sodium_Compat
      * @return void
      * @throws SodiumException
      */
-    public static function add(&$val, $addv)
-    {
+    public static function add(
+        #[\SensitiveParameter]
+        &$val,
+        #[\SensitiveParameter]
+        $addv
+    ) {
         $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
         $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
         if ($val_len !== $addv_len) {
@@ -181,8 +193,12 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function base642bin($encoded, $variant, $ignore = '')
-    {
+    public static function base642bin(
+        #[\SensitiveParameter]
+        $encoded,
+        $variant,
+        $ignore = ''
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1);
 
@@ -224,8 +240,11 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function bin2base64($decoded, $variant)
-    {
+    public static function bin2base64(
+        #[\SensitiveParameter]
+        $decoded,
+        $variant
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1);
         /** @var string $decoded */
@@ -257,8 +276,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function bin2hex($string)
-    {
+    public static function bin2hex(
+        #[\SensitiveParameter]
+        $string
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
 
@@ -284,8 +305,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function compare($left, $right)
-    {
+    public static function compare(
+        #[\SensitiveParameter]
+        $left,
+        #[\SensitiveParameter]
+        $right
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
@@ -299,6 +324,224 @@ class ParagonIE_Sodium_Compat
         return ParagonIE_Sodium_Core_Util::compare($left, $right);
     }
 
+    /**
+     * Authenticated Encryption with Associated Data: Decryption
+     *
+     * Algorithm:
+     *     AEGIS-128L
+     *
+     * @param string $ciphertext Encrypted message (with MAC appended)
+     * @param string $assocData  Authenticated Associated Data (unencrypted)
+     * @param string $nonce      Number to be used only Once; must be 32 bytes
+     * @param string $key        Encryption key
+     *
+     * @return string            The original plaintext message
+     * @throws SodiumException
+     * @throws TypeError
+     * @psalm-suppress MixedArgument
+     * @psalm-suppress MixedInferredReturnType
+     * @psalm-suppress MixedReturnStatement
+     */
+    public static function crypto_aead_aegis128l_decrypt(
+        $ciphertext = '',
+        $assocData = '',
+        $nonce = '',
+        #[\SensitiveParameter]
+        $key = ''
+    ) {
+        ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
+        ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
+        ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
+
+        /* Input validation: */
+        if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS128L_NPUBBYTES) {
+            throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS_128L_NPUBBYTES long');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS128L_KEYBYTES) {
+            throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long');
+        }
+        $ct_length = ParagonIE_Sodium_Core_Util::strlen($ciphertext);
+        if ($ct_length < self::CRYPTO_AEAD_AEGIS128L_ABYTES) {
+            throw new SodiumException('Message must be at least CRYPTO_AEAD_AEGIS128L_ABYTES long');
+        }
+
+        $ct = ParagonIE_Sodium_Core_Util::substr(
+            $ciphertext,
+            0,
+            $ct_length - self::CRYPTO_AEAD_AEGIS128L_ABYTES
+        );
+        $tag = ParagonIE_Sodium_Core_Util::substr(
+            $ciphertext,
+            $ct_length - self::CRYPTO_AEAD_AEGIS128L_ABYTES,
+            self::CRYPTO_AEAD_AEGIS128L_ABYTES
+        );
+        return ParagonIE_Sodium_Core_AEGIS128L::decrypt($ct, $tag, $assocData, $key, $nonce);
+    }
+
+    /**
+     * Authenticated Encryption with Associated Data: Encryption
+     *
+     * Algorithm:
+     *     AEGIS-128L
+     *
+     * @param string $plaintext Message to be encrypted
+     * @param string $assocData Authenticated Associated Data (unencrypted)
+     * @param string $nonce     Number to be used only Once; must be 32 bytes
+     * @param string $key       Encryption key
+     *
+     * @return string           Ciphertext with 32-byte authentication tag appended
+     * @throws SodiumException
+     * @throws TypeError
+     * @psalm-suppress MixedArgument
+     */
+    public static function crypto_aead_aegis128l_encrypt(
+        #[\SensitiveParameter]
+        $plaintext = '',
+        $assocData = '',
+        $nonce = '',
+        #[\SensitiveParameter]
+        $key = ''
+    ) {
+        ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
+        ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
+        ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
+
+        /* Input validation: */
+        if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS128L_NPUBBYTES) {
+            throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS128L_KEYBYTES) {
+            throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long');
+        }
+
+        list($ct, $tag) = ParagonIE_Sodium_Core_AEGIS128L::encrypt($plaintext, $assocData, $key, $nonce);
+        return $ct . $tag;
+    }
+
+    /**
+     * Return a secure random key for use with the AEGIS-128L
+     * symmetric AEAD interface.
+     *
+     * @return string
+     * @throws Exception
+     * @throws Error
+     */
+    public static function crypto_aead_aegis128l_keygen()
+    {
+        return random_bytes(self::CRYPTO_AEAD_AEGIS128L_KEYBYTES);
+    }
+
+    /**
+     * Authenticated Encryption with Associated Data: Decryption
+     *
+     * Algorithm:
+     *     AEGIS-256
+     *
+     * @param string $ciphertext Encrypted message (with MAC appended)
+     * @param string $assocData  Authenticated Associated Data (unencrypted)
+     * @param string $nonce      Number to be used only Once; must be 32 bytes
+     * @param string $key        Encryption key
+     *
+     * @return string            The original plaintext message
+     * @throws SodiumException
+     * @throws TypeError
+     * @psalm-suppress MixedArgument
+     * @psalm-suppress MixedInferredReturnType
+     * @psalm-suppress MixedReturnStatement
+     */
+    public static function crypto_aead_aegis256_decrypt(
+        $ciphertext = '',
+        $assocData = '',
+        $nonce = '',
+        #[\SensitiveParameter]
+        $key = ''
+    ) {
+        ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
+        ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
+        ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
+
+        /* Input validation: */
+        if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS256_NPUBBYTES) {
+            throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS256_NPUBBYTES long');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS256_KEYBYTES) {
+            throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS256_KEYBYTES long');
+        }
+        $ct_length = ParagonIE_Sodium_Core_Util::strlen($ciphertext);
+        if ($ct_length < self::CRYPTO_AEAD_AEGIS256_ABYTES) {
+            throw new SodiumException('Message must be at least CRYPTO_AEAD_AEGIS256_ABYTES long');
+        }
+
+        $ct = ParagonIE_Sodium_Core_Util::substr(
+            $ciphertext,
+            0,
+            $ct_length - self::CRYPTO_AEAD_AEGIS256_ABYTES
+        );
+        $tag = ParagonIE_Sodium_Core_Util::substr(
+            $ciphertext,
+            $ct_length - self::CRYPTO_AEAD_AEGIS256_ABYTES,
+            self::CRYPTO_AEAD_AEGIS256_ABYTES
+        );
+        return ParagonIE_Sodium_Core_AEGIS256::decrypt($ct, $tag, $assocData, $key, $nonce);
+    }
+
+    /**
+     * Authenticated Encryption with Associated Data: Encryption
+     *
+     * Algorithm:
+     *     AEGIS-256
+     *
+     * @param string $plaintext Message to be encrypted
+     * @param string $assocData Authenticated Associated Data (unencrypted)
+     * @param string $nonce Number to be used only Once; must be 32 bytes
+     * @param string $key Encryption key
+     *
+     * @return string           Ciphertext with 32-byte authentication tag appended
+     * @throws SodiumException
+     * @throws TypeError
+     * @psalm-suppress MixedArgument
+     */
+    public static function crypto_aead_aegis256_encrypt(
+        #[\SensitiveParameter]
+        $plaintext = '',
+        $assocData = '',
+        $nonce = '',
+        #[\SensitiveParameter]
+        $key = ''
+    ) {
+        ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
+        ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2);
+        ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3);
+        ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4);
+
+        /* Input validation: */
+        if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AEGIS256_NPUBBYTES) {
+            throw new SodiumException('Nonce must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long');
+        }
+        if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AEGIS256_KEYBYTES) {
+            throw new SodiumException('Key must be CRYPTO_AEAD_AEGIS128L_KEYBYTES long');
+        }
+
+        list($ct, $tag) = ParagonIE_Sodium_Core_AEGIS256::encrypt($plaintext, $assocData, $key, $nonce);
+        return $ct . $tag;
+    }
+
+    /**
+     * Return a secure random key for use with the AEGIS-256
+     * symmetric AEAD interface.
+     *
+     * @return string
+     * @throws Exception
+     * @throws Error
+     */
+    public static function crypto_aead_aegis256_keygen()
+    {
+        return random_bytes(self::CRYPTO_AEAD_AEGIS256_KEYBYTES);
+    }
+
     /**
      * Is AES-256-GCM even available to use?
      *
@@ -351,6 +594,7 @@ class ParagonIE_Sodium_Compat
         $ciphertext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = ''
     ) {
         if (!self::crypto_aead_aes256gcm_is_available()) {
@@ -408,9 +652,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedArgument
      */
     public static function crypto_aead_aes256gcm_encrypt(
+        #[\SensitiveParameter]
         $plaintext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = ''
     ) {
         if (!self::crypto_aead_aes256gcm_is_available()) {
@@ -484,6 +730,7 @@ class ParagonIE_Sodium_Compat
         $ciphertext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = ''
     ) {
         /* Type checks: */
@@ -561,9 +808,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedArgument
      */
     public static function crypto_aead_chacha20poly1305_encrypt(
+        #[\SensitiveParameter]
         $plaintext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = ''
     ) {
         /* Type checks: */
@@ -638,6 +887,7 @@ class ParagonIE_Sodium_Compat
         $ciphertext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = ''
     ) {
         /* Type checks: */
@@ -728,9 +978,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedArgument
      */
     public static function crypto_aead_chacha20poly1305_ietf_encrypt(
+        #[\SensitiveParameter]
         $plaintext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = ''
     ) {
         /* Type checks: */
@@ -819,6 +1071,7 @@ class ParagonIE_Sodium_Compat
         $ciphertext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = '',
         $dontFallback = false
     ) {
@@ -891,9 +1144,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedArgument
      */
     public static function crypto_aead_xchacha20poly1305_ietf_encrypt(
+        #[\SensitiveParameter]
         $plaintext = '',
         $assocData = '',
         $nonce = '',
+        #[\SensitiveParameter]
         $key = '',
         $dontFallback = false
     ) {
@@ -971,8 +1226,11 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_auth($message, $key)
-    {
+    public static function crypto_auth(
+        $message,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
@@ -1016,8 +1274,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_auth_verify($mac, $message, $key)
-    {
+    public static function crypto_auth_verify(
+        $mac,
+        $message,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($mac, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
@@ -1060,8 +1322,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_box($plaintext, $nonce, $keypair)
-    {
+    public static function crypto_box(
+        $plaintext,
+        $nonce,
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -1104,8 +1370,11 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_box_seal($plaintext, $publicKey)
-    {
+    public static function crypto_box_seal(
+        #[\SensitiveParameter]
+        $plaintext,
+        $publicKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
@@ -1142,8 +1411,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedInferredReturnType
      * @psalm-suppress MixedReturnStatement
      */
-    public static function crypto_box_seal_open($ciphertext, $keypair)
-    {
+    public static function crypto_box_seal_open(
+        $ciphertext,
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 2);
@@ -1205,8 +1477,11 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey)
-    {
+    public static function crypto_box_keypair_from_secretkey_and_publickey(
+        #[\SensitiveParameter]
+        $secretKey,
+        $publicKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
@@ -1244,8 +1519,12 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedInferredReturnType
      * @psalm-suppress MixedReturnStatement
      */
-    public static function crypto_box_open($ciphertext, $nonce, $keypair)
-    {
+    public static function crypto_box_open(
+        $ciphertext,
+        $nonce,
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -1287,8 +1566,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_box_publickey($keypair)
-    {
+    public static function crypto_box_publickey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
 
@@ -1318,8 +1599,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_box_publickey_from_secretkey($secretKey)
-    {
+    public static function crypto_box_publickey_from_secretkey(
+        #[\SensitiveParameter]
+        $secretKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
 
@@ -1349,8 +1632,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_box_secretkey($keypair)
-    {
+    public static function crypto_box_secretkey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
 
@@ -1381,8 +1666,10 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedArgument
      * @psalm-suppress UndefinedFunction
      */
-    public static function crypto_box_seed_keypair($seed)
-    {
+    public static function crypto_box_seed_keypair(
+        #[\SensitiveParameter]
+        $seed
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
 
@@ -1411,8 +1698,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_generichash($message, $key = '', $length = self::CRYPTO_GENERICHASH_BYTES)
-    {
+    public static function crypto_generichash(
+        $message,
+        #[\SensitiveParameter]
+        $key = '',
+        $length = self::CRYPTO_GENERICHASH_BYTES
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         if (is_null($key)) {
@@ -1455,8 +1746,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress ReferenceConstraintViolation
      * @psalm-suppress ConflictingReferenceConstraint
      */
-    public static function crypto_generichash_final(&$ctx, $length = self::CRYPTO_GENERICHASH_BYTES)
-    {
+    public static function crypto_generichash_final(
+        #[\SensitiveParameter]
+        &$ctx,
+        $length = self::CRYPTO_GENERICHASH_BYTES
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2);
@@ -1500,8 +1794,11 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_generichash_init($key = '', $length = self::CRYPTO_GENERICHASH_BYTES)
-    {
+    public static function crypto_generichash_init(
+        #[\SensitiveParameter]
+        $key = '',
+        $length = self::CRYPTO_GENERICHASH_BYTES
+    ) {
         /* Type checks: */
         if (is_null($key)) {
             $key = '';
@@ -1545,6 +1842,7 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedArgument
      */
     public static function crypto_generichash_init_salt_personal(
+        #[\SensitiveParameter]
         $key = '',
         $length = self::CRYPTO_GENERICHASH_BYTES,
         $salt = '',
@@ -1591,8 +1889,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedArgument
      * @psalm-suppress ReferenceConstraintViolation
      */
-    public static function crypto_generichash_update(&$ctx, $message)
-    {
+    public static function crypto_generichash_update(
+        #[\SensitiveParameter]
+        &$ctx,
+        $message
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2);
@@ -1635,6 +1936,7 @@ class ParagonIE_Sodium_Compat
         $subkey_len,
         $subkey_id,
         $context,
+        #[\SensitiveParameter]
         $key
     ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1);
@@ -1712,8 +2014,14 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_kx($my_secret, $their_public, $client_public, $server_public, $dontFallback = false)
-    {
+    public static function crypto_kx(
+        #[\SensitiveParameter]
+        $my_secret,
+        $their_public,
+        $client_public,
+        $server_public,
+        $dontFallback = false
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($my_secret, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($their_public, 'string', 2);
@@ -1774,8 +2082,10 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function crypto_kx_seed_keypair($seed)
-    {
+    public static function crypto_kx_seed_keypair(
+        #[\SensitiveParameter]
+        $seed
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
 
         $seed = (string) $seed;
@@ -1806,8 +2116,11 @@ class ParagonIE_Sodium_Compat
      * @return array{0: string, 1: string}
      * @throws SodiumException
      */
-    public static function crypto_kx_client_session_keys($keypair, $serverPublicKey)
-    {
+    public static function crypto_kx_client_session_keys(
+        #[\SensitiveParameter]
+        $keypair,
+        $serverPublicKey
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2);
 
@@ -1848,8 +2161,11 @@ class ParagonIE_Sodium_Compat
      * @return array{0: string, 1: string}
      * @throws SodiumException
      */
-    public static function crypto_kx_server_session_keys($keypair, $clientPublicKey)
-    {
+    public static function crypto_kx_server_session_keys(
+        #[\SensitiveParameter]
+        $keypair,
+        $clientPublicKey
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2);
 
@@ -1889,8 +2205,10 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function crypto_kx_secretkey($kp)
-    {
+    public static function crypto_kx_secretkey(
+        #[\SensitiveParameter]
+        $kp
+    ) {
         return ParagonIE_Sodium_Core_Util::substr(
             $kp,
             0,
@@ -1924,8 +2242,15 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg = null)
-    {
+    public static function crypto_pwhash(
+        $outlen,
+        #[\SensitiveParameter]
+        $passwd,
+        $salt,
+        $opslimit,
+        $memlimit,
+        $alg = null
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
         ParagonIE_Sodium_Core_Util::declareScalarType($salt,  'string', 3);
@@ -1976,8 +2301,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_pwhash_str($passwd, $opslimit, $memlimit)
-    {
+    public static function crypto_pwhash_str(
+        #[\SensitiveParameter]
+        $passwd,
+        $opslimit,
+        $memlimit
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
         ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
@@ -2003,8 +2332,12 @@ class ParagonIE_Sodium_Compat
      * @return bool
      * @throws SodiumException
      */
-    public static function crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit)
-    {
+    public static function crypto_pwhash_str_needs_rehash(
+        #[\SensitiveParameter]
+        $hash,
+        $opslimit,
+        $memlimit
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
         ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
@@ -2032,8 +2365,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_pwhash_str_verify($passwd, $hash)
-    {
+    public static function crypto_pwhash_str_verify(
+        #[\SensitiveParameter]
+        $passwd,
+        #[\SensitiveParameter]
+        $hash
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
 
@@ -2059,8 +2396,14 @@ class ParagonIE_Sodium_Compat
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function crypto_pwhash_scryptsalsa208sha256($outlen, $passwd, $salt, $opslimit, $memlimit)
-    {
+    public static function crypto_pwhash_scryptsalsa208sha256(
+        $outlen,
+        #[\SensitiveParameter]
+        $passwd,
+        $salt,
+        $opslimit,
+        $memlimit
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2);
         ParagonIE_Sodium_Core_Util::declareScalarType($salt,  'string', 3);
@@ -2119,8 +2462,12 @@ class ParagonIE_Sodium_Compat
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit)
-    {
+    public static function crypto_pwhash_scryptsalsa208sha256_str(
+        #[\SensitiveParameter]
+        $passwd,
+        $opslimit,
+        $memlimit
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2);
         ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3);
@@ -2153,8 +2500,12 @@ class ParagonIE_Sodium_Compat
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash)
-    {
+    public static function crypto_pwhash_scryptsalsa208sha256_str_verify(
+        #[\SensitiveParameter]
+        $passwd,
+        #[\SensitiveParameter]
+        $hash
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2);
 
@@ -2190,8 +2541,11 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_scalarmult($secretKey, $publicKey)
-    {
+    public static function crypto_scalarmult(
+        #[\SensitiveParameter]
+        $secretKey,
+        $publicKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
@@ -2234,8 +2588,10 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress TooFewArguments
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_scalarmult_base($secretKey)
-    {
+    public static function crypto_scalarmult_base(
+        #[\SensitiveParameter]
+        $secretKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
 
@@ -2272,8 +2628,13 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_secretbox($plaintext, $nonce, $key)
-    {
+    public static function crypto_secretbox(
+        #[\SensitiveParameter]
+        $plaintext,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -2312,8 +2673,12 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedInferredReturnType
      * @psalm-suppress MixedReturnStatement
      */
-    public static function crypto_secretbox_open($ciphertext, $nonce, $key)
-    {
+    public static function crypto_secretbox_open(
+        $ciphertext,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -2398,8 +2763,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
-    {
+    public static function crypto_secretbox_xchacha20poly1305_open(
+        $ciphertext,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -2425,8 +2794,10 @@ class ParagonIE_Sodium_Compat
      * @throws Exception
      * @throws SodiumException
      */
-    public static function crypto_secretstream_xchacha20poly1305_init_push($key)
-    {
+    public static function crypto_secretstream_xchacha20poly1305_init_push(
+        #[\SensitiveParameter]
+        $key
+    ) {
         if (PHP_INT_SIZE === 4) {
             return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key);
         }
@@ -2439,8 +2810,11 @@ class ParagonIE_Sodium_Compat
      * @return string Returns a state.
      * @throws Exception
      */
-    public static function crypto_secretstream_xchacha20poly1305_init_pull($header, $key)
-    {
+    public static function crypto_secretstream_xchacha20poly1305_init_pull(
+        $header,
+        #[\SensitiveParameter]
+        $key
+    ) {
         if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
             throw new SodiumException(
                 'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes'
@@ -2460,8 +2834,14 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function crypto_secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
-    {
+    public static function crypto_secretstream_xchacha20poly1305_push(
+        #[\SensitiveParameter]
+        &$state,
+        #[\SensitiveParameter]
+        $msg,
+        $aad = '',
+        $tag = 0
+    ) {
         if (PHP_INT_SIZE === 4) {
             return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push(
                 $state,
@@ -2485,8 +2865,12 @@ class ParagonIE_Sodium_Compat
      * @return bool|array{0: string, 1: int}
      * @throws SodiumException
      */
-    public static function crypto_secretstream_xchacha20poly1305_pull(&$state, $msg, $aad = '')
-    {
+    public static function crypto_secretstream_xchacha20poly1305_pull(
+        #[\SensitiveParameter]
+        &$state,
+        $msg,
+        $aad = ''
+    ) {
         if (PHP_INT_SIZE === 4) {
             return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull(
                 $state,
@@ -2515,8 +2899,10 @@ class ParagonIE_Sodium_Compat
      * @return void
      * @throws SodiumException
      */
-    public static function crypto_secretstream_xchacha20poly1305_rekey(&$state)
-    {
+    public static function crypto_secretstream_xchacha20poly1305_rekey(
+        #[\SensitiveParameter]
+        &$state
+    ) {
         if (PHP_INT_SIZE === 4) {
             ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state);
         } else {
@@ -2536,8 +2922,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedInferredReturnType
      * @psalm-suppress MixedReturnStatement
      */
-    public static function crypto_shorthash($message, $key)
-    {
+    public static function crypto_shorthash(
+        $message,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2);
@@ -2586,8 +2975,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedInferredReturnType
      * @psalm-suppress MixedReturnStatement
      */
-    public static function crypto_sign($message, $secretKey)
-    {
+    public static function crypto_sign(
+        $message,
+        #[\SensitiveParameter]
+        $secretKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
@@ -2622,8 +3014,10 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress MixedInferredReturnType
      * @psalm-suppress MixedReturnStatement
      */
-    public static function crypto_sign_open($signedMessage, $publicKey)
-    {
+    public static function crypto_sign_open(
+        $signedMessage,
+        $publicKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($signedMessage, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2);
@@ -2679,8 +3073,11 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk)
-    {
+    public static function crypto_sign_keypair_from_secretkey_and_publickey(
+        #[\SensitiveParameter]
+        $sk,
+        $pk
+    )  {
         ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1);
         $sk = (string) $sk;
@@ -2708,8 +3105,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_sign_seed_keypair($seed)
-    {
+    public static function crypto_sign_seed_keypair(
+        #[\SensitiveParameter]
+        $seed
+    ) {
         ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1);
 
         if (self::useNewSodiumAPI()) {
@@ -2737,8 +3136,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_sign_publickey($keypair)
-    {
+    public static function crypto_sign_publickey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
 
@@ -2768,8 +3169,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_sign_publickey_from_secretkey($secretKey)
-    {
+    public static function crypto_sign_publickey_from_secretkey(
+        #[\SensitiveParameter]
+        $secretKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1);
 
@@ -2799,8 +3202,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_sign_secretkey($keypair)
-    {
+    public static function crypto_sign_secretkey(
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1);
 
@@ -2833,8 +3238,11 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_sign_detached($message, $secretKey)
-    {
+    public static function crypto_sign_detached(
+        $message,
+        #[\SensitiveParameter]
+        $secretKey
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2);
@@ -2941,8 +3349,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_sign_ed25519_sk_to_curve25519($sk)
-    {
+    public static function crypto_sign_ed25519_sk_to_curve25519(
+        #[\SensitiveParameter]
+        $sk
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1);
 
@@ -2983,8 +3393,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_stream($len, $nonce, $key)
-    {
+    public static function crypto_stream(
+        $len,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -3030,8 +3444,13 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_stream_xor($message, $nonce, $key)
-    {
+    public static function crypto_stream_xor(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -3085,8 +3504,13 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_stream_xchacha20($len, $nonce, $key, $dontFallback = false)
-    {
+    public static function crypto_stream_xchacha20(
+        $len,
+        $nonce,
+        #[\SensitiveParameter]
+        $key,
+        $dontFallback = false
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -3130,8 +3554,14 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_stream_xchacha20_xor($message, $nonce, $key, $dontFallback = false)
-    {
+    public static function crypto_stream_xchacha20_xor(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        #[\SensitiveParameter]
+        $key,
+        $dontFallback = false
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -3176,8 +3606,15 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function crypto_stream_xchacha20_xor_ic($message, $nonce, $counter, $key, $dontFallback = false)
-    {
+    public static function crypto_stream_xchacha20_xor_ic(
+        #[\SensitiveParameter]
+        $message,
+        $nonce,
+        $counter,
+        #[\SensitiveParameter]
+        $key,
+        $dontFallback = false
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2);
@@ -3226,8 +3663,11 @@ class ParagonIE_Sodium_Compat
      * @psalm-suppress TooFewArguments
      * @psalm-suppress MixedArgument
      */
-    public static function hex2bin($string, $ignore = '')
-    {
+    public static function hex2bin(
+        #[\SensitiveParameter]
+        $string,
+        $ignore = ''
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($ignore, 'string', 2);
@@ -3253,8 +3693,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function increment(&$var)
-    {
+    public static function increment(
+        #[\SensitiveParameter]
+        &$var
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
 
@@ -3287,8 +3729,10 @@ class ParagonIE_Sodium_Compat
      *
      * @throws SodiumException
      */
-    public static function is_zero($str)
-    {
+    public static function is_zero(
+        #[\SensitiveParameter]
+        $str
+    ) {
         $d = 0;
         for ($i = 0; $i < 32; ++$i) {
             $d |= ParagonIE_Sodium_Core_Util::chrToInt($str[$i]);
@@ -3342,8 +3786,12 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress MixedArgument
      */
-    public static function memcmp($left, $right)
-    {
+    public static function memcmp(
+        #[\SensitiveParameter]
+        $left,
+        #[\SensitiveParameter]
+        $right
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2);
@@ -3371,8 +3819,10 @@ class ParagonIE_Sodium_Compat
      * @throws TypeError
      * @psalm-suppress TooFewArguments
      */
-    public static function memzero(&$var)
-    {
+    public static function memzero(
+        #[\SensitiveParameter]
+        &$var
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1);
 
@@ -3402,8 +3852,12 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function pad($unpadded, $blockSize, $dontFallback = false)
-    {
+    public static function pad(
+        #[\SensitiveParameter]
+        $unpadded,
+        $blockSize,
+        $dontFallback = false
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
@@ -3488,8 +3942,12 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function unpad($padded, $blockSize, $dontFallback = false)
-    {
+    public static function unpad(
+        #[\SensitiveParameter]
+        $padded,
+        $blockSize,
+        $dontFallback = false
+    ) {
         /* Type checks: */
         ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1);
         ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2);
@@ -3643,8 +4101,11 @@ class ParagonIE_Sodium_Compat
      * @return bool
      * @throws SodiumException
      */
-    public static function ristretto255_is_valid_point($p, $dontFallback = false)
-    {
+    public static function ristretto255_is_valid_point(
+        #[\SensitiveParameter]
+        $p,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_is_valid_point($p);
         }
@@ -3667,8 +4128,13 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_add($p, $q, $dontFallback = false)
-    {
+    public static function ristretto255_add(
+        #[\SensitiveParameter]
+        $p,
+        #[\SensitiveParameter]
+        $q,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_add($p, $q);
         }
@@ -3682,8 +4148,13 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_sub($p, $q, $dontFallback = false)
-    {
+    public static function ristretto255_sub(
+        #[\SensitiveParameter]
+        $p,
+        #[\SensitiveParameter]
+        $q,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_sub($p, $q);
         }
@@ -3697,8 +4168,11 @@ class ParagonIE_Sodium_Compat
      *
      * @throws SodiumException
      */
-    public static function ristretto255_from_hash($r, $dontFallback = false)
-    {
+    public static function ristretto255_from_hash(
+        #[\SensitiveParameter]
+        $r,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_from_hash($r);
         }
@@ -3739,8 +4213,11 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_scalar_invert($s, $dontFallback = false)
-    {
+    public static function ristretto255_scalar_invert(
+        #[\SensitiveParameter]
+        $s,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_scalar_invert($s);
         }
@@ -3752,8 +4229,11 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_scalar_negate($s, $dontFallback = false)
-    {
+    public static function ristretto255_scalar_negate(
+        #[\SensitiveParameter]
+        $s,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_scalar_negate($s);
         }
@@ -3766,8 +4246,11 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_scalar_complement($s, $dontFallback = false)
-    {
+    public static function ristretto255_scalar_complement(
+        #[\SensitiveParameter]
+        $s,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_scalar_complement($s);
         }
@@ -3781,8 +4264,13 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_scalar_add($x, $y, $dontFallback = false)
-    {
+    public static function ristretto255_scalar_add(
+        #[\SensitiveParameter]
+        $x,
+        #[\SensitiveParameter]
+        $y,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_scalar_add($x, $y);
         }
@@ -3796,8 +4284,13 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_scalar_sub($x, $y, $dontFallback = false)
-    {
+    public static function ristretto255_scalar_sub(
+        #[\SensitiveParameter]
+        $x,
+        #[\SensitiveParameter]
+        $y,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_scalar_sub($x, $y);
         }
@@ -3811,8 +4304,13 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_scalar_mul($x, $y, $dontFallback = false)
-    {
+    public static function ristretto255_scalar_mul(
+        #[\SensitiveParameter]
+        $x,
+        #[\SensitiveParameter]
+        $y,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_scalar_mul($x, $y);
         }
@@ -3826,8 +4324,13 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function scalarmult_ristretto255($n, $p, $dontFallback = false)
-    {
+    public static function scalarmult_ristretto255(
+        #[\SensitiveParameter]
+        $n,
+        #[\SensitiveParameter]
+        $p,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_scalarmult_ristretto255($n, $p);
         }
@@ -3841,8 +4344,11 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function scalarmult_ristretto255_base($n, $dontFallback = false)
-    {
+    public static function scalarmult_ristretto255_base(
+        #[\SensitiveParameter]
+        $n,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_scalarmult_ristretto255_base($n);
         }
@@ -3855,8 +4361,11 @@ class ParagonIE_Sodium_Compat
      * @return string
      * @throws SodiumException
      */
-    public static function ristretto255_scalar_reduce($s, $dontFallback = false)
-    {
+    public static function ristretto255_scalar_reduce(
+        #[\SensitiveParameter]
+        $s,
+        $dontFallback = false
+    ) {
         if (self::useNewSodiumAPI() && !$dontFallback) {
             return sodium_crypto_core_ristretto255_scalar_reduce($s);
         }
@@ -3910,8 +4419,12 @@ class ParagonIE_Sodium_Compat
      * @return void
      * @throws SodiumException
      */
-    public static function sub(&$val, $addv)
-    {
+    public static function sub(
+        #[\SensitiveParameter]
+        &$val,
+        #[\SensitiveParameter]
+        $addv
+    ) {
         $val_len = ParagonIE_Sodium_Core_Util::strlen($val);
         $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv);
         if ($val_len !== $addv_len) {
diff --git a/vendor/paragonie/sodium_compat/src/Core/AEGIS/State128L.php b/vendor/paragonie/sodium_compat/src/Core/AEGIS/State128L.php
new file mode 100644
index 0000000..9decd2b
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AEGIS/State128L.php
@@ -0,0 +1,284 @@
+<?php
+
+if (class_exists('ParagonIE_Sodium_Core_AEGIS_State128L', false)) {
+    return;
+}
+
+if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
+    define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
+}
+if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
+    define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
+}
+
+class ParagonIE_Sodium_Core_AEGIS_State128L
+{
+    /** @var array<int, string> $state */
+    protected $state;
+    public function __construct()
+    {
+        $this->state = array_fill(0, 8, '');
+    }
+
+    /**
+     * @internal Only use this for unit tests!
+     * @return string[]
+     */
+    public function getState()
+    {
+        return array_values($this->state);
+    }
+
+    /**
+     * @param array $input
+     * @return self
+     * @throws SodiumException
+     *
+     * @internal Only for unit tests
+     */
+    public static function initForUnitTests(array $input)
+    {
+        if (count($input) < 8) {
+            throw new SodiumException('invalid input');
+        }
+        $state = new self();
+        for ($i = 0; $i < 8; ++$i) {
+            $state->state[$i] = $input[$i];
+        }
+        return $state;
+    }
+
+    /**
+     * @param string $key
+     * @param string $nonce
+     * @return self
+     */
+    public static function init($key, $nonce)
+    {
+        $state = new self();
+
+        // S0 = key ^ nonce
+        $state->state[0] = $key ^ $nonce;
+        // S1 = C1
+        $state->state[1] = SODIUM_COMPAT_AEGIS_C1;
+        // S2 = C0
+        $state->state[2] = SODIUM_COMPAT_AEGIS_C0;
+        // S3 = C1
+        $state->state[3] = SODIUM_COMPAT_AEGIS_C1;
+        // S4 = key ^ nonce
+        $state->state[4] = $key ^ $nonce;
+        // S5 = key ^ C0
+        $state->state[5] = $key ^ SODIUM_COMPAT_AEGIS_C0;
+        // S6 = key ^ C1
+        $state->state[6] = $key ^ SODIUM_COMPAT_AEGIS_C1;
+        // S7 = key ^ C0
+        $state->state[7] = $key ^ SODIUM_COMPAT_AEGIS_C0;
+
+        // Repeat(10, Update(nonce, key))
+        for ($i = 0; $i < 10; ++$i) {
+            $state->update($nonce, $key);
+        }
+        return $state;
+    }
+
+    /**
+     * @param string $ai
+     * @return self
+     */
+    public function absorb($ai)
+    {
+        if (ParagonIE_Sodium_Core_Util::strlen($ai) !== 32) {
+            throw new SodiumException('Input must be two AES blocks in size');
+        }
+        $t0 = ParagonIE_Sodium_Core_Util::substr($ai, 0, 16);
+        $t1 = ParagonIE_Sodium_Core_Util::substr($ai, 16, 16);
+        return $this->update($t0, $t1);
+    }
+
+
+    /**
+     * @param string $ci
+     * @return string
+     * @throws SodiumException
+     */
+    public function dec($ci)
+    {
+        if (ParagonIE_Sodium_Core_Util::strlen($ci) !== 32) {
+            throw new SodiumException('Input must be two AES blocks in size');
+        }
+
+        // z0 = S6 ^ S1 ^ (S2 & S3)
+        $z0 = $this->state[6]
+            ^ $this->state[1]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
+        // z1 = S2 ^ S5 ^ (S6 & S7)
+        $z1 = $this->state[2]
+            ^ $this->state[5]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]);
+
+        // t0, t1 = Split(xi, 128)
+        $t0 = ParagonIE_Sodium_Core_Util::substr($ci, 0, 16);
+        $t1 = ParagonIE_Sodium_Core_Util::substr($ci, 16, 16);
+
+        // out0 = t0 ^ z0
+        // out1 = t1 ^ z1
+        $out0 = $t0 ^ $z0;
+        $out1 = $t1 ^ $z1;
+
+        // Update(out0, out1)
+        // xi = out0 || out1
+        $this->update($out0, $out1);
+        return $out0 . $out1;
+    }
+
+    /**
+     * @param string $cn
+     * @return string
+     */
+    public function decPartial($cn)
+    {
+        $len = ParagonIE_Sodium_Core_Util::strlen($cn);
+
+        // z0 = S6 ^ S1 ^ (S2 & S3)
+        $z0 = $this->state[6]
+            ^ $this->state[1]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
+        // z1 = S2 ^ S5 ^ (S6 & S7)
+        $z1 = $this->state[2]
+            ^ $this->state[5]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]);
+
+        // t0, t1 = Split(ZeroPad(cn, 256), 128)
+        $cn = str_pad($cn, 32, "\0", STR_PAD_RIGHT);
+        $t0 = ParagonIE_Sodium_Core_Util::substr($cn, 0, 16);
+        $t1 = ParagonIE_Sodium_Core_Util::substr($cn, 16, 16);
+        // out0 = t0 ^ z0
+        // out1 = t1 ^ z1
+        $out0 = $t0 ^ $z0;
+        $out1 = $t1 ^ $z1;
+
+        // xn = Truncate(out0 || out1, |cn|)
+        $xn = ParagonIE_Sodium_Core_Util::substr($out0 . $out1, 0, $len);
+
+        // v0, v1 = Split(ZeroPad(xn, 256), 128)
+        $padded = str_pad($xn, 32, "\0", STR_PAD_RIGHT);
+        $v0 = ParagonIE_Sodium_Core_Util::substr($padded, 0, 16);
+        $v1 = ParagonIE_Sodium_Core_Util::substr($padded, 16, 16);
+        // Update(v0, v1)
+        $this->update($v0, $v1);
+
+        // return xn
+        return $xn;
+    }
+
+    /**
+     * @param string $xi
+     * @return string
+     * @throws SodiumException
+     */
+    public function enc($xi)
+    {
+        if (ParagonIE_Sodium_Core_Util::strlen($xi) !== 32) {
+            throw new SodiumException('Input must be two AES blocks in size');
+        }
+
+        // z0 = S6 ^ S1 ^ (S2 & S3)
+        $z0 = $this->state[6]
+            ^ $this->state[1]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
+        // z1 = S2 ^ S5 ^ (S6 & S7)
+        $z1 = $this->state[2]
+            ^ $this->state[5]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[6], $this->state[7]);
+
+        // t0, t1 = Split(xi, 128)
+        $t0 = ParagonIE_Sodium_Core_Util::substr($xi, 0, 16);
+        $t1 = ParagonIE_Sodium_Core_Util::substr($xi, 16, 16);
+
+        // out0 = t0 ^ z0
+        // out1 = t1 ^ z1
+        $out0 = $t0 ^ $z0;
+        $out1 = $t1 ^ $z1;
+
+        // Update(t0, t1)
+        // ci = out0 || out1
+        $this->update($t0, $t1);
+
+        // return ci
+        return $out0 . $out1;
+    }
+
+    /**
+     * @param int $ad_len_bits
+     * @param int $msg_len_bits
+     * @return string
+     */
+    public function finalize($ad_len_bits, $msg_len_bits)
+    {
+        $encoded = ParagonIE_Sodium_Core_Util::store64_le($ad_len_bits) .
+            ParagonIE_Sodium_Core_Util::store64_le($msg_len_bits);
+        $t = $this->state[2] ^ $encoded;
+        for ($i = 0; $i < 7; ++$i) {
+            $this->update($t, $t);
+        }
+        return ($this->state[0] ^ $this->state[1] ^ $this->state[2] ^ $this->state[3]) .
+            ($this->state[4] ^ $this->state[5] ^ $this->state[6] ^ $this->state[7]);
+    }
+
+    /**
+     * @param string $m0
+     * @param string $m1
+     * @return self
+     */
+    public function update($m0, $m1)
+    {
+        /*
+           S'0 = AESRound(S7, S0 ^ M0)
+           S'1 = AESRound(S0, S1)
+           S'2 = AESRound(S1, S2)
+           S'3 = AESRound(S2, S3)
+           S'4 = AESRound(S3, S4 ^ M1)
+           S'5 = AESRound(S4, S5)
+           S'6 = AESRound(S5, S6)
+           S'7 = AESRound(S6, S7)
+         */
+        list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
+            $this->state[7], $this->state[0] ^ $m0,
+            $this->state[0], $this->state[1]
+        );
+
+        list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
+            $this->state[1], $this->state[2],
+            $this->state[2], $this->state[3]
+        );
+
+        list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
+            $this->state[3], $this->state[4] ^ $m1,
+            $this->state[4], $this->state[5]
+        );
+        list($s_6, $s_7) = ParagonIE_Sodium_Core_AES::doubleRound(
+            $this->state[5], $this->state[6],
+            $this->state[6], $this->state[7]
+        );
+
+        /*
+           S0  = S'0
+           S1  = S'1
+           S2  = S'2
+           S3  = S'3
+           S4  = S'4
+           S5  = S'5
+           S6  = S'6
+           S7  = S'7
+         */
+        $this->state[0] = $s_0;
+        $this->state[1] = $s_1;
+        $this->state[2] = $s_2;
+        $this->state[3] = $s_3;
+        $this->state[4] = $s_4;
+        $this->state[5] = $s_5;
+        $this->state[6] = $s_6;
+        $this->state[7] = $s_7;
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/vendor/paragonie/sodium_compat/src/Core/AEGIS/State256.php b/vendor/paragonie/sodium_compat/src/Core/AEGIS/State256.php
new file mode 100644
index 0000000..6f88b82
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AEGIS/State256.php
@@ -0,0 +1,240 @@
+<?php
+
+if (class_exists('ParagonIE_Sodium_Core_AEGIS_State256', false)) {
+    return;
+}
+
+if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
+    define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
+}
+if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
+    define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
+}
+
+class ParagonIE_Sodium_Core_AEGIS_State256
+{
+    /** @var array<int, string> $state */
+    protected $state;
+    public function __construct()
+    {
+        $this->state = array_fill(0, 6, '');
+    }
+
+    /**
+     * @internal Only use this for unit tests!
+     * @return string[]
+     */
+    public function getState()
+    {
+        return array_values($this->state);
+    }
+
+    /**
+     * @param array $input
+     * @return self
+     * @throws SodiumException
+     *
+     * @internal Only for unit tests
+     */
+    public static function initForUnitTests(array $input)
+    {
+        if (count($input) < 6) {
+            throw new SodiumException('invalid input');
+        }
+        $state = new self();
+        for ($i = 0; $i < 6; ++$i) {
+            $state->state[$i] = $input[$i];
+        }
+        return $state;
+    }
+
+    /**
+     * @param string $key
+     * @param string $nonce
+     * @return self
+     */
+    public static function init($key, $nonce)
+    {
+        $state = new self();
+        $k0 = ParagonIE_Sodium_Core_Util::substr($key, 0, 16);
+        $k1 = ParagonIE_Sodium_Core_Util::substr($key, 16, 16);
+        $n0 = ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16);
+        $n1 = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 16);
+
+        // S0 = k0 ^ n0
+        // S1 = k1 ^ n1
+        // S2 = C1
+        // S3 = C0
+        // S4 = k0 ^ C0
+        // S5 = k1 ^ C1
+        $k0_n0 = $k0 ^ $n0;
+        $k1_n1 = $k1 ^ $n1;
+        $state->state[0] = $k0_n0;
+        $state->state[1] = $k1_n1;
+        $state->state[2] = SODIUM_COMPAT_AEGIS_C1;
+        $state->state[3] = SODIUM_COMPAT_AEGIS_C0;
+        $state->state[4] = $k0 ^ SODIUM_COMPAT_AEGIS_C0;
+        $state->state[5] = $k1 ^ SODIUM_COMPAT_AEGIS_C1;
+
+        // Repeat(4,
+        //   Update(k0)
+        //   Update(k1)
+        //   Update(k0 ^ n0)
+        //   Update(k1 ^ n1)
+        // )
+        for ($i = 0; $i < 4; ++$i) {
+            $state->update($k0);
+            $state->update($k1);
+            $state->update($k0 ^ $n0);
+            $state->update($k1 ^ $n1);
+        }
+        return $state;
+    }
+
+    /**
+     * @param string $ai
+     * @return self
+     * @throws SodiumException
+     */
+    public function absorb($ai)
+    {
+        if (ParagonIE_Sodium_Core_Util::strlen($ai) !== 16) {
+            throw new SodiumException('Input must be an AES block in size');
+        }
+        return $this->update($ai);
+    }
+
+    /**
+     * @param string $ci
+     * @return string
+     * @throws SodiumException
+     */
+    public function dec($ci)
+    {
+        if (ParagonIE_Sodium_Core_Util::strlen($ci) !== 16) {
+            throw new SodiumException('Input must be an AES block in size');
+        }
+        // z = S1 ^ S4 ^ S5 ^ (S2 & S3)
+        $z = $this->state[1]
+            ^ $this->state[4]
+            ^ $this->state[5]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
+        $xi = $ci ^ $z;
+        $this->update($xi);
+        return $xi;
+    }
+
+    /**
+     * @param string $cn
+     * @return string
+     */
+    public function decPartial($cn)
+    {
+        $len = ParagonIE_Sodium_Core_Util::strlen($cn);
+        // z = S1 ^ S4 ^ S5 ^ (S2 & S3)
+        $z = $this->state[1]
+            ^ $this->state[4]
+            ^ $this->state[5]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
+
+        // t = ZeroPad(cn, 128)
+        $t = str_pad($cn, 16, "\0", STR_PAD_RIGHT);
+
+        // out = t ^ z
+        $out = $t ^ $z;
+
+        // xn = Truncate(out, |cn|)
+        $xn = ParagonIE_Sodium_Core_Util::substr($out, 0, $len);
+
+        // v = ZeroPad(xn, 128)
+        $v = str_pad($xn, 16, "\0", STR_PAD_RIGHT);
+        // Update(v)
+        $this->update($v);
+
+        // return xn
+        return $xn;
+    }
+
+    /**
+     * @param string $xi
+     * @return string
+     * @throws SodiumException
+     */
+    public function enc($xi)
+    {
+        if (ParagonIE_Sodium_Core_Util::strlen($xi) !== 16) {
+            throw new SodiumException('Input must be an AES block in size');
+        }
+        // z = S1 ^ S4 ^ S5 ^ (S2 & S3)
+        $z = $this->state[1]
+            ^ $this->state[4]
+            ^ $this->state[5]
+            ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
+        $this->update($xi);
+        return $xi ^ $z;
+    }
+
+    /**
+     * @param int $ad_len_bits
+     * @param int $msg_len_bits
+     * @return string
+     */
+    public function finalize($ad_len_bits, $msg_len_bits)
+    {
+        $encoded = ParagonIE_Sodium_Core_Util::store64_le($ad_len_bits) .
+            ParagonIE_Sodium_Core_Util::store64_le($msg_len_bits);
+        $t = $this->state[3] ^ $encoded;
+
+        for ($i = 0; $i < 7; ++$i) {
+            $this->update($t);
+        }
+
+        return ($this->state[0] ^ $this->state[1] ^ $this->state[2]) .
+            ($this->state[3] ^ $this->state[4] ^ $this->state[5]);
+    }
+
+    /**
+     * @param string $m
+     * @return self
+     */
+    public function update($m)
+    {
+        /*
+            S'0 = AESRound(S5, S0 ^ M)
+            S'1 = AESRound(S0, S1)
+            S'2 = AESRound(S1, S2)
+            S'3 = AESRound(S2, S3)
+            S'4 = AESRound(S3, S4)
+            S'5 = AESRound(S4, S5)
+         */
+        list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
+            $this->state[5],$this->state[0] ^ $m,
+            $this->state[0], $this->state[1]
+        );
+
+        list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
+            $this->state[1], $this->state[2],
+            $this->state[2], $this->state[3]
+        );
+        list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
+            $this->state[3], $this->state[4],
+            $this->state[4], $this->state[5]
+        );
+
+        /*
+            S0  = S'0
+            S1  = S'1
+            S2  = S'2
+            S3  = S'3
+            S4  = S'4
+            S5  = S'5
+         */
+        $this->state[0] = $s_0;
+        $this->state[1] = $s_1;
+        $this->state[2] = $s_2;
+        $this->state[3] = $s_3;
+        $this->state[4] = $s_4;
+        $this->state[5] = $s_5;
+        return $this;
+    }
+}
diff --git a/vendor/paragonie/sodium_compat/src/Core/AEGIS128L.php b/vendor/paragonie/sodium_compat/src/Core/AEGIS128L.php
new file mode 100644
index 0000000..ad1e85d
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AEGIS128L.php
@@ -0,0 +1,119 @@
+<?php
+
+if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
+    define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
+}
+if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
+    define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
+}
+
+class ParagonIE_Sodium_Core_AEGIS128L extends ParagonIE_Sodium_Core_AES
+{
+    /**
+     * @param string $ct
+     * @param string $tag
+     * @param string $ad
+     * @param string $key
+     * @param string $nonce
+     * @return string
+     * @throws SodiumException
+     */
+    public static function decrypt($ct, $tag, $ad, $key, $nonce)
+    {
+        $state = self::init($key, $nonce);
+        $ad_blocks = (self::strlen($ad) + 31) >> 5;
+        for ($i = 0; $i < $ad_blocks; ++$i) {
+            $ai = self::substr($ad, $i << 5, 32);
+            if (self::strlen($ai) < 32) {
+                $ai = str_pad($ai, 32, "\0", STR_PAD_RIGHT);
+            }
+            $state->absorb($ai);
+        }
+
+        $msg = '';
+        $cn = self::strlen($ct) & 31;
+        $ct_blocks = self::strlen($ct) >> 5;
+        for ($i = 0; $i < $ct_blocks; ++$i) {
+            $msg .= $state->dec(self::substr($ct, $i << 5, 32));
+        }
+        if ($cn) {
+            $start = $ct_blocks << 5;
+            $msg .= $state->decPartial(self::substr($ct, $start, $cn));
+        }
+        $expected_tag = $state->finalize(
+            self::strlen($ad) << 3,
+            self::strlen($msg) << 3
+        );
+        if (!self::hashEquals($expected_tag, $tag)) {
+            try {
+                // The RFC says to erase msg, so we shall try:
+                ParagonIE_Sodium_Compat::memzero($msg);
+            } catch (SodiumException $ex) {
+                // Do nothing if we cannot memzero
+            }
+            throw new SodiumException('verification failed');
+        }
+        return $msg;
+    }
+
+    /**
+     * @param string $msg
+     * @param string $ad
+     * @param string $key
+     * @param string $nonce
+     * @return array
+     *
+     * @throws SodiumException
+     */
+    public static function encrypt($msg, $ad, $key, $nonce)
+    {
+        $state = self::init($key, $nonce);
+        // ad_blocks = Split(ZeroPad(ad, 256), 256)
+        // for ai in ad_blocks:
+        //     Absorb(ai)
+        $ad_len = self::strlen($ad);
+        $msg_len = self::strlen($msg);
+        $ad_blocks = ($ad_len + 31) >> 5;
+        for ($i = 0; $i < $ad_blocks; ++$i) {
+            $ai = self::substr($ad, $i << 5, 32);
+            if (self::strlen($ai) < 32) {
+                $ai = str_pad($ai, 32, "\0", STR_PAD_RIGHT);
+            }
+            $state->absorb($ai);
+        }
+
+        // msg_blocks = Split(ZeroPad(msg, 256), 256)
+        // for xi in msg_blocks:
+        //     ct = ct || Enc(xi)
+        $ct = '';
+        $msg_blocks = ($msg_len + 31) >> 5;
+        for ($i = 0; $i < $msg_blocks; ++$i) {
+            $xi = self::substr($msg, $i << 5, 32);
+            if (self::strlen($xi) < 32) {
+                $xi = str_pad($xi, 32, "\0", STR_PAD_RIGHT);
+            }
+            $ct .= $state->enc($xi);
+        }
+        // tag = Finalize(|ad|, |msg|)
+        // ct = Truncate(ct, |msg|)
+        $tag = $state->finalize(
+            $ad_len << 3,
+            $msg_len << 3
+        );
+        // return ct and tag
+        return array(
+            self::substr($ct, 0, $msg_len),
+            $tag
+        );
+    }
+
+    /**
+     * @param string $key
+     * @param string $nonce
+     * @return ParagonIE_Sodium_Core_AEGIS_State128L
+     */
+    public static function init($key, $nonce)
+    {
+        return ParagonIE_Sodium_Core_AEGIS_State128L::init($key, $nonce);
+    }
+}
diff --git a/vendor/paragonie/sodium_compat/src/Core/AEGIS256.php b/vendor/paragonie/sodium_compat/src/Core/AEGIS256.php
new file mode 100644
index 0000000..605bbca
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AEGIS256.php
@@ -0,0 +1,118 @@
+<?php
+
+if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
+    define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
+}
+if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
+    define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
+}
+
+class ParagonIE_Sodium_Core_AEGIS256 extends ParagonIE_Sodium_Core_AES
+{
+    /**
+     * @param string $ct
+     * @param string $tag
+     * @param string $ad
+     * @param string $key
+     * @param string $nonce
+     * @return string
+     * @throws SodiumException
+     */
+    public static function decrypt($ct, $tag, $ad, $key, $nonce)
+    {
+        $state = self::init($key, $nonce);
+
+        // ad_blocks = Split(ZeroPad(ad, 128), 128)
+        $ad_blocks = (self::strlen($ad) + 15) >> 4;
+        // for ai in ad_blocks:
+        //     Absorb(ai)
+        for ($i = 0; $i < $ad_blocks; ++$i) {
+            $ai = self::substr($ad, $i << 4, 16);
+            if (self::strlen($ai) < 16) {
+                $ai = str_pad($ai, 16, "\0", STR_PAD_RIGHT);
+            }
+            $state->absorb($ai);
+        }
+
+        $msg = '';
+        $cn = self::strlen($ct) & 15;
+        $ct_blocks = self::strlen($ct) >> 4;
+        // ct_blocks = Split(ZeroPad(ct, 128), 128)
+        // cn = Tail(ct, |ct| mod 128)
+        for ($i = 0; $i < $ct_blocks; ++$i) {
+            $msg .= $state->dec(self::substr($ct, $i << 4, 16));
+        }
+        // if cn is not empty:
+        //   msg = msg || DecPartial(cn)
+        if ($cn) {
+            $start = $ct_blocks << 4;
+            $msg .= $state->decPartial(self::substr($ct, $start, $cn));
+        }
+        $expected_tag = $state->finalize(
+            self::strlen($ad) << 3,
+            self::strlen($msg) << 3
+        );
+        if (!self::hashEquals($expected_tag, $tag)) {
+            try {
+                // The RFC says to erase msg, so we shall try:
+                ParagonIE_Sodium_Compat::memzero($msg);
+            } catch (SodiumException $ex) {
+                // Do nothing if we cannot memzero
+            }
+            throw new SodiumException('verification failed');
+        }
+        return $msg;
+    }
+
+    /**
+     * @param string $msg
+     * @param string $ad
+     * @param string $key
+     * @param string $nonce
+     * @return array
+     * @throws SodiumException
+     */
+    public static function encrypt($msg, $ad, $key, $nonce)
+    {
+        $state = self::init($key, $nonce);
+        $ad_len = self::strlen($ad);
+        $msg_len = self::strlen($msg);
+        $ad_blocks = ($ad_len + 15) >> 4;
+        for ($i = 0; $i < $ad_blocks; ++$i) {
+            $ai = self::substr($ad, $i << 4, 16);
+            if (self::strlen($ai) < 16) {
+                $ai = str_pad($ai, 16, "\0", STR_PAD_RIGHT);
+            }
+            $state->absorb($ai);
+        }
+
+        $ct = '';
+        $msg_blocks = ($msg_len + 15) >> 4;
+        for ($i = 0; $i < $msg_blocks; ++$i) {
+            $xi = self::substr($msg, $i << 4, 16);
+            if (self::strlen($xi) < 16) {
+                $xi = str_pad($xi, 16, "\0", STR_PAD_RIGHT);
+            }
+            $ct .= $state->enc($xi);
+        }
+        $tag = $state->finalize(
+            $ad_len << 3,
+            $msg_len << 3
+        );
+        return array(
+            self::substr($ct, 0, $msg_len),
+            $tag
+        );
+
+    }
+
+    /**
+     * @param string $key
+     * @param string $nonce
+     * @return ParagonIE_Sodium_Core_AEGIS_State256
+     */
+    public static function init($key, $nonce)
+    {
+        return ParagonIE_Sodium_Core_AEGIS_State256::init($key, $nonce);
+    }
+}
diff --git a/vendor/paragonie/sodium_compat/src/Core/AES.php b/vendor/paragonie/sodium_compat/src/Core/AES.php
new file mode 100644
index 0000000..d86cff1
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AES.php
@@ -0,0 +1,518 @@
+<?php
+
+if (class_exists('ParagonIE_Sodium_Core_AES', false)) {
+    return;
+}
+
+/**
+ * Bitsliced implementation of the AES block cipher.
+ *
+ * Based on the implementation provided by BearSSL.
+ *
+ * @internal This should only be used by sodium_compat
+ */
+class ParagonIE_Sodium_Core_AES extends ParagonIE_Sodium_Core_Util
+{
+    /**
+     * @var int[] AES round constants
+     */
+    private static $Rcon = array(
+        0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+    );
+
+    /**
+     * Mutates the values of $q!
+     *
+     * @param ParagonIE_Sodium_Core_AES_Block $q
+     * @return void
+     */
+    public static function sbox(ParagonIE_Sodium_Core_AES_Block $q)
+    {
+        /**
+         * @var int $x0
+         * @var int $x1
+         * @var int $x2
+         * @var int $x3
+         * @var int $x4
+         * @var int $x5
+         * @var int $x6
+         * @var int $x7
+         */
+        $x0 = $q[7] & self::U32_MAX;
+        $x1 = $q[6] & self::U32_MAX;
+        $x2 = $q[5] & self::U32_MAX;
+        $x3 = $q[4] & self::U32_MAX;
+        $x4 = $q[3] & self::U32_MAX;
+        $x5 = $q[2] & self::U32_MAX;
+        $x6 = $q[1] & self::U32_MAX;
+        $x7 = $q[0] & self::U32_MAX;
+
+        $y14 = $x3 ^ $x5;
+        $y13 = $x0 ^ $x6;
+        $y9 = $x0 ^ $x3;
+        $y8 = $x0 ^ $x5;
+        $t0 = $x1 ^ $x2;
+        $y1 = $t0 ^ $x7;
+        $y4 = $y1 ^ $x3;
+        $y12 = $y13 ^ $y14;
+        $y2 = $y1 ^ $x0;
+        $y5 = $y1 ^ $x6;
+        $y3 = $y5 ^ $y8;
+        $t1 = $x4 ^ $y12;
+        $y15 = $t1 ^ $x5;
+        $y20 = $t1 ^ $x1;
+        $y6 = $y15 ^ $x7;
+        $y10 = $y15 ^ $t0;
+        $y11 = $y20 ^ $y9;
+        $y7 = $x7 ^ $y11;
+        $y17 = $y10 ^ $y11;
+        $y19 = $y10 ^ $y8;
+        $y16 = $t0 ^ $y11;
+        $y21 = $y13 ^ $y16;
+        $y18 = $x0 ^ $y16;
+
+        /*
+         * Non-linear section.
+         */
+        $t2 = $y12 & $y15;
+        $t3 = $y3 & $y6;
+        $t4 = $t3 ^ $t2;
+        $t5 = $y4 & $x7;
+        $t6 = $t5 ^ $t2;
+        $t7 = $y13 & $y16;
+        $t8 = $y5 & $y1;
+        $t9 = $t8 ^ $t7;
+        $t10 = $y2 & $y7;
+        $t11 = $t10 ^ $t7;
+        $t12 = $y9 & $y11;
+        $t13 = $y14 & $y17;
+        $t14 = $t13 ^ $t12;
+        $t15 = $y8 & $y10;
+        $t16 = $t15 ^ $t12;
+        $t17 = $t4 ^ $t14;
+        $t18 = $t6 ^ $t16;
+        $t19 = $t9 ^ $t14;
+        $t20 = $t11 ^ $t16;
+        $t21 = $t17 ^ $y20;
+        $t22 = $t18 ^ $y19;
+        $t23 = $t19 ^ $y21;
+        $t24 = $t20 ^ $y18;
+
+        $t25 = $t21 ^ $t22;
+        $t26 = $t21 & $t23;
+        $t27 = $t24 ^ $t26;
+        $t28 = $t25 & $t27;
+        $t29 = $t28 ^ $t22;
+        $t30 = $t23 ^ $t24;
+        $t31 = $t22 ^ $t26;
+        $t32 = $t31 & $t30;
+        $t33 = $t32 ^ $t24;
+        $t34 = $t23 ^ $t33;
+        $t35 = $t27 ^ $t33;
+        $t36 = $t24 & $t35;
+        $t37 = $t36 ^ $t34;
+        $t38 = $t27 ^ $t36;
+        $t39 = $t29 & $t38;
+        $t40 = $t25 ^ $t39;
+
+        $t41 = $t40 ^ $t37;
+        $t42 = $t29 ^ $t33;
+        $t43 = $t29 ^ $t40;
+        $t44 = $t33 ^ $t37;
+        $t45 = $t42 ^ $t41;
+        $z0 = $t44 & $y15;
+        $z1 = $t37 & $y6;
+        $z2 = $t33 & $x7;
+        $z3 = $t43 & $y16;
+        $z4 = $t40 & $y1;
+        $z5 = $t29 & $y7;
+        $z6 = $t42 & $y11;
+        $z7 = $t45 & $y17;
+        $z8 = $t41 & $y10;
+        $z9 = $t44 & $y12;
+        $z10 = $t37 & $y3;
+        $z11 = $t33 & $y4;
+        $z12 = $t43 & $y13;
+        $z13 = $t40 & $y5;
+        $z14 = $t29 & $y2;
+        $z15 = $t42 & $y9;
+        $z16 = $t45 & $y14;
+        $z17 = $t41 & $y8;
+
+        /*
+         * Bottom linear transformation.
+         */
+        $t46 = $z15 ^ $z16;
+        $t47 = $z10 ^ $z11;
+        $t48 = $z5 ^ $z13;
+        $t49 = $z9 ^ $z10;
+        $t50 = $z2 ^ $z12;
+        $t51 = $z2 ^ $z5;
+        $t52 = $z7 ^ $z8;
+        $t53 = $z0 ^ $z3;
+        $t54 = $z6 ^ $z7;
+        $t55 = $z16 ^ $z17;
+        $t56 = $z12 ^ $t48;
+        $t57 = $t50 ^ $t53;
+        $t58 = $z4 ^ $t46;
+        $t59 = $z3 ^ $t54;
+        $t60 = $t46 ^ $t57;
+        $t61 = $z14 ^ $t57;
+        $t62 = $t52 ^ $t58;
+        $t63 = $t49 ^ $t58;
+        $t64 = $z4 ^ $t59;
+        $t65 = $t61 ^ $t62;
+        $t66 = $z1 ^ $t63;
+        $s0 = $t59 ^ $t63;
+        $s6 = $t56 ^ ~$t62;
+        $s7 = $t48 ^ ~$t60;
+        $t67 = $t64 ^ $t65;
+        $s3 = $t53 ^ $t66;
+        $s4 = $t51 ^ $t66;
+        $s5 = $t47 ^ $t65;
+        $s1 = $t64 ^ ~$s3;
+        $s2 = $t55 ^ ~$t67;
+
+        $q[7] = $s0 & self::U32_MAX;
+        $q[6] = $s1 & self::U32_MAX;
+        $q[5] = $s2 & self::U32_MAX;
+        $q[4] = $s3 & self::U32_MAX;
+        $q[3] = $s4 & self::U32_MAX;
+        $q[2] = $s5 & self::U32_MAX;
+        $q[1] = $s6 & self::U32_MAX;
+        $q[0] = $s7 & self::U32_MAX;
+    }
+
+    /**
+     * Mutates the values of $q!
+     *
+     * @param ParagonIE_Sodium_Core_AES_Block $q
+     * @return void
+     */
+    public static function invSbox(ParagonIE_Sodium_Core_AES_Block $q)
+    {
+        self::processInversion($q);
+        self::sbox($q);
+        self::processInversion($q);
+    }
+
+    /**
+     * This is some boilerplate code needed to invert an S-box. Rather than repeat the code
+     * twice, I moved it to a protected method.
+     *
+     * Mutates $q
+     *
+     * @param ParagonIE_Sodium_Core_AES_Block $q
+     * @return void
+     */
+    protected static function processInversion(ParagonIE_Sodium_Core_AES_Block $q)
+    {
+        $q0 = (~$q[0]) & self::U32_MAX;
+        $q1 = (~$q[1]) & self::U32_MAX;
+        $q2 = $q[2] & self::U32_MAX;
+        $q3 = $q[3] & self::U32_MAX;
+        $q4 = $q[4] & self::U32_MAX;
+        $q5 = (~$q[5])  & self::U32_MAX;
+        $q6 = (~$q[6])  & self::U32_MAX;
+        $q7 = $q[7] & self::U32_MAX;
+        $q[7] = ($q1 ^ $q4 ^ $q6) & self::U32_MAX;
+        $q[6] = ($q0 ^ $q3 ^ $q5) & self::U32_MAX;
+        $q[5] = ($q7 ^ $q2 ^ $q4) & self::U32_MAX;
+        $q[4] = ($q6 ^ $q1 ^ $q3) & self::U32_MAX;
+        $q[3] = ($q5 ^ $q0 ^ $q2) & self::U32_MAX;
+        $q[2] = ($q4 ^ $q7 ^ $q1) & self::U32_MAX;
+        $q[1] = ($q3 ^ $q6 ^ $q0) & self::U32_MAX;
+        $q[0] = ($q2 ^ $q5 ^ $q7) & self::U32_MAX;
+    }
+
+    /**
+     * @param int $x
+     * @return int
+     */
+    public static function subWord($x)
+    {
+        $q = ParagonIE_Sodium_Core_AES_Block::fromArray(
+            array($x, $x, $x, $x, $x, $x, $x, $x)
+        );
+        $q->orthogonalize();
+        self::sbox($q);
+        $q->orthogonalize();
+        return $q[0] & self::U32_MAX;
+    }
+
+    /**
+     * Calculate the key schedule from a given random key
+     *
+     * @param string $key
+     * @return ParagonIE_Sodium_Core_AES_KeySchedule
+     * @throws SodiumException
+     */
+    public static function keySchedule($key)
+    {
+        $key_len = self::strlen($key);
+        switch ($key_len) {
+            case 16:
+                $num_rounds = 10;
+                break;
+            case 24:
+                $num_rounds = 12;
+                break;
+            case 32:
+                $num_rounds = 14;
+                break;
+            default:
+                throw new SodiumException('Invalid key length: ' . $key_len);
+        }
+        $skey = array();
+        $comp_skey = array();
+        $nk = $key_len >> 2;
+        $nkf = ($num_rounds + 1) << 2;
+        $tmp = 0;
+
+        for ($i = 0; $i < $nk; ++$i) {
+            $tmp = self::load_4(self::substr($key, $i << 2, 4));
+            $skey[($i << 1)] = $tmp;
+            $skey[($i << 1) + 1] = $tmp;
+        }
+
+        for ($i = $nk, $j = 0, $k = 0; $i < $nkf; ++$i) {
+            if ($j === 0) {
+                $tmp = (($tmp & 0xff) << 24) | ($tmp >> 8);
+                $tmp = (self::subWord($tmp) ^ self::$Rcon[$k]) & self::U32_MAX;
+            } elseif ($nk > 6 && $j === 4) {
+                $tmp = self::subWord($tmp);
+            }
+            $tmp ^= $skey[($i - $nk) << 1];
+            $skey[($i << 1)] = $tmp & self::U32_MAX;
+            $skey[($i << 1) + 1] = $tmp & self::U32_MAX;
+            if (++$j === $nk) {
+                /** @psalm-suppress LoopInvalidation */
+                $j = 0;
+                ++$k;
+            }
+        }
+        for ($i = 0; $i < $nkf; $i += 4) {
+            $q = ParagonIE_Sodium_Core_AES_Block::fromArray(
+                array_slice($skey, $i << 1, 8)
+            );
+            $q->orthogonalize();
+            // We have to overwrite $skey since we're not using C pointers like BearSSL did
+            for ($j = 0; $j < 8; ++$j) {
+                $skey[($i << 1) + $j] = $q[$j];
+            }
+        }
+        for ($i = 0, $j = 0; $i < $nkf; ++$i, $j += 2) {
+            $comp_skey[$i] = ($skey[$j] & 0x55555555)
+                | ($skey[$j + 1] & 0xAAAAAAAA);
+        }
+        return new ParagonIE_Sodium_Core_AES_KeySchedule($comp_skey, $num_rounds);
+    }
+
+    /**
+     * Mutates $q
+     *
+     * @param ParagonIE_Sodium_Core_AES_KeySchedule $skey
+     * @param ParagonIE_Sodium_Core_AES_Block $q
+     * @param int $offset
+     * @return void
+     */
+    public static function addRoundKey(
+        ParagonIE_Sodium_Core_AES_Block $q,
+        ParagonIE_Sodium_Core_AES_KeySchedule $skey,
+        $offset = 0
+    ) {
+        $block = $skey->getRoundKey($offset);
+        for ($j = 0; $j < 8; ++$j) {
+            $q[$j] = ($q[$j] ^ $block[$j]) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        }
+    }
+
+    /**
+     * This mainly exists for testing, as we need the round key features for AEGIS.
+     *
+     * @param string $message
+     * @param string $key
+     * @return string
+     * @throws SodiumException
+     */
+    public static function decryptBlockECB($message, $key)
+    {
+        if (self::strlen($message) !== 16) {
+            throw new SodiumException('decryptBlockECB() expects a 16 byte message');
+        }
+        $skey = self::keySchedule($key)->expand();
+        $q = ParagonIE_Sodium_Core_AES_Block::init();
+        $q[0] = self::load_4(self::substr($message, 0, 4));
+        $q[2] = self::load_4(self::substr($message, 4, 4));
+        $q[4] = self::load_4(self::substr($message, 8, 4));
+        $q[6] = self::load_4(self::substr($message, 12, 4));
+
+        $q->orthogonalize();
+        self::bitsliceDecryptBlock($skey, $q);
+        $q->orthogonalize();
+
+        return self::store32_le($q[0]) .
+            self::store32_le($q[2]) .
+            self::store32_le($q[4]) .
+            self::store32_le($q[6]);
+    }
+
+    /**
+     * This mainly exists for testing, as we need the round key features for AEGIS.
+     *
+     * @param string $message
+     * @param string $key
+     * @return string
+     * @throws SodiumException
+     */
+    public static function encryptBlockECB($message, $key)
+    {
+        if (self::strlen($message) !== 16) {
+            throw new SodiumException('encryptBlockECB() expects a 16 byte message');
+        }
+        $comp_skey = self::keySchedule($key);
+        $skey = $comp_skey->expand();
+        $q = ParagonIE_Sodium_Core_AES_Block::init();
+        $q[0] = self::load_4(self::substr($message, 0, 4));
+        $q[2] = self::load_4(self::substr($message, 4, 4));
+        $q[4] = self::load_4(self::substr($message, 8, 4));
+        $q[6] = self::load_4(self::substr($message, 12, 4));
+
+        $q->orthogonalize();
+        self::bitsliceEncryptBlock($skey, $q);
+        $q->orthogonalize();
+
+        return self::store32_le($q[0]) .
+            self::store32_le($q[2]) .
+            self::store32_le($q[4]) .
+            self::store32_le($q[6]);
+    }
+
+    /**
+     * Mutates $q
+     *
+     * @param ParagonIE_Sodium_Core_AES_Expanded $skey
+     * @param ParagonIE_Sodium_Core_AES_Block $q
+     * @return void
+     */
+    public static function bitsliceEncryptBlock(
+        ParagonIE_Sodium_Core_AES_Expanded $skey,
+        ParagonIE_Sodium_Core_AES_Block $q
+    ) {
+        self::addRoundKey($q, $skey);
+        for ($u = 1; $u < $skey->getNumRounds(); ++$u) {
+            self::sbox($q);
+            $q->shiftRows();
+            $q->mixColumns();
+            self::addRoundKey($q, $skey, ($u << 3));
+        }
+        self::sbox($q);
+        $q->shiftRows();
+        self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3));
+    }
+
+    /**
+     * @param string $x
+     * @param string $y
+     * @return string
+     */
+    public static function aesRound($x, $y)
+    {
+        $q = ParagonIE_Sodium_Core_AES_Block::init();
+        $q[0] = self::load_4(self::substr($x, 0, 4));
+        $q[2] = self::load_4(self::substr($x, 4, 4));
+        $q[4] = self::load_4(self::substr($x, 8, 4));
+        $q[6] = self::load_4(self::substr($x, 12, 4));
+
+        $rk = ParagonIE_Sodium_Core_AES_Block::init();
+        $rk[0] = $rk[1] = self::load_4(self::substr($y, 0, 4));
+        $rk[2] = $rk[3] = self::load_4(self::substr($y, 4, 4));
+        $rk[4] = $rk[5] = self::load_4(self::substr($y, 8, 4));
+        $rk[6] = $rk[7] = self::load_4(self::substr($y, 12, 4));
+
+        $q->orthogonalize();
+        self::sbox($q);
+        $q->shiftRows();
+        $q->mixColumns();
+        $q->orthogonalize();
+        // add round key without key schedule:
+        for ($i = 0; $i < 8; ++$i) {
+            $q[$i] ^= $rk[$i];
+        }
+        return self::store32_le($q[0]) .
+            self::store32_le($q[2]) .
+            self::store32_le($q[4]) .
+            self::store32_le($q[6]);
+    }
+
+    /**
+     * Process two AES blocks in one shot.
+     *
+     * @param string $b0  First AES block
+     * @param string $rk0 First round key
+     * @param string $b1  Second AES block
+     * @param string $rk1 Second round key
+     * @return string[]
+     */
+    public static function doubleRound($b0, $rk0, $b1, $rk1)
+    {
+        $q = ParagonIE_Sodium_Core_AES_Block::init();
+        // First block
+        $q[0] = self::load_4(self::substr($b0, 0, 4));
+        $q[2] = self::load_4(self::substr($b0, 4, 4));
+        $q[4] = self::load_4(self::substr($b0, 8, 4));
+        $q[6] = self::load_4(self::substr($b0, 12, 4));
+        // Second block
+        $q[1] = self::load_4(self::substr($b1, 0, 4));
+        $q[3] = self::load_4(self::substr($b1, 4, 4));
+        $q[5] = self::load_4(self::substr($b1, 8, 4));
+        $q[7] = self::load_4(self::substr($b1, 12, 4));;
+
+        $rk = ParagonIE_Sodium_Core_AES_Block::init();
+        // First round key
+        $rk[0] = self::load_4(self::substr($rk0, 0, 4));
+        $rk[2] = self::load_4(self::substr($rk0, 4, 4));
+        $rk[4] = self::load_4(self::substr($rk0, 8, 4));
+        $rk[6] = self::load_4(self::substr($rk0, 12, 4));
+        // Second round key
+        $rk[1] = self::load_4(self::substr($rk1, 0, 4));
+        $rk[3] = self::load_4(self::substr($rk1, 4, 4));
+        $rk[5] = self::load_4(self::substr($rk1, 8, 4));
+        $rk[7] = self::load_4(self::substr($rk1, 12, 4));
+
+        $q->orthogonalize();
+        self::sbox($q);
+        $q->shiftRows();
+        $q->mixColumns();
+        $q->orthogonalize();
+        // add round key without key schedule:
+        for ($i = 0; $i < 8; ++$i) {
+            $q[$i] ^= $rk[$i];
+        }
+        return array(
+            self::store32_le($q[0]) . self::store32_le($q[2]) . self::store32_le($q[4]) . self::store32_le($q[6]),
+            self::store32_le($q[1]) . self::store32_le($q[3]) . self::store32_le($q[5]) . self::store32_le($q[7]),
+        );
+    }
+
+    /**
+     * @param ParagonIE_Sodium_Core_AES_Expanded $skey
+     * @param ParagonIE_Sodium_Core_AES_Block $q
+     * @return void
+     */
+    public static function bitsliceDecryptBlock(
+        ParagonIE_Sodium_Core_AES_Expanded $skey,
+        ParagonIE_Sodium_Core_AES_Block $q
+    ) {
+        self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3));
+        for ($u = $skey->getNumRounds() - 1; $u > 0; --$u) {
+            $q->inverseShiftRows();
+            self::invSbox($q);
+            self::addRoundKey($q, $skey, ($u << 3));
+            $q->inverseMixColumns();
+        }
+        $q->inverseShiftRows();
+        self::invSbox($q);
+        self::addRoundKey($q, $skey, ($u << 3));
+    }
+}
diff --git a/vendor/paragonie/sodium_compat/src/Core/AES/Block.php b/vendor/paragonie/sodium_compat/src/Core/AES/Block.php
new file mode 100644
index 0000000..070eb8d
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AES/Block.php
@@ -0,0 +1,343 @@
+<?php
+
+if (class_exists('ParagonIE_Sodium_Core_AES_Block', false)) {
+    return;
+}
+
+/**
+ * @internal This should only be used by sodium_compat
+ */
+class ParagonIE_Sodium_Core_AES_Block extends SplFixedArray
+{
+    /**
+     * @var array<int, int>
+     */
+    protected $values = array();
+
+    /**
+     * @var int
+     */
+    protected $size;
+
+    /**
+     * @param int $size
+     */
+    public function __construct($size = 8)
+    {
+        parent::__construct($size);
+        $this->size = $size;
+        $this->values = array_fill(0, $size, 0);
+    }
+
+    /**
+     * @return self
+     */
+    public static function init()
+    {
+        return new self(8);
+    }
+
+    /**
+     * @internal You should not use this directly from another application
+     *
+     * @param array<int, int> $array
+     * @param bool $save_indexes
+     * @return self
+     *
+     * @psalm-suppress MethodSignatureMismatch
+     */
+    #[ReturnTypeWillChange]
+    public static function fromArray($array, $save_indexes = null)
+    {
+        $count = count($array);
+        if ($save_indexes) {
+            $keys = array_keys($array);
+        } else {
+            $keys = range(0, $count - 1);
+        }
+        $array = array_values($array);
+        /** @var array<int, int> $keys */
+
+        $obj = new ParagonIE_Sodium_Core_AES_Block();
+        if ($save_indexes) {
+            for ($i = 0; $i < $count; ++$i) {
+                $obj->offsetSet($keys[$i], $array[$i]);
+            }
+        } else {
+            for ($i = 0; $i < $count; ++$i) {
+                $obj->offsetSet($i, $array[$i]);
+            }
+        }
+        return $obj;
+    }
+
+
+    /**
+     * @internal You should not use this directly from another application
+     *
+     * @param int|null $offset
+     * @param int $value
+     * @return void
+     *
+     * @psalm-suppress MethodSignatureMismatch
+     * @psalm-suppress MixedArrayOffset
+     */
+    #[ReturnTypeWillChange]
+    public function offsetSet($offset, $value)
+    {
+        if (!is_int($value)) {
+            throw new InvalidArgumentException('Expected an integer');
+        }
+        if (is_null($offset)) {
+            $this->values[] = $value;
+        } else {
+            $this->values[$offset] = $value;
+        }
+    }
+
+    /**
+     * @internal You should not use this directly from another application
+     *
+     * @param int $offset
+     * @return bool
+     *
+     * @psalm-suppress MethodSignatureMismatch
+     * @psalm-suppress MixedArrayOffset
+     */
+    #[ReturnTypeWillChange]
+    public function offsetExists($offset)
+    {
+        return isset($this->values[$offset]);
+    }
+
+    /**
+     * @internal You should not use this directly from another application
+     *
+     * @param int $offset
+     * @return void
+     *
+     * @psalm-suppress MethodSignatureMismatch
+     * @psalm-suppress MixedArrayOffset
+     */
+    #[ReturnTypeWillChange]
+    public function offsetUnset($offset)
+    {
+        unset($this->values[$offset]);
+    }
+
+    /**
+     * @internal You should not use this directly from another application
+     *
+     * @param int $offset
+     * @return int
+     *
+     * @psalm-suppress MethodSignatureMismatch
+     * @psalm-suppress MixedArrayOffset
+     */
+    #[ReturnTypeWillChange]
+    public function offsetGet($offset)
+    {
+        if (!isset($this->values[$offset])) {
+            $this->values[$offset] = 0;
+        }
+        return (int) ($this->values[$offset]);
+    }
+
+    /**
+     * @internal You should not use this directly from another application
+     *
+     * @return array
+     */
+    public function __debugInfo()
+    {
+        $out = array();
+        foreach ($this->values as $v) {
+            $out[] = str_pad(dechex($v), 8, '0', STR_PAD_LEFT);
+        }
+        return array(implode(', ', $out));
+        /*
+         return array(implode(', ', $this->values));
+         */
+    }
+
+    /**
+     * @param int $cl low bit mask
+     * @param int $ch high bit mask
+     * @param int $s shift
+     * @param int $x index 1
+     * @param int $y index 2
+     * @return self
+     */
+    public function swapN($cl, $ch, $s, $x, $y)
+    {
+        static $u32mask = ParagonIE_Sodium_Core_Util::U32_MAX;
+        $a = $this->values[$x] & $u32mask;
+        $b = $this->values[$y] & $u32mask;
+        // (x) = (a & cl) | ((b & cl) << (s));
+        $this->values[$x] = ($a & $cl) | ((($b & $cl) << $s) & $u32mask);
+        // (y) = ((a & ch) >> (s)) | (b & ch);
+        $this->values[$y] = ((($a & $ch) & $u32mask) >> $s) | ($b & $ch);
+        return $this;
+    }
+
+    /**
+     * @param int $x index 1
+     * @param int $y index 2
+     * @return self
+     */
+    public function swap2($x, $y)
+    {
+        return $this->swapN(0x55555555, 0xAAAAAAAA, 1, $x, $y);
+    }
+
+    /**
+     * @param int $x index 1
+     * @param int $y index 2
+     * @return self
+     */
+    public function swap4($x, $y)
+    {
+        return $this->swapN(0x33333333, 0xCCCCCCCC, 2, $x, $y);
+    }
+
+    /**
+     * @param int $x index 1
+     * @param int $y index 2
+     * @return self
+     */
+    public function swap8($x, $y)
+    {
+        return $this->swapN(0x0F0F0F0F, 0xF0F0F0F0, 4, $x, $y);
+    }
+
+    /**
+     * @return self
+     */
+    public function orthogonalize()
+    {
+        return $this
+            ->swap2(0, 1)
+            ->swap2(2, 3)
+            ->swap2(4, 5)
+            ->swap2(6, 7)
+
+            ->swap4(0, 2)
+            ->swap4(1, 3)
+            ->swap4(4, 6)
+            ->swap4(5, 7)
+
+            ->swap8(0, 4)
+            ->swap8(1, 5)
+            ->swap8(2, 6)
+            ->swap8(3, 7);
+    }
+
+    /**
+     * @return self
+     */
+    public function shiftRows()
+    {
+        for ($i = 0; $i < 8; ++$i) {
+            $x = $this->values[$i] & ParagonIE_Sodium_Core_Util::U32_MAX;
+            $this->values[$i] = (
+                ($x & 0x000000FF)
+                    | (($x & 0x0000FC00) >> 2) | (($x & 0x00000300) << 6)
+                    | (($x & 0x00F00000) >> 4) | (($x & 0x000F0000) << 4)
+                    | (($x & 0xC0000000) >> 6) | (($x & 0x3F000000) << 2)
+            ) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        }
+        return $this;
+    }
+
+    /**
+     * @param int $x
+     * @return int
+     */
+    public static function rotr16($x)
+    {
+        return (($x << 16) & ParagonIE_Sodium_Core_Util::U32_MAX) | ($x >> 16);
+    }
+
+    /**
+     * @return self
+     */
+    public function mixColumns()
+    {
+        $q0 = $this->values[0];
+        $q1 = $this->values[1];
+        $q2 = $this->values[2];
+        $q3 = $this->values[3];
+        $q4 = $this->values[4];
+        $q5 = $this->values[5];
+        $q6 = $this->values[6];
+        $q7 = $this->values[7];
+        $r0 = (($q0 >> 8) | ($q0 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r1 = (($q1 >> 8) | ($q1 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r2 = (($q2 >> 8) | ($q2 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r3 = (($q3 >> 8) | ($q3 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r4 = (($q4 >> 8) | ($q4 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r5 = (($q5 >> 8) | ($q5 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r6 = (($q6 >> 8) | ($q6 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r7 = (($q7 >> 8) | ($q7 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+
+        $this->values[0] = $q7 ^ $r7 ^ $r0 ^ self::rotr16($q0 ^ $r0);
+        $this->values[1] = $q0 ^ $r0 ^ $q7 ^ $r7 ^ $r1 ^ self::rotr16($q1 ^ $r1);
+        $this->values[2] = $q1 ^ $r1 ^ $r2 ^ self::rotr16($q2 ^ $r2);
+        $this->values[3] = $q2 ^ $r2 ^ $q7 ^ $r7 ^ $r3 ^ self::rotr16($q3 ^ $r3);
+        $this->values[4] = $q3 ^ $r3 ^ $q7 ^ $r7 ^ $r4 ^ self::rotr16($q4 ^ $r4);
+        $this->values[5] = $q4 ^ $r4 ^ $r5 ^ self::rotr16($q5 ^ $r5);
+        $this->values[6] = $q5 ^ $r5 ^ $r6 ^ self::rotr16($q6 ^ $r6);
+        $this->values[7] = $q6 ^ $r6 ^ $r7 ^ self::rotr16($q7 ^ $r7);
+        return $this;
+    }
+
+    /**
+     * @return self
+     */
+    public function inverseMixColumns()
+    {
+        $q0 = $this->values[0];
+        $q1 = $this->values[1];
+        $q2 = $this->values[2];
+        $q3 = $this->values[3];
+        $q4 = $this->values[4];
+        $q5 = $this->values[5];
+        $q6 = $this->values[6];
+        $q7 = $this->values[7];
+        $r0 = (($q0 >> 8) | ($q0 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r1 = (($q1 >> 8) | ($q1 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r2 = (($q2 >> 8) | ($q2 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r3 = (($q3 >> 8) | ($q3 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r4 = (($q4 >> 8) | ($q4 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r5 = (($q5 >> 8) | ($q5 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r6 = (($q6 >> 8) | ($q6 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        $r7 = (($q7 >> 8) | ($q7 << 24)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+
+        $this->values[0] = $q5 ^ $q6 ^ $q7 ^ $r0 ^ $r5 ^ $r7 ^ self::rotr16($q0 ^ $q5 ^ $q6 ^ $r0 ^ $r5);
+        $this->values[1] = $q0 ^ $q5 ^ $r0 ^ $r1 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q1 ^ $q5 ^ $q7 ^ $r1 ^ $r5 ^ $r6);
+        $this->values[2] = $q0 ^ $q1 ^ $q6 ^ $r1 ^ $r2 ^ $r6 ^ $r7 ^ self::rotr16($q0 ^ $q2 ^ $q6 ^ $r2 ^ $r6 ^ $r7);
+        $this->values[3] = $q0 ^ $q1 ^ $q2 ^ $q5 ^ $q6 ^ $r0 ^ $r2 ^ $r3 ^ $r5 ^ self::rotr16($q0 ^ $q1 ^ $q3 ^ $q5 ^ $q6 ^ $q7 ^ $r0 ^ $r3 ^ $r5 ^ $r7);
+        $this->values[4] = $q1 ^ $q2 ^ $q3 ^ $q5 ^ $r1 ^ $r3 ^ $r4 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q1 ^ $q2 ^ $q4 ^ $q5 ^ $q7 ^ $r1 ^ $r4 ^ $r5 ^ $r6);
+        $this->values[5] = $q2 ^ $q3 ^ $q4 ^ $q6 ^ $r2 ^ $r4 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q2 ^ $q3 ^ $q5 ^ $q6 ^ $r2 ^ $r5 ^ $r6 ^ $r7);
+        $this->values[6] = $q3 ^ $q4 ^ $q5 ^ $q7 ^ $r3 ^ $r5 ^ $r6 ^ $r7 ^ self::rotr16($q3 ^ $q4 ^ $q6 ^ $q7 ^ $r3 ^ $r6 ^ $r7);
+        $this->values[7] = $q4 ^ $q5 ^ $q6 ^ $r4 ^ $r6 ^ $r7 ^ self::rotr16($q4 ^ $q5 ^ $q7 ^ $r4 ^ $r7);
+        return $this;
+    }
+
+    /**
+     * @return self
+     */
+    public function inverseShiftRows()
+    {
+        for ($i = 0; $i < 8; ++$i) {
+            $x = $this->values[$i];
+            $this->values[$i] = ParagonIE_Sodium_Core_Util::U32_MAX & (
+                ($x & 0x000000FF)
+                    | (($x & 0x00003F00) << 2) | (($x & 0x0000C000) >> 6)
+                    | (($x & 0x000F0000) << 4) | (($x & 0x00F00000) >> 4)
+                    | (($x & 0x03000000) << 6) | (($x & 0xFC000000) >> 2)
+            );
+        }
+        return $this;
+    }
+}
diff --git a/vendor/paragonie/sodium_compat/src/Core/AES/Expanded.php b/vendor/paragonie/sodium_compat/src/Core/AES/Expanded.php
new file mode 100644
index 0000000..84a6a47
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AES/Expanded.php
@@ -0,0 +1,14 @@
+<?php
+
+if (class_exists('ParagonIE_Sodium_Core_AES_Expanded', false)) {
+    return;
+}
+
+/**
+ * @internal This should only be used by sodium_compat
+ */
+class ParagonIE_Sodium_Core_AES_Expanded extends ParagonIE_Sodium_Core_AES_KeySchedule
+{
+    /** @var bool $expanded */
+    protected $expanded = true;
+}
diff --git a/vendor/paragonie/sodium_compat/src/Core/AES/KeySchedule.php b/vendor/paragonie/sodium_compat/src/Core/AES/KeySchedule.php
new file mode 100644
index 0000000..6b0df18
--- /dev/null
+++ b/vendor/paragonie/sodium_compat/src/Core/AES/KeySchedule.php
@@ -0,0 +1,82 @@
+<?php
+
+if (class_exists('ParagonIE_Sodium_Core_AES_KeySchedule', false)) {
+    return;
+}
+
+/**
+ * @internal This should only be used by sodium_compat
+ */
+class ParagonIE_Sodium_Core_AES_KeySchedule
+{
+    /** @var array<int, int> $skey -- has size 120 */
+    protected $skey;
+
+    /** @var bool $expanded */
+    protected $expanded = false;
+
+    /** @var int $numRounds */
+    private $numRounds;
+
+    /**
+     * @param array $skey
+     * @param int $numRounds
+     */
+    public function __construct(array $skey, $numRounds = 10)
+    {
+        $this->skey = $skey;
+        $this->numRounds = $numRounds;
+    }
+
+    /**
+     * Get a value at an arbitrary index. Mostly used for unit testing.
+     *
+     * @param int $i
+     * @return int
+     */
+    public function get($i)
+    {
+        return $this->skey[$i];
+    }
+
+    /**
+     * @return int
+     */
+    public function getNumRounds()
+    {
+        return $this->numRounds;
+    }
+
+    /**
+     * @param int $offset
+     * @return ParagonIE_Sodium_Core_AES_Block
+     */
+    public function getRoundKey($offset)
+    {
+        return ParagonIE_Sodium_Core_AES_Block::fromArray(
+            array_slice($this->skey, $offset, 8)
+        );
+    }
+
+    /**
+     * Return an expanded key schedule
+     *
+     * @return ParagonIE_Sodium_Core_AES_Expanded
+     */
+    public function expand()
+    {
+        $exp = new ParagonIE_Sodium_Core_AES_Expanded(
+            array_fill(0, 120, 0),
+            $this->numRounds
+        );
+        $n = ($exp->numRounds + 1) << 2;
+        for ($u = 0, $v = 0; $u < $n; ++$u, $v += 2) {
+            $x = $y = $this->skey[$u];
+            $x &= 0x55555555;
+            $exp->skey[$v] = ($x | ($x << 1)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+            $y &= 0xAAAAAAAA;
+            $exp->skey[$v + 1] = ($y | ($y >> 1)) & ParagonIE_Sodium_Core_Util::U32_MAX;
+        }
+        return $exp;
+    }
+}
diff --git a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Cached.php b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Cached.php
index 39bf897..06774ba 100644
--- a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Cached.php
+++ b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Cached.php
@@ -40,26 +40,38 @@ class ParagonIE_Sodium_Core_Curve25519_Ge_Cached
      * @param ParagonIE_Sodium_Core_Curve25519_Fe|null $T2d
      */
     public function __construct(
-        ParagonIE_Sodium_Core_Curve25519_Fe $YplusX = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $YminusX = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $Z = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $T2d = null
+        $YplusX = null,
+        $YminusX = null,
+        $Z = null,
+        $T2d = null
     ) {
         if ($YplusX === null) {
             $YplusX = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($YplusX instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 1 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->YplusX = $YplusX;
         if ($YminusX === null) {
             $YminusX = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($YminusX instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 2 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->YminusX = $YminusX;
         if ($Z === null) {
             $Z = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($Z instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 3 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->Z = $Z;
         if ($T2d === null) {
             $T2d = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($T2d instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 4 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->T2d = $T2d;
     }
 }
diff --git a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P1p1.php b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P1p1.php
index a63d6ab..62d36eb 100644
--- a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P1p1.php
+++ b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P1p1.php
@@ -39,26 +39,38 @@ class ParagonIE_Sodium_Core_Curve25519_Ge_P1p1
      * @param ParagonIE_Sodium_Core_Curve25519_Fe|null $t
      */
     public function __construct(
-        ParagonIE_Sodium_Core_Curve25519_Fe $x = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $y = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $z = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $t = null
+        $x = null,
+        $y = null,
+        $z = null,
+        $t = null
     ) {
         if ($x === null) {
             $x = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($x instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 1 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->X = $x;
         if ($y === null) {
             $y = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($y instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 2 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->Y = $y;
         if ($z === null) {
             $z = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($z instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 3 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->Z = $z;
         if ($t === null) {
             $t = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($t instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 4 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->T = $t;
     }
 }
diff --git a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P2.php b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P2.php
index aee4000..029be72 100644
--- a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P2.php
+++ b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P2.php
@@ -34,21 +34,30 @@ class ParagonIE_Sodium_Core_Curve25519_Ge_P2
      * @param ParagonIE_Sodium_Core_Curve25519_Fe|null $z
      */
     public function __construct(
-        ParagonIE_Sodium_Core_Curve25519_Fe $x = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $y = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $z = null
+        $x = null,
+        $y = null,
+        $z = null
     ) {
         if ($x === null) {
             $x = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($x instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 1 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->X = $x;
         if ($y === null) {
             $y = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($y instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 2 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->Y = $y;
         if ($z === null) {
             $z = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($z instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 3 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->Z = $z;
     }
 }
diff --git a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P3.php b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P3.php
index 00f5b27..e5b2fe4 100644
--- a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P3.php
+++ b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/P3.php
@@ -40,26 +40,38 @@ class ParagonIE_Sodium_Core_Curve25519_Ge_P3
      * @param ParagonIE_Sodium_Core_Curve25519_Fe|null $t
      */
     public function __construct(
-        ParagonIE_Sodium_Core_Curve25519_Fe $x = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $y = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $z = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $t = null
+        $x = null,
+        $y = null,
+        $z = null,
+        $t = null
     ) {
         if ($x === null) {
             $x = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($x instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 1 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->X = $x;
         if ($y === null) {
             $y = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($y instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 2 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->Y = $y;
         if ($z === null) {
             $z = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($z instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 3 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->Z = $z;
         if ($t === null) {
             $t = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($t instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 4 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->T = $t;
     }
 }
diff --git a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Precomp.php b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Precomp.php
index 59611c1..2503d7a 100644
--- a/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Precomp.php
+++ b/vendor/paragonie/sodium_compat/src/Core/Curve25519/Ge/Precomp.php
@@ -34,21 +34,30 @@ class ParagonIE_Sodium_Core_Curve25519_Ge_Precomp
      * @param ParagonIE_Sodium_Core_Curve25519_Fe $xy2d
      */
     public function __construct(
-        ParagonIE_Sodium_Core_Curve25519_Fe $yplusx = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $yminusx = null,
-        ParagonIE_Sodium_Core_Curve25519_Fe $xy2d = null
+        $yplusx = null,
+        $yminusx = null,
+        $xy2d = null
     ) {
         if ($yplusx === null) {
             $yplusx = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($yplusx instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 1 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->yplusx = $yplusx;
         if ($yminusx === null) {
             $yminusx = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($yminusx instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 2 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->yminusx = $yminusx;
         if ($xy2d === null) {
             $xy2d = new ParagonIE_Sodium_Core_Curve25519_Fe();
         }
+        if (!($xy2d instanceof ParagonIE_Sodium_Core_Curve25519_Fe)) {
+            throw new TypeError('Argument 3 must be an instance of ParagonIE_Sodium_Core_Curve25519_Fe');
+        }
         $this->xy2d = $xy2d;
     }
 }
diff --git a/vendor/paragonie/sodium_compat/src/Core/Poly1305/State.php b/vendor/paragonie/sodium_compat/src/Core/Poly1305/State.php
index 4b64e04..cfaeb0b 100644
--- a/vendor/paragonie/sodium_compat/src/Core/Poly1305/State.php
+++ b/vendor/paragonie/sodium_compat/src/Core/Poly1305/State.php
@@ -210,43 +210,43 @@ class ParagonIE_Sodium_Core_Poly1305_State extends ParagonIE_Sodium_Core_Util
 
             /* h *= r */
             $d0 = (
-                self::mul($h0, $r0, 25) +
-                self::mul($s4, $h1, 26) +
-                self::mul($s3, $h2, 26) +
-                self::mul($s2, $h3, 26) +
-                self::mul($s1, $h4, 26)
+                self::mul($h0, $r0, 27) +
+                self::mul($s4, $h1, 27) +
+                self::mul($s3, $h2, 27) +
+                self::mul($s2, $h3, 27) +
+                self::mul($s1, $h4, 27)
             );
 
             $d1 = (
-                self::mul($h0, $r1, 25) +
-                self::mul($h1, $r0, 25) +
-                self::mul($s4, $h2, 26) +
-                self::mul($s3, $h3, 26) +
-                self::mul($s2, $h4, 26)
+                self::mul($h0, $r1, 27) +
+                self::mul($h1, $r0, 27) +
+                self::mul($s4, $h2, 27) +
+                self::mul($s3, $h3, 27) +
+                self::mul($s2, $h4, 27)
             );
 
             $d2 = (
-                self::mul($h0, $r2, 25) +
-                self::mul($h1, $r1, 25) +
-                self::mul($h2, $r0, 25) +
-                self::mul($s4, $h3, 26) +
-                self::mul($s3, $h4, 26)
+                self::mul($h0, $r2, 27) +
+                self::mul($h1, $r1, 27) +
+                self::mul($h2, $r0, 27) +
+                self::mul($s4, $h3, 27) +
+                self::mul($s3, $h4, 27)
             );
 
             $d3 = (
-                self::mul($h0, $r3, 25) +
-                self::mul($h1, $r2, 25) +
-                self::mul($h2, $r1, 25) +
-                self::mul($h3, $r0, 25) +
-                self::mul($s4, $h4, 26)
+                self::mul($h0, $r3, 27) +
+                self::mul($h1, $r2, 27) +
+                self::mul($h2, $r1, 27) +
+                self::mul($h3, $r0, 27) +
+                self::mul($s4, $h4, 27)
             );
 
             $d4 = (
-                self::mul($h0, $r4, 25) +
-                self::mul($h1, $r3, 25) +
-                self::mul($h2, $r2, 25) +
-                self::mul($h3, $r1, 25) +
-                self::mul($h4, $r0, 25)
+                self::mul($h0, $r4, 27) +
+                self::mul($h1, $r3, 27) +
+                self::mul($h2, $r2, 27) +
+                self::mul($h3, $r1, 27) +
+                self::mul($h4, $r0, 27)
             );
 
             /* (partial) h %= p */
diff --git a/vendor/paragonie/sodium_compat/src/Core/Util.php b/vendor/paragonie/sodium_compat/src/Core/Util.php
index 73e463f..e5d96dc 100644
--- a/vendor/paragonie/sodium_compat/src/Core/Util.php
+++ b/vendor/paragonie/sodium_compat/src/Core/Util.php
@@ -9,6 +9,8 @@ if (class_exists('ParagonIE_Sodium_Core_Util', false)) {
  */
 abstract class ParagonIE_Sodium_Core_Util
 {
+    const U32_MAX = 0xFFFFFFFF;
+
     /**
      * @param int $integer
      * @param int $size (16, 32, 64)
@@ -33,6 +35,28 @@ abstract class ParagonIE_Sodium_Core_Util
         );
     }
 
+    /**
+     * @param string $a
+     * @param string $b
+     * @return string
+     * @throws SodiumException
+     */
+    public static function andStrings($a, $b)
+    {
+        /* Type checks: */
+        if (!is_string($a)) {
+            throw new TypeError('Argument 1 must be a string');
+        }
+        if (!is_string($b)) {
+            throw new TypeError('Argument 2 must be a string');
+        }
+        $len = self::strlen($a);
+        if (self::strlen($b) !== $len) {
+            throw new SodiumException('Both strings must be of equal length to combine with bitwise AND');
+        }
+        return $a & $b;
+    }
+
     /**
      * Convert a binary string into a hexadecimal string without cache-timing
      * leaks
diff --git a/vendor/paragonie/sodium_compat/src/Core32/Poly1305/State.php b/vendor/paragonie/sodium_compat/src/Core32/Poly1305/State.php
index e77a8f4..90d0362 100644
--- a/vendor/paragonie/sodium_compat/src/Core32/Poly1305/State.php
+++ b/vendor/paragonie/sodium_compat/src/Core32/Poly1305/State.php
@@ -255,39 +255,39 @@ class ParagonIE_Sodium_Core32_Poly1305_State extends ParagonIE_Sodium_Core32_Uti
 
             /* h *= r */
             $d0 = $zero
-                ->addInt64($h0->mulInt64($r0, 25))
-                ->addInt64($s4->mulInt64($h1, 26))
-                ->addInt64($s3->mulInt64($h2, 26))
-                ->addInt64($s2->mulInt64($h3, 26))
-                ->addInt64($s1->mulInt64($h4, 26));
+                ->addInt64($h0->mulInt64($r0, 27))
+                ->addInt64($s4->mulInt64($h1, 27))
+                ->addInt64($s3->mulInt64($h2, 27))
+                ->addInt64($s2->mulInt64($h3, 27))
+                ->addInt64($s1->mulInt64($h4, 27));
 
             $d1 = $zero
-                ->addInt64($h0->mulInt64($r1, 25))
-                ->addInt64($h1->mulInt64($r0, 25))
-                ->addInt64($s4->mulInt64($h2, 26))
-                ->addInt64($s3->mulInt64($h3, 26))
-                ->addInt64($s2->mulInt64($h4, 26));
+                ->addInt64($h0->mulInt64($r1, 27))
+                ->addInt64($h1->mulInt64($r0, 27))
+                ->addInt64($s4->mulInt64($h2, 27))
+                ->addInt64($s3->mulInt64($h3, 27))
+                ->addInt64($s2->mulInt64($h4, 27));
 
             $d2 = $zero
-                ->addInt64($h0->mulInt64($r2, 25))
-                ->addInt64($h1->mulInt64($r1, 25))
-                ->addInt64($h2->mulInt64($r0, 25))
-                ->addInt64($s4->mulInt64($h3, 26))
-                ->addInt64($s3->mulInt64($h4, 26));
+                ->addInt64($h0->mulInt64($r2, 27))
+                ->addInt64($h1->mulInt64($r1, 27))
+                ->addInt64($h2->mulInt64($r0, 27))
+                ->addInt64($s4->mulInt64($h3, 27))
+                ->addInt64($s3->mulInt64($h4, 27));
 
             $d3 = $zero
-                ->addInt64($h0->mulInt64($r3, 25))
-                ->addInt64($h1->mulInt64($r2, 25))
-                ->addInt64($h2->mulInt64($r1, 25))
-                ->addInt64($h3->mulInt64($r0, 25))
-                ->addInt64($s4->mulInt64($h4, 26));
+                ->addInt64($h0->mulInt64($r3, 27))
+                ->addInt64($h1->mulInt64($r2, 27))
+                ->addInt64($h2->mulInt64($r1, 27))
+                ->addInt64($h3->mulInt64($r0, 27))
+                ->addInt64($s4->mulInt64($h4, 27));
 
             $d4 = $zero
-                ->addInt64($h0->mulInt64($r4, 25))
-                ->addInt64($h1->mulInt64($r3, 25))
-                ->addInt64($h2->mulInt64($r2, 25))
-                ->addInt64($h3->mulInt64($r1, 25))
-                ->addInt64($h4->mulInt64($r0, 25));
+                ->addInt64($h0->mulInt64($r4, 27))
+                ->addInt64($h1->mulInt64($r3, 27))
+                ->addInt64($h2->mulInt64($r2, 27))
+                ->addInt64($h3->mulInt64($r1, 27))
+                ->addInt64($h4->mulInt64($r0, 27));
 
             /* (partial) h %= p */
             $c = $d0->shiftRight(26);
diff --git a/vendor/paragonie/sodium_compat/src/File.php b/vendor/paragonie/sodium_compat/src/File.php
index e8622c7..1b02fc4 100644
--- a/vendor/paragonie/sodium_compat/src/File.php
+++ b/vendor/paragonie/sodium_compat/src/File.php
@@ -25,8 +25,13 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function box($inputFile, $outputFile, $nonce, $keyPair)
-    {
+    public static function box(
+        $inputFile,
+        $outputFile,
+        $nonce,
+        #[\SensitiveParameter]
+        $keyPair
+    ) {
         /* Type checks: */
         if (!is_string($inputFile)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
@@ -91,8 +96,13 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function box_open($inputFile, $outputFile, $nonce, $keypair)
-    {
+    public static function box_open(
+        $inputFile,
+        $outputFile,
+        $nonce,
+        #[\SensitiveParameter]
+        $keypair
+    ) {
         /* Type checks: */
         if (!is_string($inputFile)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
@@ -161,8 +171,12 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function box_seal($inputFile, $outputFile, $publicKey)
-    {
+    public static function box_seal(
+        $inputFile,
+        $outputFile,
+        #[\SensitiveParameter]
+        $publicKey
+    ) {
         /* Type checks: */
         if (!is_string($inputFile)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
@@ -265,8 +279,12 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function box_seal_open($inputFile, $outputFile, $ecdhKeypair)
-    {
+    public static function box_seal_open(
+        $inputFile,
+        $outputFile,
+        #[\SensitiveParameter]
+        $ecdhKeypair
+    ) {
         /* Type checks: */
         if (!is_string($inputFile)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
@@ -350,8 +368,12 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws TypeError
      * @psalm-suppress FailedTypeResolution
      */
-    public static function generichash($filePath, $key = '', $outputLength = 32)
-    {
+    public static function generichash(
+        $filePath,
+        #[\SensitiveParameter]
+        $key = '',
+        $outputLength = 32
+    ) {
         /* Type checks: */
         if (!is_string($filePath)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.');
@@ -428,8 +450,13 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function secretbox($inputFile, $outputFile, $nonce, $key)
-    {
+    public static function secretbox(
+        $inputFile,
+        $outputFile,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         if (!is_string($inputFile)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given..');
@@ -493,8 +520,13 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function secretbox_open($inputFile, $outputFile, $nonce, $key)
-    {
+    public static function secretbox_open(
+        $inputFile,
+        $outputFile,
+        $nonce,
+        #[\SensitiveParameter]
+        $key
+    ) {
         /* Type checks: */
         if (!is_string($inputFile)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.');
@@ -560,8 +592,11 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws SodiumException
      * @throws TypeError
      */
-    public static function sign($filePath, $secretKey)
-    {
+    public static function sign(
+        $filePath,
+        #[\SensitiveParameter]
+        $secretKey
+    ) {
         /* Type checks: */
         if (!is_string($filePath)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.');
@@ -656,8 +691,11 @@ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util
      * @throws TypeError
      * @throws Exception
      */
-    public static function verify($sig, $filePath, $publicKey)
-    {
+    public static function verify(
+        $sig,
+        $filePath,
+        $publicKey
+    ) {
         /* Type checks: */
         if (!is_string($sig)) {
             throw new TypeError('Argument 1 must be a string, ' . gettype($sig) . ' given.');
diff --git a/vendor/phpdocumentor/reflection-docblock/README.md b/vendor/phpdocumentor/reflection-docblock/README.md
index 51f1088..a0fe02b 100644
--- a/vendor/phpdocumentor/reflection-docblock/README.md
+++ b/vendor/phpdocumentor/reflection-docblock/README.md
@@ -1,6 +1,5 @@
 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
 ![Qa workflow](https://github.com/phpDocumentor/ReflectionDocBlock/workflows/Qa%20workflow/badge.svg)
-[![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/ReflectionDocBlock.svg)](https://coveralls.io/github/phpDocumentor/ReflectionDocBlock?branch=master)
 [![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/ReflectionDocBlock.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionDocBlock/?branch=master)
 [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/ReflectionDocBlock.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionDocBlock/?branch=master)
 [![Stable Version](https://img.shields.io/packagist/v/phpdocumentor/reflection-docblock.svg)](https://packagist.org/packages/phpdocumentor/reflection-docblock)
diff --git a/vendor/phpdocumentor/reflection-docblock/composer.json b/vendor/phpdocumentor/reflection-docblock/composer.json
index d907630..cf8f49d 100644
--- a/vendor/phpdocumentor/reflection-docblock/composer.json
+++ b/vendor/phpdocumentor/reflection-docblock/composer.json
@@ -10,19 +10,26 @@
         },
         {
             "name": "Jaap van Otterdijk",
-            "email": "account@ijaap.nl"
+            "email": "opensource@ijaap.nl"
         }
     ],
     "require": {
-        "php": "^7.2 || ^8.0",
-        "phpdocumentor/type-resolver": "^1.3",
+        "php": "^7.4 || ^8.0",
+        "phpdocumentor/type-resolver": "^1.7",
         "webmozart/assert": "^1.9.1",
         "phpdocumentor/reflection-common": "^2.2",
-        "ext-filter": "*"
+        "ext-filter": "*",
+        "phpstan/phpdoc-parser": "^1.7",
+        "doctrine/deprecations": "^1.1"
     },
     "require-dev": {
-        "mockery/mockery": "~1.3.2",
-        "psalm/phar": "^4.8"
+        "mockery/mockery": "~1.3.5",
+        "phpunit/phpunit": "^9.5",
+        "phpstan/phpstan": "^1.8",
+        "phpstan/phpstan-mockery": "^1.1",
+        "phpstan/extension-installer": "^1.1",
+        "phpstan/phpstan-webmozart-assert": "^1.2",
+        "vimeo/psalm": "^5.13"
     },
     "autoload": {
         "psr-4": {
@@ -34,6 +41,14 @@
             "phpDocumentor\\Reflection\\": ["tests/unit", "tests/integration"]
         }
     },
+    "config": {
+        "platform": {
+            "php":"7.4.0"
+        },
+        "allow-plugins": {
+            "phpstan/extension-installer": true
+        }
+    },
     "extra": {
         "branch-alias": {
             "dev-master": "5.x-dev"
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock.php
index cc33e60..90d8066 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock.php
@@ -20,25 +20,25 @@ use Webmozart\Assert\Assert;
 final class DocBlock
 {
     /** @var string The opening line for this docblock. */
-    private $summary;
+    private string $summary;
 
     /** @var DocBlock\Description The actual description for this docblock. */
-    private $description;
+    private DocBlock\Description $description;
 
     /** @var Tag[] An array containing all the tags in this docblock; except inline. */
-    private $tags = [];
+    private array $tags = [];
 
     /** @var Types\Context|null Information about the context of this DocBlock. */
-    private $context;
+    private ?Types\Context $context = null;
 
     /** @var Location|null Information about the location of this DocBlock. */
-    private $location;
+    private ?Location $location = null;
 
     /** @var bool Is this DocBlock (the start of) a template? */
-    private $isTemplateStart;
+    private bool $isTemplateStart;
 
     /** @var bool Does this DocBlock signify the end of a DocBlock template? */
-    private $isTemplateEnd;
+    private bool $isTemplateEnd;
 
     /**
      * @param DocBlock\Tag[] $tags
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php
index a31b289..a188ae3 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.php
@@ -48,15 +48,14 @@ use function vsprintf;
  * is mainly responsible for rendering.
  *
  * @see DescriptionFactory to create a new Description.
- * @see Description\Formatter for the formatting of the body and tags.
+ * @see Tags\Formatter for the formatting of the body and tags.
  */
 class Description
 {
-    /** @var string */
-    private $bodyTemplate;
+    private string $bodyTemplate;
 
     /** @var Tag[] */
-    private $tags;
+    private array $tags;
 
     /**
      * Initializes a Description with its body (template) and a listing of the tags used in the body template.
@@ -93,6 +92,10 @@ class Description
      */
     public function render(?Formatter $formatter = null): string
     {
+        if ($this->tags === []) {
+            return vsprintf($this->bodyTemplate, []);
+        }
+
         if ($formatter === null) {
             $formatter = new PassthroughFormatter();
         }
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php
index 1a519ec..e7bc616 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php
@@ -13,6 +13,7 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock;
 
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
 use phpDocumentor\Reflection\Types\Context as TypeContext;
 use phpDocumentor\Reflection\Utils;
 
@@ -47,13 +48,12 @@ use const PREG_SPLIT_DELIM_CAPTURE;
  */
 class DescriptionFactory
 {
-    /** @var TagFactory */
-    private $tagFactory;
+    private Factory $tagFactory;
 
     /**
      * Initializes this factory with the means to construct (inline) tags.
      */
-    public function __construct(TagFactory $tagFactory)
+    public function __construct(Factory $tagFactory)
     {
         $this->tagFactory = $tagFactory;
     }
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php
index 6a6b472..7136e28 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php
@@ -31,11 +31,10 @@ use const DIRECTORY_SEPARATOR;
  */
 class ExampleFinder
 {
-    /** @var string */
-    private $sourceDirectory = '';
+    private string $sourceDirectory = '';
 
     /** @var string[] */
-    private $exampleDirectories = [];
+    private array $exampleDirectories = [];
 
     /**
      * Attempts to find the example contents for the given descriptor.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php
index 77e5fb5..31ca29c 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php
@@ -29,21 +29,20 @@ use function wordwrap;
 class Serializer
 {
     /** @var string The string to indent the comment with. */
-    protected $indentString = ' ';
+    protected string $indentString = ' ';
 
     /** @var int The number of times the indent string is repeated. */
-    protected $indent = 0;
+    protected int $indent = 0;
 
     /** @var bool Whether to indent the first line with the given indent amount and string. */
-    protected $isFirstLineIndented = true;
+    protected bool $isFirstLineIndented = true;
 
     /** @var int|null The max length of a line. */
-    protected $lineLength;
+    protected ?int $lineLength = null;
 
     /** @var Formatter A custom tag formatter. */
-    protected $tagFormatter;
-    /** @var string */
-    private $lineEnding;
+    protected Formatter $tagFormatter;
+    private string $lineEnding;
 
     /**
      * Create a Serializer instance.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php
index 8d76595..7360a70 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php
@@ -17,6 +17,7 @@ use InvalidArgumentException;
 use phpDocumentor\Reflection\DocBlock\Tags\Author;
 use phpDocumentor\Reflection\DocBlock\Tags\Covers;
 use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
 use phpDocumentor\Reflection\DocBlock\Tags\Generic;
 use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
 use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag;
@@ -40,12 +41,15 @@ use ReflectionNamedType;
 use ReflectionParameter;
 use Webmozart\Assert\Assert;
 
+use function array_key_exists;
 use function array_merge;
 use function array_slice;
 use function call_user_func_array;
 use function count;
 use function get_class;
+use function is_object;
 use function preg_match;
+use function sprintf;
 use function strpos;
 use function trim;
 
@@ -72,10 +76,10 @@ final class StandardTagFactory implements TagFactory
     public const REGEX_TAGNAME = '[\w\-\_\\\\:]+';
 
     /**
-     * @var array<class-string<Tag>> An array with a tag as a key, and an
+     * @var array<class-string<Tag>|Factory> An array with a tag as a key, and an
      *                               FQCN to a class that handles it as an array value.
      */
-    private $tagHandlerMappings = [
+    private array $tagHandlerMappings = [
         'author' => Author::class,
         'covers' => Covers::class,
         'deprecated' => Deprecated::class,
@@ -101,22 +105,21 @@ final class StandardTagFactory implements TagFactory
      * @var array<class-string<Tag>> An array with a anotation s a key, and an
      *      FQCN to a class that handles it as an array value.
      */
-    private $annotationMappings = [];
+    private array $annotationMappings = [];
 
     /**
      * @var ReflectionParameter[][] a lazy-loading cache containing parameters
      *      for each tagHandler that has been used.
      */
-    private $tagHandlerParameterCache = [];
+    private array $tagHandlerParameterCache = [];
 
-    /** @var FqsenResolver */
-    private $fqsenResolver;
+    private FqsenResolver $fqsenResolver;
 
     /**
      * @var mixed[] an array representing a simple Service Locator where we can store parameters and
      *     services that can be inserted into the Factory Methods of Tag Handlers.
      */
-    private $serviceLocator = [];
+    private array $serviceLocator = [];
 
     /**
      * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers.
@@ -162,18 +165,25 @@ final class StandardTagFactory implements TagFactory
         $this->serviceLocator[$alias ?: get_class($service)] = $service;
     }
 
-    public function registerTagHandler(string $tagName, string $handler): void
+    /** {@inheritDoc} */
+    public function registerTagHandler(string $tagName, $handler): void
     {
         Assert::stringNotEmpty($tagName);
-        Assert::classExists($handler);
-        Assert::implementsInterface($handler, Tag::class);
-
         if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
             throw new InvalidArgumentException(
                 'A namespaced tag must have a leading backslash as it must be fully qualified'
             );
         }
 
+        if (is_object($handler)) {
+            Assert::isInstanceOf($handler, Factory::class);
+            $this->tagHandlerMappings[$tagName] = $handler;
+
+            return;
+        }
+
+        Assert::classExists($handler);
+        Assert::implementsInterface($handler, Tag::class);
         $this->tagHandlerMappings[$tagName] = $handler;
     }
 
@@ -210,6 +220,10 @@ final class StandardTagFactory implements TagFactory
             $this->getServiceLocatorWithDynamicParameters($context, $name, $body)
         );
 
+        if (array_key_exists('tagLine', $arguments)) {
+            $arguments['tagLine'] = sprintf('@%s %s', $name, $body);
+        }
+
         try {
             $callable = [$handlerClassName, 'create'];
             Assert::isCallable($callable);
@@ -225,9 +239,9 @@ final class StandardTagFactory implements TagFactory
     /**
      * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
      *
-     * @return class-string<Tag>
+     * @return class-string<Tag>|Factory
      */
-    private function findHandlerClassName(string $tagName, TypeContext $context): string
+    private function findHandlerClassName(string $tagName, TypeContext $context)
     {
         $handlerClassName = Generic::class;
         if (isset($this->tagHandlerMappings[$tagName])) {
@@ -268,18 +282,18 @@ final class StandardTagFactory implements TagFactory
                 }
             }
 
-            if (isset($locator[$typeHint])) {
-                $arguments[] = $locator[$typeHint];
-                continue;
-            }
-
             $parameterName = $parameter->getName();
-            if (isset($locator[$parameterName])) {
-                $arguments[] = $locator[$parameterName];
+            if (isset($locator[$typeHint])) {
+                $arguments[$parameterName] = $locator[$typeHint];
                 continue;
             }
 
-            $arguments[] = null;
+            if (isset($locator[$parameterName])) {
+                $arguments[$parameterName] = $locator[$parameterName];
+                continue;
+            }
+
+            $arguments[$parameterName] = null;
         }
 
         return $arguments;
@@ -289,12 +303,14 @@ final class StandardTagFactory implements TagFactory
      * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
      * tag handler class name.
      *
-     * @param class-string $handlerClassName
+     * @param class-string|Factory $handler
      *
      * @return ReflectionParameter[]
      */
-    private function fetchParametersForHandlerFactoryMethod(string $handlerClassName): array
+    private function fetchParametersForHandlerFactoryMethod($handler): array
     {
+        $handlerClassName = is_object($handler) ? get_class($handler) : $handler;
+
         if (!isset($this->tagHandlerParameterCache[$handlerClassName])) {
             $methodReflection                                  = new ReflectionMethod($handlerClassName, 'create');
             $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php
index c0868dc..a6f1ae8 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php
@@ -14,9 +14,9 @@ declare(strict_types=1);
 namespace phpDocumentor\Reflection\DocBlock;
 
 use InvalidArgumentException;
-use phpDocumentor\Reflection\Types\Context as TypeContext;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
 
-interface TagFactory
+interface TagFactory extends Factory
 {
     /**
      * Adds a parameter to the service locator that can be injected in a tag's factory method.
@@ -40,17 +40,6 @@ interface TagFactory
      */
     public function addParameter(string $name, $value): void;
 
-    /**
-     * Factory method responsible for instantiating the correct sub type.
-     *
-     * @param string $tagLine The text for this tag, including description.
-     *
-     * @return Tag A new tag object.
-     *
-     * @throws InvalidArgumentException If an invalid tag line was presented.
-     */
-    public function create(string $tagLine, ?TypeContext $context = null): Tag;
-
     /**
      * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided.
      *
@@ -71,7 +60,7 @@ interface TagFactory
      *
      * @param string                    $tagName Name of tag to register a handler for. When registering a namespaced
      *                                   tag, the full name, along with a prefixing slash MUST be provided.
-     * @param class-string<Tag>         $handler FQCN of handler.
+     * @param class-string<Tag>|Factory $handler FQCN of handler.
      *
      * @throws InvalidArgumentException If the tag name is not a string.
      * @throws InvalidArgumentException If the tag name is namespaced (contains backslashes) but
@@ -80,5 +69,5 @@ interface TagFactory
      * @throws InvalidArgumentException If the handler is not an existing class.
      * @throws InvalidArgumentException If the handler does not implement the {@see Tag} interface.
      */
-    public function registerTagHandler(string $tagName, string $handler): void;
+    public function registerTagHandler(string $tagName, $handler): void;
 }
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php
index ae09ecf..290e5a9 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php
@@ -27,13 +27,13 @@ use const FILTER_VALIDATE_EMAIL;
 final class Author extends BaseTag implements Factory\StaticMethod
 {
     /** @var string register that this is the author tag. */
-    protected $name = 'author';
+    protected string $name = 'author';
 
     /** @var string The name of the author */
-    private $authorName;
+    private string $authorName;
 
     /** @var string The email of the author */
-    private $authorEmail;
+    private string $authorEmail;
 
     /**
      * Initializes this tag with the author name and e-mail.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php
index a28d5bf..98b0d88 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php
@@ -22,10 +22,10 @@ use phpDocumentor\Reflection\DocBlock\Description;
 abstract class BaseTag implements DocBlock\Tag
 {
     /** @var string Name of the tag */
-    protected $name = '';
+    protected string $name = '';
 
     /** @var Description|null Description of the tag. */
-    protected $description;
+    protected ?Description $description = null;
 
     /**
      * Gets the name of this tag.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php
index 3eff9d8..022594e 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php
@@ -29,11 +29,9 @@ use function explode;
  */
 final class Covers extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'covers';
+    protected string $name = 'covers';
 
-    /** @var Fqsen */
-    private $refers;
+    private Fqsen $refers;
 
     /**
      * Initializes this tag.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php
index dbcad28..a55cd91 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php
@@ -25,8 +25,7 @@ use function preg_match;
  */
 final class Deprecated extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'deprecated';
+    protected string $name = 'deprecated';
 
     /**
      * PCRE regular expression matching a version vector.
@@ -45,7 +44,7 @@ final class Deprecated extends BaseTag implements Factory\StaticMethod
     )';
 
     /** @var string|null The version vector. */
-    private $version;
+    private ?string $version = null;
 
     public function __construct(?string $version = null, ?Description $description = null)
     {
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php
index 825355a..fd9ef6e 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php
@@ -29,22 +29,19 @@ use function trim;
 final class Example implements Tag, Factory\StaticMethod
 {
     /** @var string Path to a file to use as an example. May also be an absolute URI. */
-    private $filePath;
+    private string $filePath;
 
     /**
      * @var bool Whether the file path component represents an URI. This determines how the file portion
      *     appears at {@link getContent()}.
      */
-    private $isURI;
+    private bool $isURI;
 
-    /** @var int */
-    private $startingLine;
+    private int $startingLine;
 
-    /** @var int */
-    private $lineCount;
+    private int $lineCount;
 
-    /** @var string|null */
-    private $content;
+    private ?string $content = null;
 
     public function __construct(
         string $filePath,
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php
new file mode 100644
index 0000000..b1618bf
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php
@@ -0,0 +1,122 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\Parser\ConstExprParser;
+use PHPStan\PhpDocParser\Parser\PhpDocParser;
+use PHPStan\PhpDocParser\Parser\TokenIterator;
+use PHPStan\PhpDocParser\Parser\TypeParser;
+use RuntimeException;
+
+use function ltrim;
+use function property_exists;
+use function rtrim;
+
+/**
+ * Factory class creating tags using phpstan's parser
+ *
+ * This class uses {@see PHPStanFactory} implementations to create tags
+ * from the ast of the phpstan docblock parser.
+ *
+ * @internal This class is not part of the BC promise of this library.
+ */
+class AbstractPHPStanFactory implements Factory
+{
+    private PhpDocParser $parser;
+    private Lexer $lexer;
+    /** @var PHPStanFactory[] */
+    private array $factories;
+
+    public function __construct(PHPStanFactory ...$factories)
+    {
+        $this->lexer = new Lexer(true);
+        $constParser = new ConstExprParser(true, true, ['lines' => true, 'indexes' => true]);
+        $this->parser = new PhpDocParser(
+            new TypeParser($constParser, true, ['lines' => true, 'indexes' => true]),
+            $constParser,
+            true,
+            true,
+            ['lines' => true, 'indexes' => true],
+            true
+        );
+        $this->factories = $factories;
+    }
+
+    public function create(string $tagLine, ?TypeContext $context = null): Tag
+    {
+        $tokens = $this->tokenizeLine($tagLine);
+        $ast = $this->parser->parseTag($tokens);
+        if (property_exists($ast->value, 'description') === true) {
+            $ast->value->setAttribute(
+                'description',
+                $ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END)
+            );
+        }
+
+        if ($context === null) {
+            $context = new TypeContext('');
+        }
+
+        try {
+            foreach ($this->factories as $factory) {
+                if ($factory->supports($ast, $context)) {
+                    return $factory->create($ast, $context);
+                }
+            }
+        } catch (RuntimeException $e) {
+            return InvalidTag::create((string) $ast->value, 'method')->withError($e);
+        }
+
+        return InvalidTag::create(
+            (string) $ast->value,
+            $ast->name
+        );
+    }
+
+    /**
+     * Solve the issue with the lexer not tokenizing the line correctly
+     *
+     * This method is a workaround for the lexer that includes newline tokens with spaces. For
+     * phpstan this isn't an issue, as it doesn't do a lot of things with the indentation of descriptions.
+     * But for us is important to keep the indentation of the descriptions, so we need to fix the lexer output.
+     */
+    private function tokenizeLine(string $tagLine): TokenIterator
+    {
+        $tokens = $this->lexer->tokenize($tagLine);
+        $fixed = [];
+        foreach ($tokens as $token) {
+            if (($token[1] === Lexer::TOKEN_PHPDOC_EOL) && rtrim($token[0], " \t") !== $token[0]) {
+                $fixed[] = [
+                    rtrim($token[Lexer::VALUE_OFFSET], " \t"),
+                    Lexer::TOKEN_PHPDOC_EOL,
+                    $token[2] ?? null,
+                ];
+                $fixed[] = [
+                    ltrim($token[Lexer::VALUE_OFFSET], "\n\r"),
+                    Lexer::TOKEN_HORIZONTAL_WS,
+                    ($token[2] ?? null) + 1,
+                ];
+                continue;
+            }
+
+            $fixed[] = $token;
+        }
+
+        return new TokenIterator($fixed);
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Factory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Factory.php
new file mode 100644
index 0000000..190d3ff
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Factory.php
@@ -0,0 +1,41 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @link http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use InvalidArgumentException;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\Types\Context as TypeContext;
+
+interface Factory
+{
+    /**
+     * Factory method responsible for instantiating the correct sub type.
+     *
+     * @param string $tagLine The text for this tag, including description.
+     *
+     * @return Tag A new tag object.
+     *
+     * @throws InvalidArgumentException If an invalid tag line was presented.
+     */
+    public function create(string $tagLine, ?TypeContext $context = null): Tag;
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodFactory.php
new file mode 100644
index 0000000..920be84
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/MethodFactory.php
@@ -0,0 +1,81 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\Method;
+use phpDocumentor\Reflection\DocBlock\Tags\MethodParameter;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context;
+use phpDocumentor\Reflection\Types\Mixed_;
+use phpDocumentor\Reflection\Types\Void_;
+use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use Webmozart\Assert\Assert;
+
+use function array_map;
+use function trim;
+
+/**
+ * @internal This class is not part of the BC promise of this library.
+ */
+final class MethodFactory implements PHPStanFactory
+{
+    private DescriptionFactory $descriptionFactory;
+    private TypeResolver $typeResolver;
+
+    public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
+    {
+        $this->descriptionFactory = $descriptionFactory;
+        $this->typeResolver = $typeResolver;
+    }
+
+    public function create(PhpDocTagNode $node, Context $context): Tag
+    {
+        $tagValue = $node->value;
+        Assert::isInstanceOf($tagValue, MethodTagValueNode::class);
+
+        return new Method(
+            $tagValue->methodName,
+            [],
+            $this->createReturnType($tagValue, $context),
+            $tagValue->isStatic,
+            $this->descriptionFactory->create($tagValue->description, $context),
+            false,
+            array_map(
+                function (MethodTagValueParameterNode $param) use ($context) {
+                    return new MethodParameter(
+                        trim($param->parameterName, '$'),
+                        $param->type === null ? new Mixed_() : $this->typeResolver->createType(
+                            $param->type,
+                            $context
+                        ),
+                        $param->isReference,
+                        $param->isVariadic,
+                        (string) $param->defaultValue
+                    );
+                },
+                $tagValue->parameters
+            ),
+        );
+    }
+
+    public function supports(PhpDocTagNode $node, Context $context): bool
+    {
+        return $node->value instanceof MethodTagValueNode;
+    }
+
+    private function createReturnType(MethodTagValueNode $tagValue, Context $context): Type
+    {
+        if ($tagValue->returnType === null) {
+            return new Void_();
+        }
+
+        return $this->typeResolver->createType($tagValue->returnType, $context);
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PHPStanFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PHPStanFactory.php
new file mode 100644
index 0000000..cf04a06
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PHPStanFactory.php
@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\Types\Context;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+
+interface PHPStanFactory
+{
+    public function create(PhpDocTagNode $node, Context $context): Tag;
+
+    public function supports(PhpDocTagNode $node, Context $context): bool;
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ParamFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ParamFactory.php
new file mode 100644
index 0000000..7d680d9
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ParamFactory.php
@@ -0,0 +1,92 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use Doctrine\Deprecations\Deprecation;
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
+use phpDocumentor\Reflection\DocBlock\Tags\Param;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context;
+use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
+use Webmozart\Assert\Assert;
+
+use function is_string;
+use function sprintf;
+use function trim;
+
+/**
+ * @internal This class is not part of the BC promise of this library.
+ */
+final class ParamFactory implements PHPStanFactory
+{
+    private DescriptionFactory $descriptionFactory;
+    private TypeResolver $typeResolver;
+
+    public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
+    {
+        $this->descriptionFactory = $descriptionFactory;
+        $this->typeResolver = $typeResolver;
+    }
+
+    public function create(PhpDocTagNode $node, Context $context): Tag
+    {
+        $tagValue = $node->value;
+
+        if ($tagValue instanceof InvalidTagValueNode) {
+            Deprecation::trigger(
+                'phpdocumentor/reflection-docblock',
+                'https://github.com/phpDocumentor/ReflectionDocBlock/issues/362',
+                sprintf(
+                    'Param tag value "%s" is invalid, falling back to legacy parsing. Please update your docblocks.',
+                    $tagValue->value
+                )
+            );
+
+            return Param::create($tagValue->value, $this->typeResolver, $this->descriptionFactory, $context);
+        }
+
+        Assert::isInstanceOfAny(
+            $tagValue,
+            [
+                ParamTagValueNode::class,
+                TypelessParamTagValueNode::class,
+            ]
+        );
+
+        if (($tagValue->type ?? null) instanceof OffsetAccessTypeNode) {
+            return InvalidTag::create(
+                (string) $tagValue,
+                'param'
+            );
+        }
+
+        $description = $tagValue->getAttribute('description');
+        if (is_string($description) === false) {
+            $description = $tagValue->description;
+        }
+
+        return new Param(
+            trim($tagValue->parameterName, '$'),
+            $this->typeResolver->createType($tagValue->type ?? new IdentifierTypeNode('mixed'), $context),
+            $tagValue->isVariadic,
+            $this->descriptionFactory->create($description, $context),
+            $tagValue->isReference
+        );
+    }
+
+    public function supports(PhpDocTagNode $node, Context $context): bool
+    {
+        return $node->value instanceof ParamTagValueNode
+            || $node->value instanceof TypelessParamTagValueNode
+            || $node->name === '@param';
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyFactory.php
new file mode 100644
index 0000000..b744ed0
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyFactory.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\Property;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
+use Webmozart\Assert\Assert;
+
+use function is_string;
+use function trim;
+
+/**
+ * @internal This class is not part of the BC promise of this library.
+ */
+final class PropertyFactory implements PHPStanFactory
+{
+    private DescriptionFactory $descriptionFactory;
+    private TypeResolver $typeResolver;
+
+    public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
+    {
+        $this->descriptionFactory = $descriptionFactory;
+        $this->typeResolver = $typeResolver;
+    }
+
+    public function create(PhpDocTagNode $node, Context $context): Tag
+    {
+        $tagValue = $node->value;
+        Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
+
+        $description = $tagValue->getAttribute('description');
+        if (is_string($description) === false) {
+            $description = $tagValue->description;
+        }
+
+        return new Property(
+            trim($tagValue->propertyName, '$'),
+            $this->typeResolver->createType($tagValue->type, $context),
+            $this->descriptionFactory->create($description, $context)
+        );
+    }
+
+    public function supports(PhpDocTagNode $node, Context $context): bool
+    {
+        return $node->value instanceof PropertyTagValueNode && $node->name === '@property';
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyReadFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyReadFactory.php
new file mode 100644
index 0000000..b0898aa
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyReadFactory.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\PropertyRead;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
+use Webmozart\Assert\Assert;
+
+use function is_string;
+use function trim;
+
+/**
+ * @internal This class is not part of the BC promise of this library.
+ */
+final class PropertyReadFactory implements PHPStanFactory
+{
+    private DescriptionFactory $descriptionFactory;
+    private TypeResolver $typeResolver;
+
+    public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
+    {
+        $this->typeResolver = $typeResolver;
+        $this->descriptionFactory = $descriptionFactory;
+    }
+
+    public function create(PhpDocTagNode $node, Context $context): Tag
+    {
+        $tagValue = $node->value;
+        Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
+
+        $description = $tagValue->getAttribute('description');
+        if (is_string($description) === false) {
+            $description = $tagValue->description;
+        }
+
+        return new PropertyRead(
+            trim($tagValue->propertyName, '$'),
+            $this->typeResolver->createType($tagValue->type, $context),
+            $this->descriptionFactory->create($description, $context)
+        );
+    }
+
+    public function supports(PhpDocTagNode $node, Context $context): bool
+    {
+        return $node->value instanceof PropertyTagValueNode && $node->name === '@property-read';
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyWriteFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyWriteFactory.php
new file mode 100644
index 0000000..749b1ed
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/PropertyWriteFactory.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
+use Webmozart\Assert\Assert;
+
+use function is_string;
+use function trim;
+
+/**
+ * @internal This class is not part of the BC promise of this library.
+ */
+final class PropertyWriteFactory implements PHPStanFactory
+{
+    private DescriptionFactory $descriptionFactory;
+    private TypeResolver $typeResolver;
+
+    public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
+    {
+        $this->descriptionFactory = $descriptionFactory;
+        $this->typeResolver = $typeResolver;
+    }
+
+    public function create(PhpDocTagNode $node, Context $context): Tag
+    {
+        $tagValue = $node->value;
+        Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
+
+        $description = $tagValue->getAttribute('description');
+        if (is_string($description) === false) {
+            $description = $tagValue->description;
+        }
+
+        return new PropertyWrite(
+            trim($tagValue->propertyName, '$'),
+            $this->typeResolver->createType($tagValue->type, $context),
+            $this->descriptionFactory->create($description, $context)
+        );
+    }
+
+    public function supports(PhpDocTagNode $node, Context $context): bool
+    {
+        return $node->value instanceof PropertyTagValueNode && $node->name === '@property-write';
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ReturnFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ReturnFactory.php
new file mode 100644
index 0000000..4a17dc2
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/ReturnFactory.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\Return_;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
+use Webmozart\Assert\Assert;
+
+use function is_string;
+
+/**
+ * @internal This class is not part of the BC promise of this library.
+ */
+final class ReturnFactory implements PHPStanFactory
+{
+    private DescriptionFactory $descriptionFactory;
+    private TypeResolver $typeResolver;
+
+    public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
+    {
+        $this->descriptionFactory = $descriptionFactory;
+        $this->typeResolver = $typeResolver;
+    }
+
+    public function create(PhpDocTagNode $node, Context $context): Tag
+    {
+        $tagValue = $node->value;
+        Assert::isInstanceOf($tagValue, ReturnTagValueNode::class);
+
+        $description = $tagValue->getAttribute('description');
+        if (is_string($description) === false) {
+            $description = $tagValue->description;
+        }
+
+        return new Return_(
+            $this->typeResolver->createType($tagValue->type, $context),
+            $this->descriptionFactory->create($description, $context)
+        );
+    }
+
+    public function supports(PhpDocTagNode $node, Context $context): bool
+    {
+        return $node->value instanceof ReturnTagValueNode;
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/VarFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/VarFactory.php
new file mode 100644
index 0000000..479ceb2
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/VarFactory.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
+
+use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
+use phpDocumentor\Reflection\DocBlock\Tag;
+use phpDocumentor\Reflection\DocBlock\Tags\Var_;
+use phpDocumentor\Reflection\TypeResolver;
+use phpDocumentor\Reflection\Types\Context;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
+use Webmozart\Assert\Assert;
+
+use function is_string;
+use function trim;
+
+/**
+ * @internal This class is not part of the BC promise of this library.
+ */
+final class VarFactory implements PHPStanFactory
+{
+    private DescriptionFactory $descriptionFactory;
+    private TypeResolver $typeResolver;
+
+    public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
+    {
+        $this->descriptionFactory = $descriptionFactory;
+        $this->typeResolver = $typeResolver;
+    }
+
+    public function create(PhpDocTagNode $node, Context $context): Tag
+    {
+        $tagValue = $node->value;
+        Assert::isInstanceOf($tagValue, VarTagValueNode::class);
+
+        $description = $tagValue->getAttribute('description');
+        if (is_string($description) === false) {
+            $description = $tagValue->description;
+        }
+
+        return new Var_(
+            trim($tagValue->variableName, '$'),
+            $this->typeResolver->createType($tagValue->type, $context),
+            $this->descriptionFactory->create($description, $context)
+        );
+    }
+
+    public function supports(PhpDocTagNode $node, Context $context): bool
+    {
+        return $node->value instanceof VarTagValueNode;
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.php
index 9464434..ea5c16f 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.php
@@ -23,7 +23,7 @@ use function strlen;
 class AlignFormatter implements Formatter
 {
     /** @var int The maximum tag name length. */
-    protected $maxLen = 0;
+    protected int $maxLen = 0;
 
     /**
      * @param Tag[] $tags All tags that should later be aligned with the formatter.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.php
index 4e6abb8..c7d3cd5 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/InvalidTag.php
@@ -32,14 +32,11 @@ use function sprintf;
  */
 final class InvalidTag implements Tag
 {
-    /** @var string */
-    private $name;
+    private string $name;
 
-    /** @var string */
-    private $body;
+    private string $body;
 
-    /** @var Throwable|null */
-    private $throwable;
+    private ?Throwable $throwable = null;
 
     private function __construct(string $name, string $body)
     {
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php
index ee242e3..fcb6ec1 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php
@@ -24,11 +24,9 @@ use Webmozart\Assert\Assert;
  */
 final class Link extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'link';
+    protected string $name = 'link';
 
-    /** @var string */
-    private $link;
+    private string $link;
 
     /**
      * Initializes a link to a URL.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php
index f08bfff..471ebe9 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php
@@ -24,6 +24,7 @@ use phpDocumentor\Reflection\Types\Void_;
 use Webmozart\Assert\Assert;
 
 use function array_keys;
+use function array_map;
 use function explode;
 use function implode;
 use function is_string;
@@ -31,34 +32,33 @@ use function preg_match;
 use function sort;
 use function strpos;
 use function substr;
+use function trigger_error;
 use function trim;
 use function var_export;
 
+use const E_USER_DEPRECATED;
+
 /**
  * Reflection class for an {@}method in a Docblock.
  */
 final class Method extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'method';
+    protected string $name = 'method';
 
-    /** @var string */
-    private $methodName;
+    private string $methodName;
 
-    /**
-     * @phpstan-var array<int, array{name: string, type: Type}>
-     * @var array<int, array<string, Type|string>>
-     */
-    private $arguments;
+    private bool $isStatic;
 
-    /** @var bool */
-    private $isStatic;
+    private Type $returnType;
 
-    /** @var Type */
-    private $returnType;
+    private bool $returnsReference;
+
+    /** @var MethodParameter[] */
+    private array $parameters;
 
     /**
      * @param array<int, array<string, Type|string>> $arguments
+     * @param MethodParameter[] $parameters
      * @phpstan-param array<int, array{name: string, type: Type}|string> $arguments
      */
     public function __construct(
@@ -66,7 +66,9 @@ final class Method extends BaseTag implements Factory\StaticMethod
         array $arguments = [],
         ?Type $returnType = null,
         bool $static = false,
-        ?Description $description = null
+        ?Description $description = null,
+        bool $returnsReference = false,
+        ?array $parameters = null
     ) {
         Assert::stringNotEmpty($methodName);
 
@@ -74,19 +76,31 @@ final class Method extends BaseTag implements Factory\StaticMethod
             $returnType = new Void_();
         }
 
-        $this->methodName  = $methodName;
-        $this->arguments   = $this->filterArguments($arguments);
-        $this->returnType  = $returnType;
-        $this->isStatic    = $static;
-        $this->description = $description;
+        $arguments = $this->filterArguments($arguments);
+
+        $this->methodName       = $methodName;
+        $this->returnType       = $returnType;
+        $this->isStatic         = $static;
+        $this->description      = $description;
+        $this->returnsReference = $returnsReference;
+        $this->parameters = $parameters ?? $this->fromLegacyArguments($arguments);
     }
 
+    /**
+     * @deprecated Create using static factory is deprecated,
+     *  this method should not be called directly by library consumers
+     */
     public static function create(
         string $body,
         ?TypeResolver $typeResolver = null,
         ?DescriptionFactory $descriptionFactory = null,
         ?TypeContext $context = null
     ): ?self {
+        trigger_error(
+            'Create using static factory is deprecated, this method should not be called directly
+             by library consumers',
+            E_USER_DEPRECATED
+        );
         Assert::stringNotEmpty($body);
         Assert::notNull($typeResolver);
         Assert::notNull($descriptionFactory);
@@ -95,11 +109,13 @@ final class Method extends BaseTag implements Factory\StaticMethod
         // 2. optionally the keyword "static" followed by whitespace
         // 3. optionally a word with underscores followed by whitespace : as
         //    type for the return value
-        // 4. then optionally a word with underscores followed by () and
+        // 4. optionally an ampersand followed or not by whitespace : as
+        //    a reference
+        // 5. then optionally a word with underscores followed by () and
         //    whitespace : as method name as used by phpDocumentor
-        // 5. then a word with underscores, followed by ( and any character
+        // 6. then a word with underscores, followed by ( and any character
         //    until a ) and whitespace : as method name with signature
-        // 6. any remaining text : as description
+        // 7. any remaining text : as description
         if (
             !preg_match(
                 '/^
@@ -122,6 +138,11 @@ final class Method extends BaseTag implements Factory\StaticMethod
                     )
                     \s+
                 )?
+                # Returns reference
+                (?:
+                    (&)
+                    \s*
+                )?
                 # Method name
                 ([\w_]+)
                 # Arguments
@@ -139,7 +160,7 @@ final class Method extends BaseTag implements Factory\StaticMethod
             return null;
         }
 
-        [, $static, $returnType, $methodName, $argumentLines, $description] = $matches;
+        [, $static, $returnType, $returnsReference, $methodName, $argumentLines, $description] = $matches;
 
         $static = $static === 'static';
 
@@ -147,6 +168,8 @@ final class Method extends BaseTag implements Factory\StaticMethod
             $returnType = 'void';
         }
 
+        $returnsReference = $returnsReference === '&';
+
         $returnType  = $typeResolver->resolve($returnType, $context);
         $description = $descriptionFactory->create($description, $context);
 
@@ -172,7 +195,14 @@ final class Method extends BaseTag implements Factory\StaticMethod
             }
         }
 
-        return new static($methodName, $arguments, $returnType, $static, $description);
+        return new static(
+            $methodName,
+            $arguments,
+            $returnType,
+            $static,
+            $description,
+            $returnsReference
+        );
     }
 
     /**
@@ -184,12 +214,27 @@ final class Method extends BaseTag implements Factory\StaticMethod
     }
 
     /**
+     * @deprecated Method deprecated, use {@see self::getParameters()}
+     *
      * @return array<int, array<string, Type|string>>
      * @phpstan-return array<int, array{name: string, type: Type}>
      */
     public function getArguments(): array
     {
-        return $this->arguments;
+        trigger_error('Method deprecated, use ::getParameters()', E_USER_DEPRECATED);
+
+        return array_map(
+            static function (MethodParameter $methodParameter) {
+                return ['name' => $methodParameter->getName(), 'type' => $methodParameter->getType()];
+            },
+            $this->parameters
+        );
+    }
+
+    /** @return MethodParameter[] */
+    public function getParameters(): array
+    {
+        return $this->parameters;
     }
 
     /**
@@ -207,11 +252,19 @@ final class Method extends BaseTag implements Factory\StaticMethod
         return $this->returnType;
     }
 
+    public function returnsReference(): bool
+    {
+        return $this->returnsReference;
+    }
+
     public function __toString(): string
     {
         $arguments = [];
-        foreach ($this->arguments as $argument) {
-            $arguments[] = $argument['type'] . ' $' . $argument['name'];
+        foreach ($this->parameters as $parameter) {
+            $arguments[] = $parameter->getType() . ' ' .
+                ($parameter->isReference() ? '&' : '') .
+                ($parameter->isVariadic() ? '...' : '') .
+                '$' . $parameter->getName();
         }
 
         $argumentStr = '(' . implode(', ', $arguments) . ')';
@@ -228,9 +281,11 @@ final class Method extends BaseTag implements Factory\StaticMethod
 
         $methodName = $this->methodName;
 
+        $reference = $this->returnsReference ? '&' : '';
+
         return $static
             . ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '')
-            . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $methodName : '')
+            . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $reference . $methodName : '')
             . $argumentStr
             . ($description !== '' ? ' ' . $description : '');
     }
@@ -276,4 +331,28 @@ final class Method extends BaseTag implements Factory\StaticMethod
 
         return $argument;
     }
+
+    /**
+     * @param array{name: string, type: Type} $arguments
+     * @phpstan-param array<int, array{name: string, type: Type}> $arguments
+     *
+     * @return MethodParameter[]
+     */
+    private function fromLegacyArguments(array $arguments): array
+    {
+        trigger_error(
+            'Create method parameters via legacy format is deprecated add parameters via the constructor',
+            E_USER_DEPRECATED
+        );
+
+        return array_map(
+            static function ($arg) {
+                return new MethodParameter(
+                    $arg['name'],
+                    $arg['type']
+                );
+            },
+            $arguments
+        );
+    }
 }
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/MethodParameter.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/MethodParameter.php
new file mode 100644
index 0000000..0c85d41
--- /dev/null
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/MethodParameter.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\DocBlock\Tags;
+
+use phpDocumentor\Reflection\Type;
+
+final class MethodParameter
+{
+    private Type $type;
+
+    private bool $isReference;
+
+    private bool $isVariadic;
+
+    private string $name;
+
+    private ?string $defaultValue = null;
+
+    public function __construct(
+        string $name,
+        Type $type,
+        bool $isReference = false,
+        bool $isVariadic = false,
+        ?string $defaultValue = null
+    ) {
+        $this->type = $type;
+        $this->isReference = $isReference;
+        $this->isVariadic = $isVariadic;
+        $this->name = $name;
+        $this->defaultValue = $defaultValue;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    public function getType(): Type
+    {
+        return $this->type;
+    }
+
+    public function isReference(): bool
+    {
+        return $this->isReference;
+    }
+
+    public function isVariadic(): bool
+    {
+        return $this->isVariadic;
+    }
+
+    public function getDefaultValue(): ?string
+    {
+        return $this->defaultValue;
+    }
+}
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php
index 3399649..19b1b5f 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php
@@ -13,6 +13,7 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock\Tags;
 
+use Doctrine\Deprecations\Deprecation;
 use phpDocumentor\Reflection\DocBlock\Description;
 use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
 use phpDocumentor\Reflection\Type;
@@ -34,14 +35,13 @@ use const PREG_SPLIT_DELIM_CAPTURE;
  */
 final class Param extends TagWithType implements Factory\StaticMethod
 {
-    /** @var string|null */
-    private $variableName;
+    private ?string $variableName = null;
 
     /** @var bool determines whether this is a variadic argument */
-    private $isVariadic;
+    private bool $isVariadic;
 
     /** @var bool determines whether this is passed by reference */
-    private $isReference;
+    private bool $isReference;
 
     public function __construct(
         ?string $variableName,
@@ -58,12 +58,23 @@ final class Param extends TagWithType implements Factory\StaticMethod
         $this->isReference  = $isReference;
     }
 
+    /**
+     * @deprecated Create using static factory is deprecated,
+     *  this method should not be called directly by library consumers
+     */
     public static function create(
         string $body,
         ?TypeResolver $typeResolver = null,
         ?DescriptionFactory $descriptionFactory = null,
         ?TypeContext $context = null
     ): self {
+        Deprecation::triggerIfCalledFromOutside(
+            'phpdocumentor/reflection-docblock',
+            'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361',
+            'Create using static factory is deprecated, this method should not be called directly
+             by library consumers',
+        );
+
         Assert::stringNotEmpty($body);
         Assert::notNull($typeResolver);
         Assert::notNull($descriptionFactory);
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php
index 2521fb3..1287b6c 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php
@@ -13,6 +13,7 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock\Tags;
 
+use Doctrine\Deprecations\Deprecation;
 use phpDocumentor\Reflection\DocBlock\Description;
 use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
 use phpDocumentor\Reflection\Type;
@@ -34,8 +35,7 @@ use const PREG_SPLIT_DELIM_CAPTURE;
  */
 final class Property extends TagWithType implements Factory\StaticMethod
 {
-    /** @var string|null */
-    protected $variableName;
+    protected ?string $variableName = null;
 
     public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
     {
@@ -47,12 +47,23 @@ final class Property extends TagWithType implements Factory\StaticMethod
         $this->description  = $description;
     }
 
+    /**
+     * @deprecated Create using static factory is deprecated,
+     *  this method should not be called directly by library consumers
+     */
     public static function create(
         string $body,
         ?TypeResolver $typeResolver = null,
         ?DescriptionFactory $descriptionFactory = null,
         ?TypeContext $context = null
     ): self {
+        Deprecation::triggerIfCalledFromOutside(
+            'phpdocumentor/reflection-docblock',
+            'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361',
+            'Create using static factory is deprecated, this method should not be called directly
+             by library consumers',
+        );
+
         Assert::stringNotEmpty($body);
         Assert::notNull($typeResolver);
         Assert::notNull($descriptionFactory);
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php
index 9491b39..2cf8e61 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php
@@ -13,6 +13,7 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock\Tags;
 
+use Doctrine\Deprecations\Deprecation;
 use phpDocumentor\Reflection\DocBlock\Description;
 use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
 use phpDocumentor\Reflection\Type;
@@ -34,8 +35,7 @@ use const PREG_SPLIT_DELIM_CAPTURE;
  */
 final class PropertyRead extends TagWithType implements Factory\StaticMethod
 {
-    /** @var string|null */
-    protected $variableName;
+    protected ?string $variableName = null;
 
     public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
     {
@@ -47,12 +47,23 @@ final class PropertyRead extends TagWithType implements Factory\StaticMethod
         $this->description  = $description;
     }
 
+    /**
+     * @deprecated Create using static factory is deprecated,
+     *  this method should not be called directly by library consumers
+     */
     public static function create(
         string $body,
         ?TypeResolver $typeResolver = null,
         ?DescriptionFactory $descriptionFactory = null,
         ?TypeContext $context = null
     ): self {
+        Deprecation::triggerIfCalledFromOutside(
+            'phpdocumentor/reflection-docblock',
+            'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361',
+            'Create using static factory is deprecated, this method should not be called directly
+             by library consumers',
+        );
+
         Assert::stringNotEmpty($body);
         Assert::notNull($typeResolver);
         Assert::notNull($descriptionFactory);
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php
index 2bfdac6..57e7eb1 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php
@@ -13,6 +13,7 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock\Tags;
 
+use Doctrine\Deprecations\Deprecation;
 use phpDocumentor\Reflection\DocBlock\Description;
 use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
 use phpDocumentor\Reflection\Type;
@@ -34,8 +35,7 @@ use const PREG_SPLIT_DELIM_CAPTURE;
  */
 final class PropertyWrite extends TagWithType implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $variableName;
+    protected string $variableName;
 
     public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
     {
@@ -47,12 +47,23 @@ final class PropertyWrite extends TagWithType implements Factory\StaticMethod
         $this->description  = $description;
     }
 
+    /**
+     * @deprecated Create using static factory is deprecated,
+     *  this method should not be called directly by library consumers
+     */
     public static function create(
         string $body,
         ?TypeResolver $typeResolver = null,
         ?DescriptionFactory $descriptionFactory = null,
         ?TypeContext $context = null
     ): self {
+        Deprecation::triggerIfCalledFromOutside(
+            'phpdocumentor/reflection-docblock',
+            'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361',
+            'Create using static factory is deprecated, this method should not be called directly
+             by library consumers',
+        );
+
         Assert::stringNotEmpty($body);
         Assert::notNull($typeResolver);
         Assert::notNull($descriptionFactory);
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php
index 532003d..e4e7e31 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php
@@ -20,8 +20,7 @@ use phpDocumentor\Reflection\Fqsen as RealFqsen;
  */
 final class Fqsen implements Reference
 {
-    /** @var RealFqsen */
-    private $fqsen;
+    private RealFqsen $fqsen;
 
     public function __construct(RealFqsen $fqsen)
     {
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php
index edfba3f..5954a47 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php
@@ -20,8 +20,7 @@ use Webmozart\Assert\Assert;
  */
 final class Url implements Reference
 {
-    /** @var string */
-    private $uri;
+    private string $uri;
 
     public function __construct(string $uri)
     {
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php
index f021b60..f130760 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php
@@ -13,6 +13,7 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock\Tags;
 
+use Doctrine\Deprecations\Deprecation;
 use phpDocumentor\Reflection\DocBlock\Description;
 use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
 use phpDocumentor\Reflection\Type;
@@ -32,12 +33,23 @@ final class Return_ extends TagWithType implements Factory\StaticMethod
         $this->description = $description;
     }
 
+    /**
+     * @deprecated Create using static factory is deprecated,
+     *  this method should not be called directly by library consumers
+     */
     public static function create(
         string $body,
         ?TypeResolver $typeResolver = null,
         ?DescriptionFactory $descriptionFactory = null,
         ?TypeContext $context = null
     ): self {
+        Deprecation::triggerIfCalledFromOutside(
+            'phpdocumentor/reflection-docblock',
+            'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361',
+            'Create using static factory is deprecated, this method should not be called directly
+             by library consumers',
+        );
+
         Assert::notNull($typeResolver);
         Assert::notNull($descriptionFactory);
 
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php
index a194c7d..e7330e8 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php
@@ -33,11 +33,9 @@ use function preg_match;
  */
 final class See extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'see';
+    protected string $name = 'see';
 
-    /** @var Reference */
-    protected $refers;
+    protected Reference $refers;
 
     /**
      * Initializes this tag.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php
index 54af43c..24400fa 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php
@@ -25,8 +25,7 @@ use function preg_match;
  */
 final class Since extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'since';
+    protected string $name = 'since';
 
     /**
      * PCRE regular expression matching a version vector.
@@ -45,7 +44,7 @@ final class Since extends BaseTag implements Factory\StaticMethod
     )';
 
     /** @var string|null The version vector. */
-    private $version;
+    private ?string $version = null;
 
     public function __construct(?string $version = null, ?Description $description = null)
     {
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php
index 8b8c0fb..f6b4f57 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php
@@ -25,14 +25,13 @@ use function preg_match;
  */
 final class Source extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'source';
+    protected string $name = 'source';
 
     /** @var int The starting line, relative to the structural element's location. */
-    private $startingLine;
+    private int $startingLine;
 
     /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */
-    private $lineCount;
+    private ?int $lineCount = null;
 
     /**
      * @param int|string      $startingLine should be a to int convertible value
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php
index 158578b..271c41b 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/TagWithType.php
@@ -13,9 +13,11 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock\Tags;
 
+use InvalidArgumentException;
 use phpDocumentor\Reflection\Type;
 
 use function in_array;
+use function sprintf;
 use function strlen;
 use function substr;
 use function trim;
@@ -23,7 +25,7 @@ use function trim;
 abstract class TagWithType extends BaseTag
 {
     /** @var ?Type */
-    protected $type;
+    protected ?Type $type = null;
 
     /**
      * Returns the type section of the variable.
@@ -59,6 +61,12 @@ abstract class TagWithType extends BaseTag
             }
         }
 
+        if ($nestingLevel < 0 || $nestingLevel > 0) {
+            throw new InvalidArgumentException(
+                sprintf('Could not find type in %s, please check for malformed notations', $body)
+            );
+        }
+
         $description = trim(substr($body, strlen($type)));
 
         return [$type, $description];
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php
index b72f403..d9aa360 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php
@@ -29,11 +29,9 @@ use function explode;
  */
 final class Uses extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'uses';
+    protected string $name = 'uses';
 
-    /** @var Fqsen */
-    protected $refers;
+    protected Fqsen $refers;
 
     /**
      * Initializes this tag.
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php
index fa1f9db..0a79ab9 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php
@@ -13,6 +13,7 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection\DocBlock\Tags;
 
+use Doctrine\Deprecations\Deprecation;
 use phpDocumentor\Reflection\DocBlock\Description;
 use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
 use phpDocumentor\Reflection\Type;
@@ -34,8 +35,7 @@ use const PREG_SPLIT_DELIM_CAPTURE;
  */
 final class Var_ extends TagWithType implements Factory\StaticMethod
 {
-    /** @var string|null */
-    protected $variableName = '';
+    protected ?string $variableName = '';
 
     public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
     {
@@ -47,12 +47,22 @@ final class Var_ extends TagWithType implements Factory\StaticMethod
         $this->description  = $description;
     }
 
+    /**
+     * @deprecated Create using static factory is deprecated,
+     *  this method should not be called directly by library consumers
+     */
     public static function create(
         string $body,
         ?TypeResolver $typeResolver = null,
         ?DescriptionFactory $descriptionFactory = null,
         ?TypeContext $context = null
     ): self {
+        Deprecation::triggerIfCalledFromOutside(
+            'phpdocumentor/reflection-docblock',
+            'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361',
+            'Create using static factory is deprecated, this method should not be called directly
+             by library consumers',
+        );
         Assert::stringNotEmpty($body);
         Assert::notNull($typeResolver);
         Assert::notNull($descriptionFactory);
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php
index f46e4b8..1ed25d1 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php
@@ -25,8 +25,7 @@ use function preg_match;
  */
 final class Version extends BaseTag implements Factory\StaticMethod
 {
-    /** @var string */
-    protected $name = 'version';
+    protected string $name = 'version';
 
     /**
      * PCRE regular expression matching a version vector.
@@ -45,7 +44,7 @@ final class Version extends BaseTag implements Factory\StaticMethod
     )';
 
     /** @var string|null The version vector. */
-    private $version;
+    private ?string $version = null;
 
     public function __construct(?string $version = null, ?Description $description = null)
     {
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php
index 37f72dd..1f7faf1 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php
@@ -19,6 +19,15 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
 use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
 use phpDocumentor\Reflection\DocBlock\Tag;
 use phpDocumentor\Reflection\DocBlock\TagFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\AbstractPHPStanFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\MethodFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyReadFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyWriteFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\ReturnFactory;
+use phpDocumentor\Reflection\DocBlock\Tags\Factory\VarFactory;
 use Webmozart\Assert\Assert;
 
 use function array_shift;
@@ -35,11 +44,9 @@ use function trim;
 
 final class DocBlockFactory implements DocBlockFactoryInterface
 {
-    /** @var DocBlock\DescriptionFactory */
-    private $descriptionFactory;
+    private DocBlock\DescriptionFactory $descriptionFactory;
 
-    /** @var DocBlock\TagFactory */
-    private $tagFactory;
+    private TagFactory $tagFactory;
 
     /**
      * Initializes this factory with the required subcontractors.
@@ -47,22 +54,40 @@ final class DocBlockFactory implements DocBlockFactoryInterface
     public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory)
     {
         $this->descriptionFactory = $descriptionFactory;
-        $this->tagFactory         = $tagFactory;
+        $this->tagFactory = $tagFactory;
     }
 
     /**
      * Factory method for easy instantiation.
      *
-     * @param array<string, class-string<Tag>> $additionalTags
+     * @param array<string, class-string<Tag>|Factory> $additionalTags
      */
-    public static function createInstance(array $additionalTags = []): self
+    public static function createInstance(array $additionalTags = []): DocBlockFactoryInterface
     {
-        $fqsenResolver      = new FqsenResolver();
-        $tagFactory         = new StandardTagFactory($fqsenResolver);
+        $fqsenResolver = new FqsenResolver();
+        $tagFactory = new StandardTagFactory($fqsenResolver);
         $descriptionFactory = new DescriptionFactory($tagFactory);
+        $typeResolver = new TypeResolver($fqsenResolver);
+
+        $phpstanTagFactory = new AbstractPHPStanFactory(
+            new ParamFactory($typeResolver, $descriptionFactory),
+            new VarFactory($typeResolver, $descriptionFactory),
+            new ReturnFactory($typeResolver, $descriptionFactory),
+            new PropertyFactory($typeResolver, $descriptionFactory),
+            new PropertyReadFactory($typeResolver, $descriptionFactory),
+            new PropertyWriteFactory($typeResolver, $descriptionFactory),
+            new MethodFactory($typeResolver, $descriptionFactory)
+        );
 
         $tagFactory->addService($descriptionFactory);
-        $tagFactory->addService(new TypeResolver($fqsenResolver));
+        $tagFactory->addService($typeResolver);
+        $tagFactory->registerTagHandler('param', $phpstanTagFactory);
+        $tagFactory->registerTagHandler('var', $phpstanTagFactory);
+        $tagFactory->registerTagHandler('return', $phpstanTagFactory);
+        $tagFactory->registerTagHandler('property', $phpstanTagFactory);
+        $tagFactory->registerTagHandler('property-read', $phpstanTagFactory);
+        $tagFactory->registerTagHandler('property-write', $phpstanTagFactory);
+        $tagFactory->registerTagHandler('method', $phpstanTagFactory);
 
         $docBlockFactory = new self($descriptionFactory, $tagFactory);
         foreach ($additionalTags as $tagName => $tagHandler) {
@@ -111,9 +136,9 @@ final class DocBlockFactory implements DocBlockFactoryInterface
     }
 
     /**
-     * @param class-string<Tag> $handler
+     * @param class-string<Tag>|Factory $handler
      */
-    public function registerTagHandler(string $tagName, string $handler): void
+    public function registerTagHandler(string $tagName, $handler): void
     {
         $this->tagFactory->registerTagHandler($tagName, $handler);
     }
@@ -138,6 +163,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
     }
 
     // phpcs:disable
+
     /**
      * Splits the DocBlock into a template marker, summary, description and block of tags.
      *
@@ -149,7 +175,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
      *
      * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
      */
-    private function splitDocBlock(string $comment) : array
+    private function splitDocBlock(string $comment): array
     {
         // phpcs:enable
         // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
@@ -227,7 +253,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
     /**
      * Creates the tag objects.
      *
-     * @param string        $tags    Tag block to parse.
+     * @param string $tags Tag block to parse.
      * @param Types\Context $context Context of the parsed Tag
      *
      * @return DocBlock\Tag[]
@@ -240,7 +266,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
         }
 
         $result = [];
-        $lines  = $this->splitTagBlockIntoTagLines($tags);
+        $lines = $this->splitTagBlockIntoTagLines($tags);
         foreach ($lines as $key => $tagLine) {
             $result[$key] = $this->tagFactory->create(trim($tagLine), $context);
         }
diff --git a/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php
index 9995c0c..cacc382 100644
--- a/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php
+++ b/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php
@@ -14,7 +14,7 @@ interface DocBlockFactoryInterface
      *
      * @param array<string, class-string<Tag>> $additionalTags
      */
-    public static function createInstance(array $additionalTags = []): DocBlockFactory;
+    public static function createInstance(array $additionalTags = []): self;
 
     /**
      * @param string|object $docblock
diff --git a/vendor/phpdocumentor/type-resolver/.yamllint.yaml b/vendor/phpdocumentor/type-resolver/.yamllint.yaml
deleted file mode 100644
index 55695cd..0000000
--- a/vendor/phpdocumentor/type-resolver/.yamllint.yaml
+++ /dev/null
@@ -1,65 +0,0 @@
-extends: "default"
-
-ignore: |
-  .build/
-  .notes/
-  vendor/
-rules:
-  braces:
-    max-spaces-inside-empty: 0
-    max-spaces-inside: 1
-    min-spaces-inside-empty: 0
-    min-spaces-inside: 1
-  brackets:
-    max-spaces-inside-empty: 0
-    max-spaces-inside: 0
-    min-spaces-inside-empty: 0
-    min-spaces-inside: 0
-  colons:
-    max-spaces-after: 1
-    max-spaces-before: 0
-  commas:
-    max-spaces-after: 1
-    max-spaces-before: 0
-    min-spaces-after: 1
-  comments:
-    ignore-shebangs: true
-    min-spaces-from-content: 1
-    require-starting-space: true
-  comments-indentation: "enable"
-  document-end:
-    present: false
-  document-start:
-    present: false
-  indentation:
-    check-multi-line-strings: false
-    indent-sequences: true
-    spaces: 2
-  empty-lines:
-    max-end: 0
-    max-start: 0
-    max: 1
-  empty-values:
-    forbid-in-block-mappings: true
-    forbid-in-flow-mappings: true
-  hyphens:
-    max-spaces-after: 2
-  key-duplicates: "enable"
-  key-ordering: "disable"
-  line-length: "disable"
-  new-line-at-end-of-file: "enable"
-  new-lines:
-    type: "unix"
-  octal-values:
-    forbid-implicit-octal: true
-  quoted-strings:
-    quote-type: "double"
-  trailing-spaces: "enable"
-  truthy:
-    allowed-values:
-      - "false"
-      - "true"
-
-yaml-files:
-  - "*.yaml"
-  - "*.yml"
diff --git a/vendor/phpdocumentor/type-resolver/composer-require-checker.json b/vendor/phpdocumentor/type-resolver/composer-require-checker.json
deleted file mode 100644
index 137522d..0000000
--- a/vendor/phpdocumentor/type-resolver/composer-require-checker.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "symbol-whitelist" : [
-    "null", "true", "false",
-    "static", "self", "parent",
-    "array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "XSLTProcessor",
-    "T_NAME_QUALIFIED", "T_NAME_FULLY_QUALIFIED"
-  ],
-  "php-core-extensions" : [
-    "Core",
-    "pcre",
-    "Reflection",
-    "tokenizer",
-    "SPL",
-    "standard"
-  ]
-}
diff --git a/vendor/phpdocumentor/type-resolver/composer.json b/vendor/phpdocumentor/type-resolver/composer.json
index a7ae10f..c4afb7d 100644
--- a/vendor/phpdocumentor/type-resolver/composer.json
+++ b/vendor/phpdocumentor/type-resolver/composer.json
@@ -10,8 +10,10 @@
         }
     ],
     "require": {
-        "php": "^7.4 || ^8.0",
-        "phpdocumentor/reflection-common": "^2.0"
+        "php": "^7.3 || ^8.0",
+        "phpdocumentor/reflection-common": "^2.0",
+        "phpstan/phpdoc-parser": "^1.13",
+        "doctrine/deprecations": "^1.0"
     },
     "require-dev": {
         "ext-tokenizer": "*",
@@ -20,7 +22,8 @@
         "phpstan/phpstan-phpunit": "^1.1",
         "phpstan/extension-installer": "^1.1",
         "vimeo/psalm": "^4.25",
-        "rector/rector": "^0.13.9"
+        "rector/rector": "^0.13.9",
+        "phpbench/phpbench": "^1.2"
     },
     "autoload": {
         "psr-4": {
@@ -39,7 +42,7 @@
     },
     "config": {
         "platform": {
-            "php": "7.4.0"
+            "php": "7.3.0"
         },
         "allow-plugins": {
             "phpstan/extension-installer": true
diff --git a/vendor/phpdocumentor/type-resolver/rector.php b/vendor/phpdocumentor/type-resolver/rector.php
deleted file mode 100644
index b285d6c..0000000
--- a/vendor/phpdocumentor/type-resolver/rector.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
-use Rector\Config\RectorConfig;
-use Rector\Set\ValueObject\LevelSetList;
-
-return static function (RectorConfig $rectorConfig): void {
-    $rectorConfig->paths([
-        __DIR__ . '/src',
-        __DIR__ . '/tests/unit'
-    ]);
-
-    // register a single rule
-    $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
-    $rectorConfig->rule(Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector::class);
-    $rectorConfig->rule(Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector::class);
-    $rectorConfig->rule(Rector\PHPUnit\Rector\Class_\AddProphecyTraitRector::class);
-    $rectorConfig->importNames();
-
-    // define sets of rules
-    $rectorConfig->sets([
-        LevelSetList::UP_TO_PHP_74
-    ]);
-};
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShape.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShape.php
new file mode 100644
index 0000000..e0d0c73
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShape.php
@@ -0,0 +1,52 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\PseudoTypes;
+
+use phpDocumentor\Reflection\PseudoType;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\Types\Array_;
+use phpDocumentor\Reflection\Types\ArrayKey;
+use phpDocumentor\Reflection\Types\Mixed_;
+
+use function implode;
+
+/** @psalm-immutable */
+class ArrayShape implements PseudoType
+{
+    /** @var ArrayShapeItem[] */
+    private $items;
+
+    public function __construct(ArrayShapeItem ...$items)
+    {
+        $this->items = $items;
+    }
+
+    /**
+     * @return ArrayShapeItem[]
+     */
+    public function getItems(): array
+    {
+        return $this->items;
+    }
+
+    public function underlyingType(): Type
+    {
+        return new Array_(new Mixed_(), new ArrayKey());
+    }
+
+    public function __toString(): string
+    {
+        return 'array{' . implode(', ', $this->items) . '}';
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShapeItem.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShapeItem.php
new file mode 100644
index 0000000..a9756bb
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ArrayShapeItem.php
@@ -0,0 +1,65 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\PseudoTypes;
+
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\Types\Mixed_;
+
+use function sprintf;
+
+final class ArrayShapeItem
+{
+    /** @var string|null */
+    private $key;
+    /** @var Type */
+    private $value;
+    /** @var bool */
+    private $optional;
+
+    public function __construct(?string $key, ?Type $value, bool $optional)
+    {
+        $this->key = $key;
+        $this->value = $value ?? new Mixed_();
+        $this->optional = $optional;
+    }
+
+    public function getKey(): ?string
+    {
+        return $this->key;
+    }
+
+    public function getValue(): Type
+    {
+        return $this->value;
+    }
+
+    public function isOptional(): bool
+    {
+        return $this->optional;
+    }
+
+    public function __toString(): string
+    {
+        if ($this->key !== null) {
+            return sprintf(
+                '%s%s: %s',
+                $this->key,
+                $this->optional ? '?' : '',
+                (string) $this->value
+            );
+        }
+
+        return (string) $this->value;
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ConstExpression.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ConstExpression.php
new file mode 100644
index 0000000..7334933
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/ConstExpression.php
@@ -0,0 +1,55 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\PseudoTypes;
+
+use phpDocumentor\Reflection\PseudoType;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\Types\Mixed_;
+
+use function sprintf;
+
+/** @psalm-immutable */
+final class ConstExpression implements PseudoType
+{
+    /** @var Type */
+    private $owner;
+    /** @var string */
+    private $expression;
+
+    public function __construct(Type $owner, string $expression)
+    {
+        $this->owner = $owner;
+        $this->expression = $expression;
+    }
+
+    public function getOwner(): Type
+    {
+        return $this->owner;
+    }
+
+    public function getExpression(): string
+    {
+        return $this->expression;
+    }
+
+    public function underlyingType(): Type
+    {
+        return new Mixed_();
+    }
+
+    public function __toString(): string
+    {
+        return sprintf('%s::%s', (string) $this->owner, $this->expression);
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/FloatValue.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/FloatValue.php
new file mode 100644
index 0000000..0dbbce0
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/FloatValue.php
@@ -0,0 +1,45 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\PseudoTypes;
+
+use phpDocumentor\Reflection\PseudoType;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\Types\Float_;
+
+/** @psalm-immutable */
+class FloatValue implements PseudoType
+{
+    /** @var float */
+    private $value;
+
+    public function __construct(float $value)
+    {
+        $this->value = $value;
+    }
+
+    public function getValue(): float
+    {
+        return $this->value;
+    }
+
+    public function underlyingType(): Type
+    {
+        return new Float_();
+    }
+
+    public function __toString(): string
+    {
+        return (string) $this->value;
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerRange.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerRange.php
index 37f774d..c5a3bc5 100644
--- a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerRange.php
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerRange.php
@@ -24,9 +24,11 @@ use phpDocumentor\Reflection\Types\Integer;
  */
 final class IntegerRange extends Integer implements PseudoType
 {
-    private string $minValue;
+    /** @var string */
+    private $minValue;
 
-    private string $maxValue;
+    /** @var string */
+    private $maxValue;
 
     public function __construct(string $minValue, string $maxValue)
     {
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerValue.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerValue.php
new file mode 100644
index 0000000..51f0d34
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/IntegerValue.php
@@ -0,0 +1,45 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\PseudoTypes;
+
+use phpDocumentor\Reflection\PseudoType;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\Types\Integer;
+
+/** @psalm-immutable */
+final class IntegerValue implements PseudoType
+{
+    /** @var int */
+    private $value;
+
+    public function __construct(int $value)
+    {
+        $this->value = $value;
+    }
+
+    public function getValue(): int
+    {
+        return $this->value;
+    }
+
+    public function underlyingType(): Type
+    {
+        return new Integer();
+    }
+
+    public function __toString(): string
+    {
+        return (string) $this->value;
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/NonEmptyList.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/NonEmptyList.php
new file mode 100644
index 0000000..dd6a653
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/NonEmptyList.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * This file is part of phpDocumentor.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * @link      http://phpdoc.org
+ */
+
+namespace phpDocumentor\Reflection\PseudoTypes;
+
+use phpDocumentor\Reflection\PseudoType;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\Types\Array_;
+use phpDocumentor\Reflection\Types\Integer;
+use phpDocumentor\Reflection\Types\Mixed_;
+
+/**
+ * Value Object representing the type 'non-empty-list'.
+ *
+ * @psalm-immutable
+ */
+final class NonEmptyList extends Array_ implements PseudoType
+{
+    public function underlyingType(): Type
+    {
+        return new Array_();
+    }
+
+    public function __construct(?Type $valueType = null)
+    {
+        parent::__construct($valueType, new Integer());
+    }
+
+    /**
+     * Returns a rendered output of the Type as it would be used in a DocBlock.
+     */
+    public function __toString(): string
+    {
+        if ($this->valueType instanceof Mixed_) {
+            return 'non-empty-list';
+        }
+
+        return 'non-empty-list<' . $this->valueType . '>';
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/PseudoTypes/StringValue.php b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/StringValue.php
new file mode 100644
index 0000000..98e1046
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/PseudoTypes/StringValue.php
@@ -0,0 +1,47 @@
+<?php
+/*
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ *
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\PseudoTypes;
+
+use phpDocumentor\Reflection\PseudoType;
+use phpDocumentor\Reflection\Type;
+use phpDocumentor\Reflection\Types\String_;
+
+use function sprintf;
+
+/** @psalm-immutable */
+class StringValue implements PseudoType
+{
+    /** @var string */
+    private $value;
+
+    public function __construct(string $value)
+    {
+        $this->value = $value;
+    }
+
+    public function getValue(): string
+    {
+        return $this->value;
+    }
+
+    public function underlyingType(): Type
+    {
+        return new String_();
+    }
+
+    public function __toString(): string
+    {
+        return sprintf('"%s"', $this->value);
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/TypeResolver.php b/vendor/phpdocumentor/type-resolver/src/TypeResolver.php
index e5695b8..0c558c9 100644
--- a/vendor/phpdocumentor/type-resolver/src/TypeResolver.php
+++ b/vendor/phpdocumentor/type-resolver/src/TypeResolver.php
@@ -13,27 +13,36 @@ declare(strict_types=1);
 
 namespace phpDocumentor\Reflection;
 
-use ArrayIterator;
+use Doctrine\Deprecations\Deprecation;
 use InvalidArgumentException;
+use phpDocumentor\Reflection\PseudoTypes\ArrayShape;
+use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem;
 use phpDocumentor\Reflection\PseudoTypes\CallableString;
+use phpDocumentor\Reflection\PseudoTypes\ConstExpression;
 use phpDocumentor\Reflection\PseudoTypes\False_;
+use phpDocumentor\Reflection\PseudoTypes\FloatValue;
 use phpDocumentor\Reflection\PseudoTypes\HtmlEscapedString;
 use phpDocumentor\Reflection\PseudoTypes\IntegerRange;
+use phpDocumentor\Reflection\PseudoTypes\IntegerValue;
 use phpDocumentor\Reflection\PseudoTypes\List_;
 use phpDocumentor\Reflection\PseudoTypes\LiteralString;
 use phpDocumentor\Reflection\PseudoTypes\LowercaseString;
 use phpDocumentor\Reflection\PseudoTypes\NegativeInteger;
+use phpDocumentor\Reflection\PseudoTypes\NonEmptyList;
 use phpDocumentor\Reflection\PseudoTypes\NonEmptyLowercaseString;
 use phpDocumentor\Reflection\PseudoTypes\NonEmptyString;
 use phpDocumentor\Reflection\PseudoTypes\Numeric_;
 use phpDocumentor\Reflection\PseudoTypes\NumericString;
 use phpDocumentor\Reflection\PseudoTypes\PositiveInteger;
+use phpDocumentor\Reflection\PseudoTypes\StringValue;
 use phpDocumentor\Reflection\PseudoTypes\TraitString;
 use phpDocumentor\Reflection\PseudoTypes\True_;
+use phpDocumentor\Reflection\Types\AggregatedType;
 use phpDocumentor\Reflection\Types\Array_;
 use phpDocumentor\Reflection\Types\ArrayKey;
 use phpDocumentor\Reflection\Types\Boolean;
 use phpDocumentor\Reflection\Types\Callable_;
+use phpDocumentor\Reflection\Types\CallableParameter;
 use phpDocumentor\Reflection\Types\ClassString;
 use phpDocumentor\Reflection\Types\Collection;
 use phpDocumentor\Reflection\Types\Compound;
@@ -57,51 +66,56 @@ use phpDocumentor\Reflection\Types\Static_;
 use phpDocumentor\Reflection\Types\String_;
 use phpDocumentor\Reflection\Types\This;
 use phpDocumentor\Reflection\Types\Void_;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
+use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
+use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
+use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
+use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode;
+use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\Parser\ConstExprParser;
+use PHPStan\PhpDocParser\Parser\ParserException;
+use PHPStan\PhpDocParser\Parser\TokenIterator;
+use PHPStan\PhpDocParser\Parser\TypeParser;
 use RuntimeException;
 
+use function array_filter;
 use function array_key_exists;
-use function array_key_last;
-use function array_pop;
-use function array_values;
+use function array_map;
+use function array_reverse;
 use function class_exists;
 use function class_implements;
-use function count;
-use function current;
+use function get_class;
 use function in_array;
-use function is_numeric;
-use function preg_split;
+use function sprintf;
 use function strpos;
 use function strtolower;
 use function trim;
 
-use const PREG_SPLIT_DELIM_CAPTURE;
-use const PREG_SPLIT_NO_EMPTY;
-
 final class TypeResolver
 {
-    /** @var string Definition of the ARRAY operator for types */
-    private const OPERATOR_ARRAY = '[]';
-
     /** @var string Definition of the NAMESPACE operator in PHP */
     private const OPERATOR_NAMESPACE = '\\';
 
-    /** @var int the iterator parser is inside a compound context */
-    private const PARSER_IN_COMPOUND = 0;
-
-    /** @var int the iterator parser is inside a nullable expression context */
-    private const PARSER_IN_NULLABLE = 1;
-
-    /** @var int the iterator parser is inside an array expression context */
-    private const PARSER_IN_ARRAY_EXPRESSION = 2;
-
-    /** @var int the iterator parser is inside a collection expression context */
-    private const PARSER_IN_COLLECTION_EXPRESSION = 3;
-
     /**
      * @var array<string, string> List of recognized keywords and unto which Value Object they map
      * @psalm-var array<string, class-string<Type>>
      */
-    private array $keywords = [
+    private $keywords = [
         'string' => String_::class,
         'class-string' => ClassString::class,
         'interface-string' => InterfaceString::class,
@@ -142,10 +156,24 @@ final class TypeResolver
         'iterable' => Iterable_::class,
         'never' => Never_::class,
         'list' => List_::class,
+        'non-empty-list' => NonEmptyList::class,
     ];
 
-    /** @psalm-readonly */
-    private FqsenResolver $fqsenResolver;
+    /**
+     * @psalm-readonly
+     * @var FqsenResolver
+     */
+    private $fqsenResolver;
+    /**
+     * @psalm-readonly
+     * @var TypeParser
+     */
+    private $typeParser;
+    /**
+     * @psalm-readonly
+     * @var Lexer
+     */
+    private $lexer;
 
     /**
      * Initializes this TypeResolver with the means to create and resolve Fqsen objects.
@@ -153,6 +181,8 @@ final class TypeResolver
     public function __construct(?FqsenResolver $fqsenResolver = null)
     {
         $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver();
+        $this->typeParser = new TypeParser(new ConstExprParser());
+        $this->lexer = new Lexer();
     }
 
     /**
@@ -165,9 +195,9 @@ final class TypeResolver
      * This method only works as expected if the namespace and aliases are set;
      * no dynamic reflection is being performed here.
      *
+     * @uses Context::getNamespace()        to determine with what to prefix the type name.
      * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be
      * replaced with another namespace.
-     * @uses Context::getNamespace()        to determine with what to prefix the type name.
      *
      * @param string $type The relative or absolute type.
      */
@@ -182,178 +212,219 @@ final class TypeResolver
             $context = new Context('');
         }
 
-        // split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)`, `[]`, '<', '>' and type names
-        $tokens = preg_split(
-            '/(\\||\\?|<|>|&|, ?|\\(|\\)|\\[\\]+)/',
-            $type,
-            -1,
-            PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
-        );
+        $tokens = $this->lexer->tokenize($type);
+        $tokenIterator = new TokenIterator($tokens);
 
-        if ($tokens === false) {
-            throw new InvalidArgumentException('Unable to split the type string "' . $type . '" into tokens');
-        }
+        $ast = $this->parse($tokenIterator);
+        $type = $this->createType($ast, $context);
 
-        /** @var ArrayIterator<int, string|null> $tokenIterator */
-        $tokenIterator = new ArrayIterator($tokens);
-
-        return $this->parseTypes($tokenIterator, $context, self::PARSER_IN_COMPOUND);
+        return $this->tryParseRemainingCompoundTypes($tokenIterator, $context, $type);
     }
 
-    /**
-     * Analyse each tokens and creates types
-     *
-     * @param ArrayIterator<int, string|null> $tokens        the iterator on tokens
-     * @param int                        $parserContext on of self::PARSER_* constants, indicating
-     * the context where we are in the parsing
-     */
-    private function parseTypes(ArrayIterator $tokens, Context $context, int $parserContext): Type
+    public function createType(?TypeNode $type, Context $context): Type
     {
-        $types = [];
-        $token = '';
-        $compoundToken = '|';
-        while ($tokens->valid()) {
-            $token = $tokens->current();
-            if ($token === null) {
-                throw new RuntimeException(
-                    'Unexpected nullable character'
-                );
-            }
+        if ($type === null) {
+            return new Mixed_();
+        }
 
-            if ($token === '|' || $token === '&') {
-                if (count($types) === 0) {
+        switch (get_class($type)) {
+            case ArrayTypeNode::class:
+                return new Array_(
+                    $this->createType($type->type, $context)
+                );
+
+            case ArrayShapeNode::class:
+                return new ArrayShape(
+                    ...array_map(
+                        function (ArrayShapeItemNode $item) use ($context): ArrayShapeItem {
+                            return new ArrayShapeItem(
+                                (string) $item->keyName,
+                                $this->createType($item->valueType, $context),
+                                $item->optional
+                            );
+                        },
+                        $type->items
+                    )
+                );
+
+            case CallableTypeNode::class:
+                return $this->createFromCallable($type, $context);
+
+            case ConstTypeNode::class:
+                return $this->createFromConst($type, $context);
+
+            case GenericTypeNode::class:
+                return $this->createFromGeneric($type, $context);
+
+            case IdentifierTypeNode::class:
+                return $this->resolveSingleType($type->name, $context);
+
+            case IntersectionTypeNode::class:
+                return new Intersection(
+                    array_filter(
+                        array_map(
+                            function (TypeNode $nestedType) use ($context): Type {
+                                $type = $this->createType($nestedType, $context);
+                                if ($type instanceof AggregatedType) {
+                                    return new Expression($type);
+                                }
+
+                                return $type;
+                            },
+                            $type->types
+                        )
+                    )
+                );
+
+            case NullableTypeNode::class:
+                $nestedType = $this->createType($type->type, $context);
+
+                return new Nullable($nestedType);
+
+            case UnionTypeNode::class:
+                return new Compound(
+                    array_filter(
+                        array_map(
+                            function (TypeNode $nestedType) use ($context): Type {
+                                $type = $this->createType($nestedType, $context);
+                                if ($type instanceof AggregatedType) {
+                                    return new Expression($type);
+                                }
+
+                                return $type;
+                            },
+                            $type->types
+                        )
+                    )
+                );
+
+            case ThisTypeNode::class:
+                return new This();
+
+            case ConditionalTypeNode::class:
+            case ConditionalTypeForParameterNode::class:
+            case OffsetAccessTypeNode::class:
+            default:
+                return new Mixed_();
+        }
+    }
+
+    private function createFromGeneric(GenericTypeNode $type, Context $context): Type
+    {
+        switch (strtolower($type->type->name)) {
+            case 'array':
+                return $this->createArray($type->genericTypes, $context);
+
+            case 'class-string':
+                $subType = $this->createType($type->genericTypes[0], $context);
+                if (!$subType instanceof Object_ || $subType->getFqsen() === null) {
                     throw new RuntimeException(
-                        'A type is missing before a type separator'
+                        $subType . ' is not a class string'
                     );
                 }
 
-                if (
-                    !in_array($parserContext, [
-                        self::PARSER_IN_COMPOUND,
-                        self::PARSER_IN_ARRAY_EXPRESSION,
-                        self::PARSER_IN_COLLECTION_EXPRESSION,
-                        self::PARSER_IN_NULLABLE,
-                    ], true)
-                ) {
+                return new ClassString(
+                    $subType->getFqsen()
+                );
+
+            case 'interface-string':
+                $subType = $this->createType($type->genericTypes[0], $context);
+                if (!$subType instanceof Object_ || $subType->getFqsen() === null) {
                     throw new RuntimeException(
-                        'Unexpected type separator'
+                        $subType . ' is not a class string'
                     );
                 }
 
-                $compoundToken = $token;
-                $tokens->next();
-            } elseif ($token === '?') {
-                if (
-                    !in_array($parserContext, [
-                        self::PARSER_IN_COMPOUND,
-                        self::PARSER_IN_ARRAY_EXPRESSION,
-                        self::PARSER_IN_COLLECTION_EXPRESSION,
-                        self::PARSER_IN_NULLABLE,
-                    ], true)
-                ) {
-                    throw new RuntimeException(
-                        'Unexpected nullable character'
-                    );
-                }
-
-                $tokens->next();
-                $type    = $this->parseTypes($tokens, $context, self::PARSER_IN_NULLABLE);
-                $types[] = new Nullable($type);
-            } elseif ($token === '(') {
-                $tokens->next();
-                $type = $this->parseTypes($tokens, $context, self::PARSER_IN_ARRAY_EXPRESSION);
-
-                $token = $tokens->current();
-                if ($token === null) { // Someone did not properly close their array expression ..
-                    break;
-                }
-
-                $tokens->next();
-
-                $resolvedType = new Expression($type);
-
-                $types[] = $resolvedType;
-            } elseif ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION && isset($token[0]) && $token[0] === ')') {
-                break;
-            } elseif ($token === '<') {
-                if (count($types) === 0) {
-                    throw new RuntimeException(
-                        'Unexpected collection operator "<", class name is missing'
-                    );
-                }
-
-                $classType = array_pop($types);
-                if ($classType !== null) {
-                    if ((string) $classType === 'class-string') {
-                        $types[] = $this->resolveClassString($tokens, $context);
-                    } elseif ((string) $classType === 'int') {
-                        $types[] = $this->resolveIntRange($tokens);
-                    } elseif ((string) $classType === 'interface-string') {
-                        $types[] = $this->resolveInterfaceString($tokens, $context);
-                    } else {
-                        $types[] = $this->resolveCollection($tokens, $classType, $context);
-                    }
-                }
-
-                $tokens->next();
-            } elseif (
-                $parserContext === self::PARSER_IN_COLLECTION_EXPRESSION
-                && ($token === '>' || trim($token) === ',')
-            ) {
-                break;
-            } elseif ($token === self::OPERATOR_ARRAY) {
-                $last = array_key_last($types);
-                if ($last === null) {
-                    throw new InvalidArgumentException('Unexpected array operator');
-                }
-
-                $lastItem = $types[$last];
-                if ($lastItem instanceof Expression) {
-                    $lastItem = $lastItem->getValueType();
-                }
-
-                $types[$last] = new Array_($lastItem);
-
-                $tokens->next();
-            } else {
-                $types[] = $this->resolveSingleType($token, $context);
-                $tokens->next();
-            }
-        }
-
-        if ($token === '|' || $token === '&') {
-            throw new RuntimeException(
-                'A type is missing after a type separator'
-            );
-        }
-
-        if (count($types) === 0) {
-            if ($parserContext === self::PARSER_IN_NULLABLE) {
-                throw new RuntimeException(
-                    'A type is missing after a nullable character'
+                return new InterfaceString(
+                    $subType->getFqsen()
                 );
-            }
 
-            if ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION) {
-                throw new RuntimeException(
-                    'A type is missing in an array expression'
+            case 'list':
+                return new List_(
+                    $this->createType($type->genericTypes[0], $context)
                 );
-            }
 
-            if ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION) {
-                throw new RuntimeException(
-                    'A type is missing in a collection expression'
+            case 'non-empty-list':
+                return new NonEmptyList(
+                    $this->createType($type->genericTypes[0], $context)
                 );
-            }
-        } elseif (count($types) === 1) {
-            return current($types);
-        }
 
-        if ($compoundToken === '|') {
-            return new Compound(array_values($types));
-        }
+            case 'int':
+                if (isset($type->genericTypes[1]) === false) {
+                    throw new RuntimeException('int<min,max> has not the correct format');
+                }
 
-        return new Intersection(array_values($types));
+                return new IntegerRange((string) $type->genericTypes[0], (string) $type->genericTypes[1]);
+
+            case 'iterable':
+                return new Iterable_(
+                    ...array_reverse(
+                        array_map(
+                            function (TypeNode $genericType) use ($context): Type {
+                                return $this->createType($genericType, $context);
+                            },
+                            $type->genericTypes
+                        )
+                    )
+                );
+
+            default:
+                $collectionType = $this->createType($type->type, $context);
+                if ($collectionType instanceof Object_ === false) {
+                    throw new RuntimeException(sprintf('%s is not a collection', (string) $collectionType));
+                }
+
+                return new Collection(
+                    $collectionType->getFqsen(),
+                    ...array_reverse(
+                        array_map(
+                            function (TypeNode $genericType) use ($context): Type {
+                                return $this->createType($genericType, $context);
+                            },
+                            $type->genericTypes
+                        )
+                    )
+                );
+        }
+    }
+
+    private function createFromCallable(CallableTypeNode $type, Context $context): Callable_
+    {
+        return new Callable_(array_map(
+            function (CallableTypeParameterNode $param) use ($context): CallableParameter {
+                return new CallableParameter(
+                    $this->createType($param->type, $context),
+                    $param->parameterName !== '' ? trim($param->parameterName, '$') : null,
+                    $param->isReference,
+                    $param->isVariadic,
+                    $param->isOptional
+                );
+            },
+            $type->parameters
+        ), $this->createType($type->returnType, $context));
+    }
+
+    private function createFromConst(ConstTypeNode $type, Context $context): Type
+    {
+        switch (true) {
+            case $type->constExpr instanceof ConstExprIntegerNode:
+                return new IntegerValue((int) $type->constExpr->value);
+
+            case $type->constExpr instanceof ConstExprFloatNode:
+                return new FloatValue((float) $type->constExpr->value);
+
+            case $type->constExpr instanceof ConstExprStringNode:
+                return new StringValue($type->constExpr->value);
+
+            case $type->constExpr instanceof ConstFetchNode:
+                return new ConstExpression(
+                    $this->resolve($type->constExpr->className, $context),
+                    $type->constExpr->name
+                );
+
+            default:
+                throw new RuntimeException(sprintf('Unsupported constant type %s', get_class($type)));
+        }
     }
 
     /**
@@ -475,244 +546,89 @@ final class TypeResolver
         return new Object_($this->fqsenResolver->resolve($type, $context));
     }
 
-    /**
-     * Resolves class string
-     *
-     * @param ArrayIterator<int, (string|null)> $tokens
-     */
-    private function resolveClassString(ArrayIterator $tokens, Context $context): Type
+    /** @param TypeNode[] $typeNodes */
+    private function createArray(array $typeNodes, Context $context): Array_
     {
-        $tokens->next();
+        $types = array_reverse(
+            array_map(
+                function (TypeNode $node) use ($context): Type {
+                    return $this->createType($node, $context);
+                },
+                $typeNodes
+            )
+        );
 
-        $classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
-
-        if (!$classType instanceof Object_ || $classType->getFqsen() === null) {
-            throw new RuntimeException(
-                $classType . ' is not a class string'
-            );
+        if (isset($types[1]) === false) {
+            return new Array_(...$types);
         }
 
-        $token = $tokens->current();
-        if ($token !== '>') {
-            if (empty($token)) {
-                throw new RuntimeException(
-                    'class-string: ">" is missing'
-                );
+        if ($this->validArrayKeyType($types[1]) || $types[1] instanceof ArrayKey) {
+            return new Array_(...$types);
+        }
+
+        if ($types[1] instanceof Compound && $types[1]->getIterator()->count() === 2) {
+            if ($this->validArrayKeyType($types[1]->get(0)) && $this->validArrayKeyType($types[1]->get(1))) {
+                return new Array_(...$types);
             }
-
-            throw new RuntimeException(
-                'Unexpected character "' . $token . '", ">" is missing'
-            );
         }
 
-        return new ClassString($classType->getFqsen());
+        throw new RuntimeException('An array can have only integers or strings as keys');
+    }
+
+    private function validArrayKeyType(?Type $type): bool
+    {
+        return $type instanceof String_ || $type instanceof Integer;
+    }
+
+    private function parse(TokenIterator $tokenIterator): TypeNode
+    {
+        try {
+            $ast = $this->typeParser->parse($tokenIterator);
+        } catch (ParserException $e) {
+            throw new RuntimeException($e->getMessage(), 0, $e);
+        }
+
+        return $ast;
     }
 
     /**
-     * Resolves integer ranges
+     * Will try to parse unsupported type notations by phpstan
      *
-     * @param ArrayIterator<int, (string|null)> $tokens
+     * The phpstan parser doesn't support the illegal nullable combinations like this library does.
+     * This method will warn the user about those notations but for bc purposes we will still have it here.
      */
-    private function resolveIntRange(ArrayIterator $tokens): Type
+    private function tryParseRemainingCompoundTypes(TokenIterator $tokenIterator, Context $context, Type $type): Type
     {
-        $tokens->next();
-
-        $token = '';
-        $minValue = null;
-        $maxValue = null;
-        $commaFound = false;
-        $tokenCounter = 0;
-        while ($tokens->valid()) {
-            $tokenCounter++;
-            $token = $tokens->current();
-            if ($token === null) {
-                throw new RuntimeException(
-                    'Unexpected nullable character'
-                );
-            }
-
-            $token = trim($token);
-
-            if ($token === '>') {
-                break;
-            }
-
-            if ($token === ',') {
-                $commaFound = true;
-            }
-
-            if ($commaFound === false && $minValue === null) {
-                if (is_numeric($token) || $token === 'max' || $token === 'min') {
-                    $minValue = $token;
-                }
-            }
-
-            if ($commaFound === true && $maxValue === null) {
-                if (is_numeric($token) || $token === 'max' || $token === 'min') {
-                    $maxValue = $token;
-                }
-            }
-
-            $tokens->next();
-        }
-
-        if ($token !== '>') {
-            if (empty($token)) {
-                throw new RuntimeException(
-                    'interface-string: ">" is missing'
-                );
-            }
-
-            throw new RuntimeException(
-                'Unexpected character "' . $token . '", ">" is missing'
-            );
-        }
-
-        if ($minValue === null || $maxValue === null || $tokenCounter > 4) {
-            throw new RuntimeException(
-                'int<min,max> has not the correct format'
-            );
-        }
-
-        return new IntegerRange($minValue, $maxValue);
-    }
-
-    /**
-     * Resolves class string
-     *
-     * @param ArrayIterator<int, (string|null)> $tokens
-     */
-    private function resolveInterfaceString(ArrayIterator $tokens, Context $context): Type
-    {
-        $tokens->next();
-
-        $classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
-
-        if (!$classType instanceof Object_ || $classType->getFqsen() === null) {
-            throw new RuntimeException(
-                $classType . ' is not a interface string'
-            );
-        }
-
-        $token = $tokens->current();
-        if ($token !== '>') {
-            if (empty($token)) {
-                throw new RuntimeException(
-                    'interface-string: ">" is missing'
-                );
-            }
-
-            throw new RuntimeException(
-                'Unexpected character "' . $token . '", ">" is missing'
-            );
-        }
-
-        return new InterfaceString($classType->getFqsen());
-    }
-
-    /**
-     * Resolves the collection values and keys
-     *
-     * @param ArrayIterator<int, (string|null)> $tokens
-     *
-     * @return Array_|Iterable_|Collection
-     */
-    private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context): Type
-    {
-        $isArray    = ((string) $classType === 'array');
-        $isIterable = ((string) $classType === 'iterable');
-        $isList     = ((string) $classType === 'list');
-
-        // allow only "array", "iterable" or class name before "<"
         if (
-            !$isArray && !$isIterable && !$isList
-            && (!$classType instanceof Object_ || $classType->getFqsen() === null)
+            $tokenIterator->isCurrentTokenType(Lexer::TOKEN_UNION) ||
+            $tokenIterator->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)
         ) {
-            throw new RuntimeException(
-                $classType . ' is not a collection'
+            Deprecation::trigger(
+                'phpdocumentor/type-resolver',
+                'https://github.com/phpDocumentor/TypeResolver/issues/184',
+                'Legacy nullable type detected, please update your code as
+                you are using nullable types in a docblock. support will be removed in v2.0.0'
             );
         }
 
-        $tokens->next();
-
-        $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
-        $keyType   = null;
-
-        $token = $tokens->current();
-        if ($token !== null && trim($token) === ',' && !$isList) {
-            // if we have a comma, then we just parsed the key type, not the value type
-            $keyType = $valueType;
-            if ($isArray) {
-                // check the key type for an "array" collection. We allow only
-                // strings or integers.
-                if (
-                    !$keyType instanceof ArrayKey &&
-                    !$keyType instanceof String_ &&
-                    !$keyType instanceof Integer &&
-                    !$keyType instanceof Compound
-                ) {
-                    throw new RuntimeException(
-                        'An array can have only integers or strings as keys'
-                    );
-                }
-
-                if ($keyType instanceof Compound) {
-                    foreach ($keyType->getIterator() as $item) {
-                        if (
-                            !$item instanceof ArrayKey &&
-                            !$item instanceof String_ &&
-                            !$item instanceof Integer
-                        ) {
-                            throw new RuntimeException(
-                                'An array can have only integers or strings as keys'
-                            );
-                        }
-                    }
-                }
+        $continue = true;
+        while ($continue) {
+            $continue = false;
+            while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
+                $ast = $this->parse($tokenIterator);
+                $type2 = $this->createType($ast, $context);
+                $type = new Compound([$type, $type2]);
+                $continue = true;
             }
 
-            $tokens->next();
-            // now let's parse the value type
-            $valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
-        }
-
-        $token = $tokens->current();
-        if ($token !== '>') {
-            if (empty($token)) {
-                throw new RuntimeException(
-                    'Collection: ">" is missing'
-                );
+            while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
+                $ast = $this->typeParser->parse($tokenIterator);
+                $type2 = $this->createType($ast, $context);
+                $type = new Intersection([$type, $type2]);
+                $continue = true;
             }
-
-            throw new RuntimeException(
-                'Unexpected character "' . $token . '", ">" is missing'
-            );
         }
 
-        if ($isArray) {
-            return new Array_($valueType, $keyType);
-        }
-
-        if ($isIterable) {
-            return new Iterable_($valueType, $keyType);
-        }
-
-        if ($isList) {
-            return new List_($valueType);
-        }
-
-        if ($classType instanceof Object_) {
-            return $this->makeCollectionFromObject($classType, $valueType, $keyType);
-        }
-
-        throw new RuntimeException('Invalid $classType provided');
-    }
-
-    /**
-     * @psalm-pure
-     */
-    private function makeCollectionFromObject(Object_ $object, Type $valueType, ?Type $keyType = null): Collection
-    {
-        return new Collection($object->getFqsen(), $valueType, $keyType);
+        return $type;
     }
 }
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/AggregatedType.php b/vendor/phpdocumentor/type-resolver/src/Types/AggregatedType.php
index 257ed51..e1da6cb 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/AggregatedType.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/AggregatedType.php
@@ -34,9 +34,10 @@ abstract class AggregatedType implements Type, IteratorAggregate
      * @psalm-allow-private-mutation
      * @var array<int, Type>
      */
-    private array $types = [];
+    private $types = [];
 
-    private string $token;
+    /** @var string */
+    private $token;
 
     /**
      * @param array<Type> $types
@@ -106,7 +107,7 @@ abstract class AggregatedType implements Type, IteratorAggregate
      */
     private function add(Type $type): void
     {
-        if ($type instanceof self) {
+        if ($type instanceof static) {
             foreach ($type->getIterator() as $subType) {
                 $this->add($subType);
             }
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/CallableParameter.php b/vendor/phpdocumentor/type-resolver/src/Types/CallableParameter.php
new file mode 100644
index 0000000..51c550b
--- /dev/null
+++ b/vendor/phpdocumentor/type-resolver/src/Types/CallableParameter.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * This file is part of phpDocumentor.
+ *
+ *  For the full copyright and license information, please view the LICENSE
+ *  file that was distributed with this source code.
+ *
+ *  @link      http://phpdoc.org
+ */
+
+declare(strict_types=1);
+
+namespace phpDocumentor\Reflection\Types;
+
+use phpDocumentor\Reflection\Type;
+
+/**
+ * Value Object representing a Callable parameters.
+ *
+ * @psalm-immutable
+ */
+final class CallableParameter
+{
+    /** @var Type */
+    private $type;
+
+    /** @var bool */
+    private $isReference;
+
+    /** @var bool */
+    private $isVariadic;
+
+    /** @var bool */
+    private $isOptional;
+
+    /** @var string|null */
+    private $name;
+
+    public function __construct(
+        Type $type,
+        ?string $name = null,
+        bool $isReference = false,
+        bool $isVariadic = false,
+        bool $isOptional = false
+    ) {
+        $this->type = $type;
+        $this->isReference = $isReference;
+        $this->isVariadic = $isVariadic;
+        $this->isOptional = $isOptional;
+        $this->name = $name;
+    }
+
+    public function getName(): ?string
+    {
+        return $this->name;
+    }
+
+    public function getType(): Type
+    {
+        return $this->type;
+    }
+
+    public function isReference(): bool
+    {
+        return $this->isReference;
+    }
+
+    public function isVariadic(): bool
+    {
+        return $this->isVariadic;
+    }
+
+    public function isOptional(): bool
+    {
+        return $this->isOptional;
+    }
+}
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/Callable_.php b/vendor/phpdocumentor/type-resolver/src/Types/Callable_.php
index 4e67aa4..6b06489 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/Callable_.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/Callable_.php
@@ -22,6 +22,31 @@ use phpDocumentor\Reflection\Type;
  */
 final class Callable_ implements Type
 {
+    /** @var Type|null */
+    private $returnType;
+    /** @var CallableParameter[] */
+    private $parameters;
+
+    /**
+     * @param CallableParameter[] $parameters
+     */
+    public function __construct(array $parameters = [], ?Type $returnType = null)
+    {
+        $this->parameters = $parameters;
+        $this->returnType = $returnType;
+    }
+
+    /** @return CallableParameter[] */
+    public function getParameters(): array
+    {
+        return $this->parameters;
+    }
+
+    public function getReturnType(): ?Type
+    {
+        return $this->returnType;
+    }
+
     /**
      * Returns a rendered output of the Type as it would be used in a DocBlock.
      */
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php b/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php
index b54c549..fbdd879 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/ClassString.php
@@ -24,7 +24,8 @@ use phpDocumentor\Reflection\Type;
  */
 final class ClassString extends String_ implements PseudoType
 {
-    private ?Fqsen $fqsen;
+    /** @var Fqsen|null */
+    private $fqsen;
 
     /**
      * Initializes this representation of a class string with the given Fqsen.
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/Collection.php b/vendor/phpdocumentor/type-resolver/src/Types/Collection.php
index dbf3f19..943cc22 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/Collection.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/Collection.php
@@ -31,7 +31,8 @@ use phpDocumentor\Reflection\Type;
  */
 final class Collection extends AbstractList
 {
-    private ?Fqsen $fqsen;
+    /** @var Fqsen|null */
+    private $fqsen;
 
     /**
      * Initializes this representation of an array with the given Type or Fqsen.
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/Context.php b/vendor/phpdocumentor/type-resolver/src/Types/Context.php
index 17aa506..79aadaf 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/Context.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/Context.php
@@ -36,13 +36,13 @@ use function trim;
 final class Context
 {
     /** @var string The current namespace. */
-    private string $namespace;
+    private $namespace;
 
     /**
      * @var string[] List of namespace aliases => Fully Qualified Namespace.
      * @psalm-var array<string, string>
      */
-    private array $namespaceAliases;
+    private $namespaceAliases;
 
     /**
      * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN)
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php b/vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php
index 892ee0f..2018ff5 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php
@@ -45,14 +45,15 @@ use const T_NAME_QUALIFIED;
 use const T_NAMESPACE;
 use const T_NS_SEPARATOR;
 use const T_STRING;
+use const T_TRAIT;
 use const T_USE;
 
 if (!defined('T_NAME_QUALIFIED')) {
-    define('T_NAME_QUALIFIED', 'T_NAME_QUALIFIED');
+    define('T_NAME_QUALIFIED', 10001);
 }
 
 if (!defined('T_NAME_FULLY_QUALIFIED')) {
-    define('T_NAME_FULLY_QUALIFIED', 'T_NAME_FULLY_QUALIFIED');
+    define('T_NAME_FULLY_QUALIFIED', 10002);
 }
 
 /**
@@ -181,6 +182,7 @@ final class ContextFactory
                     $currentNamespace = $this->parseNamespace($tokens);
                     break;
                 case T_CLASS:
+                case T_TRAIT:
                     // Fast-forward the iterator through the class so that any
                     // T_USE tokens found within are skipped - these are not
                     // valid namespace use statements so should be ignored.
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/Expression.php b/vendor/phpdocumentor/type-resolver/src/Types/Expression.php
index 2b6b095..da5f65d 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/Expression.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/Expression.php
@@ -22,7 +22,8 @@ use phpDocumentor\Reflection\Type;
  */
 final class Expression implements Type
 {
-    protected Type $valueType;
+    /** @var Type */
+    protected $valueType;
 
     /**
      * Initializes this representation of an array with the given Type.
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/Float_.php b/vendor/phpdocumentor/type-resolver/src/Types/Float_.php
index 86138c0..85313a7 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/Float_.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/Float_.php
@@ -20,7 +20,7 @@ use phpDocumentor\Reflection\Type;
  *
  * @psalm-immutable
  */
-final class Float_ implements Type
+class Float_ implements Type
 {
     /**
      * Returns a rendered output of the Type as it would be used in a DocBlock.
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/InterfaceString.php b/vendor/phpdocumentor/type-resolver/src/Types/InterfaceString.php
index 0b7dbb8..9836961 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/InterfaceString.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/InterfaceString.php
@@ -23,7 +23,8 @@ use phpDocumentor\Reflection\Type;
  */
 final class InterfaceString implements Type
 {
-    private ?Fqsen $fqsen;
+    /** @var Fqsen|null */
+    private $fqsen;
 
     /**
      * Initializes this representation of a class string with the given Fqsen.
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/Nullable.php b/vendor/phpdocumentor/type-resolver/src/Types/Nullable.php
index 52c3d53..a946935 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/Nullable.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/Nullable.php
@@ -23,7 +23,7 @@ use phpDocumentor\Reflection\Type;
 final class Nullable implements Type
 {
     /** @var Type The actual type that is wrapped */
-    private Type $realType;
+    private $realType;
 
     /**
      * Initialises this nullable type using the real type embedded
diff --git a/vendor/phpdocumentor/type-resolver/src/Types/Object_.php b/vendor/phpdocumentor/type-resolver/src/Types/Object_.php
index feb6ee3..90dee57 100644
--- a/vendor/phpdocumentor/type-resolver/src/Types/Object_.php
+++ b/vendor/phpdocumentor/type-resolver/src/Types/Object_.php
@@ -30,7 +30,8 @@ use function strpos;
  */
 final class Object_ implements Type
 {
-    private ?Fqsen $fqsen;
+    /** @var Fqsen|null */
+    private $fqsen;
 
     /**
      * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'.
diff --git a/vendor/phpoffice/phpspreadsheet/.php-cs-fixer.dist.php b/vendor/phpoffice/phpspreadsheet/.php-cs-fixer.dist.php
index 4179c67..b0207d3 100644
--- a/vendor/phpoffice/phpspreadsheet/.php-cs-fixer.dist.php
+++ b/vendor/phpoffice/phpspreadsheet/.php-cs-fixer.dist.php
@@ -2,12 +2,15 @@
 
 $finder = PhpCsFixer\Finder::create()
     ->exclude('vendor')
+    ->notPath('src/PhpSpreadsheet/Writer/ZipStream3.php')
+    ->name('/(\.php|^generate-document|^generate-locales|^check-phpdoc-types)$/')
     ->in(__DIR__);
 
 $config = new PhpCsFixer\Config();
 $config
     ->setRiskyAllowed(true)
     ->setFinder($finder)
+    ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
     ->setCacheFile(sys_get_temp_dir() . '/php-cs-fixer' . preg_replace('~\W~', '-', __DIR__))
     ->setRules([
         'align_multiline_comment' => true,
@@ -21,8 +24,8 @@ $config
         'braces' => true,
         'cast_spaces' => true,
         'class_attributes_separation' => ['elements' => ['method' => 'one', 'property' => 'one']], // const are often grouped with other related const
-        'class_definition' => false,
-        'class_keyword_remove' => false, // ::class keyword gives us better support in IDE
+        'class_definition' => false, // phpcs disagree
+        'class_keyword_remove' => false, // Deprecated, and ::class keyword gives us better support in IDE
         'combine_consecutive_issets' => true,
         'combine_consecutive_unsets' => true,
         'combine_nested_dirname' => true,
@@ -39,15 +42,17 @@ $config
         'doctrine_annotation_indentation' => true,
         'doctrine_annotation_spaces' => true,
         'elseif' => true,
+        'empty_loop_body' => true,
+        'empty_loop_condition' => true,
         'encoding' => true,
         'ereg_to_preg' => true,
+        'error_suppression' => false, // it breaks \PhpOffice\PhpSpreadsheet\Helper\Handler
         'escape_implicit_backslashes' => true,
         'explicit_indirect_variable' => false, // I feel it makes the code actually harder to read
         'explicit_string_variable' => false, // I feel it makes the code actually harder to read
         'final_class' => false, // We need non-final classes
         'final_internal_class' => true,
         'final_public_method_for_abstract_class' => false, // We need non-final methods
-        'self_static_accessor' => true,
         'fopen_flag_order' => true,
         'fopen_flags' => true,
         'full_opening_tag' => true,
@@ -56,7 +61,9 @@ $config
         'function_to_constant' => true,
         'function_typehint_space' => true,
         'general_phpdoc_annotation_remove' => ['annotations' => ['access', 'category', 'copyright']],
+        'general_phpdoc_tag_rename' => true,
         'global_namespace_import' => true,
+        'group_import' => false, // I feel it makes the code actually harder to read
         'header_comment' => false, // We don't use common header in all our files
         'heredoc_indentation' => true,
         'heredoc_to_nowdoc' => false, // Not sure about this one
@@ -64,7 +71,9 @@ $config
         'include' => true,
         'increment_style' => true,
         'indentation_type' => true,
+        'integer_literal_case' => true,
         'is_null' => true,
+        'lambda_not_used_import' => true,
         'line_ending' => true,
         'linebreak_after_opening_tag' => true,
         'list_syntax' => ['syntax' => 'short'],
@@ -77,6 +86,7 @@ $config
         'mb_str_functions' => false, // No, too dangerous to change that
         'method_argument_space' => true,
         'method_chaining_indentation' => true,
+        'modernize_strpos' => true,
         'modernize_types_casting' => true,
         'multiline_comment_opening_closing' => true,
         'multiline_whitespace_before_semicolons' => true,
@@ -86,6 +96,7 @@ $config
         'native_function_type_declaration_casing' => true,
         'new_with_braces' => true,
         'no_alias_functions' => true,
+        'no_alias_language_construct_call' => true,
         'no_alternative_syntax' => true,
         'no_binary_string' => true,
         'no_blank_lines_after_class_opening' => true,
@@ -107,6 +118,7 @@ $config
         'no_short_bool_cast' => true,
         'echo_tag_syntax' => ['format' => 'long'],
         'no_singleline_whitespace_before_semicolons' => true,
+        'no_space_around_double_colon' => true,
         'no_spaces_after_function_name' => true,
         'no_spaces_around_offset' => true,
         'no_spaces_inside_parenthesis' => true,
@@ -116,15 +128,17 @@ $config
         'no_trailing_comma_in_singleline_array' => true,
         'no_trailing_whitespace' => true,
         'no_trailing_whitespace_in_comment' => true,
+        'no_trailing_whitespace_in_string' => false, // Too dangerous
         'no_unneeded_control_parentheses' => true,
         'no_unneeded_curly_braces' => true,
         'no_unneeded_final_method' => true,
         'no_unreachable_default_argument_value' => true,
         'no_unset_cast' => true,
-        'no_unset_on_property' => true,
+        'no_unset_on_property' => false,
         'no_unused_imports' => true,
         'no_useless_else' => true,
         'no_useless_return' => true,
+        'no_useless_sprintf' => true,
         'no_whitespace_before_comma_in_array' => true,
         'no_whitespace_in_blank_line' => true,
         'non_printable_character' => true,
@@ -133,9 +147,12 @@ $config
         'not_operator_with_successor_space' => false, // idem
         'nullable_type_declaration_for_default_null_value' => true,
         'object_operator_without_whitespace' => true,
+        'octal_notation' => true,
+        'operator_linebreak' => true,
         'ordered_class_elements' => false, // We prefer to keep some freedom
         'ordered_imports' => true,
         'ordered_interfaces' => true,
+        'ordered_traits' => true,
         'php_unit_construct' => true,
         'php_unit_dedicate_assert' => true,
         'php_unit_dedicate_assert_internal_type' => true,
@@ -171,9 +188,12 @@ $config
         'phpdoc_separation' => true,
         'phpdoc_single_line_var_spacing' => true,
         'phpdoc_summary' => true,
+        'phpdoc_tag_casing' => true,
+        'phpdoc_tag_type' => true,
         'phpdoc_to_comment' => false, // interferes with annotations
         'phpdoc_to_param_type' => false, // Because experimental, but interesting for one shot use
-        'phpdoc_to_return_type' => false, // idem
+        'phpdoc_to_property_type' => false, // Because experimental, but interesting for one shot use
+        'phpdoc_to_return_type' => false, // Because experimental, but interesting for one shot use
         'phpdoc_trim' => true,
         'phpdoc_trim_consecutive_blank_line_separation' => true,
         'phpdoc_types' => true,
@@ -182,8 +202,7 @@ $config
         'phpdoc_var_without_name' => true,
         'pow_to_exponentiation' => true,
         'protected_to_private' => true,
-        //'psr0' => true,
-        //'psr4' => true,
+        'psr_autoloading' => true,
         'random_api_migration' => true,
         'return_assignment' => false, // Sometimes useful for clarity or debug
         'return_type_declaration' => true,
@@ -193,6 +212,7 @@ $config
         'set_type_to_cast' => true,
         'short_scalar_cast' => true,
         'simple_to_complex_string_variable' => false, // Would differ from TypeScript without obvious advantages
+        'simplified_if_return' => false, // Even if technically correct we prefer to be explicit
         'simplified_null_return' => false, // Even if technically correct we prefer to be explicit
         'single_blank_line_at_eof' => true,
         'single_blank_line_before_namespace' => true,
@@ -202,6 +222,7 @@ $config
         'single_line_comment_style' => true,
         'single_line_throw' => false, // I don't see any reason for having a special case for Exception
         'single_quote' => true,
+        'single_space_after_construct' => true,
         'single_trait_insert_per_statement' => true,
         'space_after_semicolon' => true,
         'standardize_increment' => true,
@@ -209,14 +230,19 @@ $config
         'static_lambda' => false, // Risky if we can't guarantee nobody use `bindTo()`
         'strict_comparison' => false, // No, too dangerous to change that
         'strict_param' => false, // No, too dangerous to change that
+        'string_length_to_empty' => true,
         'string_line_ending' => true,
         'switch_case_semicolon_to_colon' => true,
         'switch_case_space' => true,
+        'switch_continue_to_break' => true,
         'ternary_operator_spaces' => true,
+        'ternary_to_elvis_operator' => true,
         'ternary_to_null_coalescing' => true,
         'trailing_comma_in_multiline' => true,
         'trim_array_spaces' => true,
+        'types_spaces' => true,
         'unary_operator_spaces' => true,
+        'use_arrow_functions' => true,
         'visibility_required' => ['elements' => ['property', 'method']], // not const
         'void_return' => true,
         'whitespace_after_comma_in_array' => true,
diff --git a/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist b/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist
index 3eafb6c..ccaa25f 100644
--- a/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist
+++ b/vendor/phpoffice/phpspreadsheet/.phpcs.xml.dist
@@ -5,6 +5,10 @@
     <file>samples</file>
     <file>src</file>
     <file>tests</file>
+    <file>infra</file>
+    <file>bin/generate-document</file>
+    <file>bin/generate-locales</file>
+    <file>bin/check-phpdoc-types</file>
 
     <exclude-pattern>samples/Header.php</exclude-pattern>
     <exclude-pattern>*/tests/Core/*/*Test\.(inc|css|js)$</exclude-pattern>
diff --git a/vendor/phpoffice/phpspreadsheet/.readthedocs.yaml b/vendor/phpoffice/phpspreadsheet/.readthedocs.yaml
new file mode 100644
index 0000000..c671015
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/.readthedocs.yaml
@@ -0,0 +1,12 @@
+# Read the Docs configuration file for MkDocs projects
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html
+
+version: 2
+
+build:
+    os: ubuntu-22.04
+    tools:
+        python: "3"
+
+mkdocs:
+    configuration: mkdocs.yml
diff --git a/vendor/phpoffice/phpspreadsheet/CHANGELOG.md b/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
index 9ce94a6..e7054f1 100644
--- a/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
+++ b/vendor/phpoffice/phpspreadsheet/CHANGELOG.md
@@ -5,6 +5,379 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com)
 and this project adheres to [Semantic Versioning](https://semver.org).
 
+## 2024-08-07 - 2.2.2
+
+### Added
+
+- Nothing yet.
+
+### Changed
+
+- Nothing yet.
+
+### Deprecated
+
+- Nothing yet.
+
+### Moved
+
+- Nothing yet.
+
+### Fixed
+
+- Html Reader Preserve Unicode Whitespace. [Issue #1284](https://github.com/PHPOffice/PhpSpreadsheet/issues/1284) [PR #4106](https://github.com/PHPOffice/PhpSpreadsheet/pull/4106)
+- RATE Function Floating Point Number of Periods. [PR #4107](https://github.com/PHPOffice/PhpSpreadsheet/pull/4107)
+- Parameter Name Change Xlsx Writer Workbook. [Issue #4108](https://github.com/PHPOffice/PhpSpreadsheet/issues/4108) [PR #4111](https://github.com/PHPOffice/PhpSpreadsheet/pull/4111)
+- New Algorithm for TRUNC, ROUNDUP, ROUNDDOWN. [Issue #4113](https://github.com/PHPOffice/PhpSpreadsheet/issues/4113) [PR #4115](https://github.com/PHPOffice/PhpSpreadsheet/pull/4115)
+- Worksheet applyStylesFromArray Retain Active Cell (Excel 16 was having a problem with some files). [Issue #4128](https://github.com/PHPOffice/PhpSpreadsheet/issues/4128) [PR #4132](https://github.com/PHPOffice/PhpSpreadsheet/pull/4132)
+
+## 2024-07-29 - 2.2.1
+
+### Security Fix
+
+- Prevent XXE when loading files [PR #4119](https://github.com/PHPOffice/PhpSpreadsheet/pull/4119)
+
+### Fixed
+
+- Add Sheet may leave Active Sheet uninitialized. [Issue #4112](https://github.com/PHPOffice/PhpSpreadsheet/issues/4112) [PR #4114](https://github.com/PHPOffice/PhpSpreadsheet/pull/4114)
+- Reference to Defined Name Specifying Worksheet. [Issue #206](https://github.com/PHPOffice/PhpSpreadsheet/issues/296) [PR #4096](https://github.com/PHPOffice/PhpSpreadsheet/pull/4096)
+- Xls Reader Print/Show Gridlines. [Issue #912](https://github.com/PHPOffice/PhpSpreadsheet/issues/912) [PR #4098](https://github.com/PHPOffice/PhpSpreadsheet/pull/4098)
+- ODS Reader Allow Omission of Page Settings Tags. [Issue #4099](https://github.com/PHPOffice/PhpSpreadsheet/issues/4099) [PR #4101](https://github.com/PHPOffice/PhpSpreadsheet/pull/4101)
+
+## 2024-07-24 - 2.2.0
+
+### Added
+
+- Xlsx Reader Optionally Ignore Rows With No Cells. [Issue #3982](https://github.com/PHPOffice/PhpSpreadsheet/issues/3982) [PR #4035](https://github.com/PHPOffice/PhpSpreadsheet/pull/4035)
+- Means to change style without affecting current cell/sheet. [PR #4073](https://github.com/PHPOffice/PhpSpreadsheet/pull/4073)
+- Option for CSV output file to have varying numbers of columns for each row. [Issue #1415](https://github.com/PHPOffice/PhpSpreadsheet/issues/1415) [PR #4076](https://github.com/PHPOffice/PhpSpreadsheet/pull/4076)
+
+### Changed
+
+- On read, Xlsx Reader had been breaking up union ranges into separate individual ranges. It will now try to preserve range as it was read in. [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
+- Xlsx/Xls spreadsheet calculation and formatting of dates will use base date of spreadsheet even when spreadsheets with different base dates are simultaneously open. [Issue #1036](https://github.com/PHPOffice/PhpSpreadsheet/issues/1036) [Issue #1635](https://github.com/PHPOffice/PhpSpreadsheet/issues/1635) [PR #4071](https://github.com/PHPOffice/PhpSpreadsheet/pull/4071) 
+
+### Deprecated
+
+- Writer\Xls\Style\ColorMap is no longer needed.
+
+### Moved
+
+- Nothing
+
+### Fixed
+
+- Incorrect Reader CSV with BOM. [Issue #4028](https://github.com/PHPOffice/PhpSpreadsheet/issues/4028) [PR #4029](https://github.com/PHPOffice/PhpSpreadsheet/pull/4029)
+- POWER Null/Bool Args. [PR #4031](https://github.com/PHPOffice/PhpSpreadsheet/pull/4031)
+- Do Not Output Alignment and Protection for Conditional Format. [Issue #4025](https://github.com/PHPOffice/PhpSpreadsheet/issues/4025) [PR #4027](https://github.com/PHPOffice/PhpSpreadsheet/pull/4027)
+- Conditional Color Scale Improvements. [Issue #4049](https://github.com/PHPOffice/PhpSpreadsheet/issues/4049) [PR #4050](https://github.com/PHPOffice/PhpSpreadsheet/pull/4050)
+- Mpdf and Tcpdf Borders on Merged Cells. [Issue #3557](https://github.com/PHPOffice/PhpSpreadsheet/issues/3557) [PR #4047](https://github.com/PHPOffice/PhpSpreadsheet/pull/4047)
+- Xls Conditional Format Improvements. [PR #4030](https://github.com/PHPOffice/PhpSpreadsheet/pull/4030) [PR #4033](https://github.com/PHPOffice/PhpSpreadsheet/pull/4033)
+- Conditional Range Unions and Intersections [Issue #4039](https://github.com/PHPOffice/PhpSpreadsheet/issues/4039) [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
+- Ods comments with newlines. [Issue #4081](https://github.com/PHPOffice/PhpSpreadsheet/issues/4081) [PR #4086](https://github.com/PHPOffice/PhpSpreadsheet/pull/4086)
+- Propagate errors in Text functions. [Issue #2581](https://github.com/PHPOffice/PhpSpreadsheet/issues/2581) [PR #4080](https://github.com/PHPOffice/PhpSpreadsheet/pull/4080)
+- Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4040](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040)
+- Problem rendering line chart with missing plot label. [PR #4074](https://github.com/PHPOffice/PhpSpreadsheet/pull/4074)
+- More RTL in Xlsx/Html Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4065](https://github.com/PHPOffice/PhpSpreadsheet/pull/4065)
+- Empty String in sharedStrings. [Issue #4063](https://github.com/PHPOffice/PhpSpreadsheet/issues/4063) [PR #4064](https://github.com/PHPOffice/PhpSpreadsheet/pull/4064)
+- Xlsx Writer RichText and TYPE_STRING. [Issue #476](https://github.com/PHPOffice/PhpSpreadsheet/issues/476) [PR #4094](https://github.com/PHPOffice/PhpSpreadsheet/pull/4094)
+- Ods boolean data. [Issue #460](https://github.com/PHPOffice/PhpSpreadsheet/issues/460) [PR #4093](https://github.com/PHPOffice/PhpSpreadsheet/pull/4093)
+- Html Writer Minor Fixes. [PR #4089](https://github.com/PHPOffice/PhpSpreadsheet/pull/4089)
+- Changes to INDEX function. [Issue #64](https://github.com/PHPOffice/PhpSpreadsheet/issues/64) [PR #4088](https://github.com/PHPOffice/PhpSpreadsheet/pull/4088)
+- Ods Reader and Whitespace Text Nodes. [Issue #804](https://github.com/PHPOffice/PhpSpreadsheet/issues/804) [PR #4087](https://github.com/PHPOffice/PhpSpreadsheet/pull/4087)
+- Ods Xml Reader and Whitespace Text Nodes. [Issue #804](https://github.com/PHPOffice/PhpSpreadsheet/issues/804) [PR #4087](https://github.com/PHPOffice/PhpSpreadsheet/pull/4087)
+- Treat invalid formulas as strings. [Issue #1310](https://github.com/PHPOffice/PhpSpreadsheet/issues/1310) [PR #4073](https://github.com/PHPOffice/PhpSpreadsheet/pull/4073)
+
+## 2024-05-11 - 2.1.0
+
+### MINOR BREAKING CHANGE
+
+- Writing of cell comments to Html will now sanitize all Html tags within the comment, so the tags will be rendered as plaintext and have no other effects when rendered. Styling can be achieved by using the Font property of of the TextRuns which make up the comment, as is already the cases for Xlsx. [PR #3957](https://github.com/PHPOffice/PhpSpreadsheet/pull/3957)
+
+### Added
+
+- Default Style Alignment Property (workaround for bug in non-Excel spreadsheet apps) [Issue #3918](https://github.com/PHPOffice/PhpSpreadsheet/issues/3918) [PR #3924](https://github.com/PHPOffice/PhpSpreadsheet/pull/3924)
+- Additional Support for Date/Time Styles [PR #3939](https://github.com/PHPOffice/PhpSpreadsheet/pull/3939)
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Reader/Xml trySimpleXMLLoadString should not have had public visibility, and will be removed.
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- IF Empty Arguments. [Issue #3875](https://github.com/PHPOffice/PhpSpreadsheet/issues/3875) [Issue #2146](https://github.com/PHPOffice/PhpSpreadsheet/issues/2146) [PR #3879](https://github.com/PHPOffice/PhpSpreadsheet/pull/3879)
+- Changes to floating point in Php8.4. [Issue #3896](https://github.com/PHPOffice/PhpSpreadsheet/issues/3896) [PR #3897](https://github.com/PHPOffice/PhpSpreadsheet/pull/3897)
+- Handling User-supplied Decimal and Thousands Separators. [Issue #3900](https://github.com/PHPOffice/PhpSpreadsheet/issues/3900) [PR #3903](https://github.com/PHPOffice/PhpSpreadsheet/pull/3903)
+- Improve Performance of CSV Writer. [Issue #3904](https://github.com/PHPOffice/PhpSpreadsheet/issues/3904) [PR #3906](https://github.com/PHPOffice/PhpSpreadsheet/pull/3906)
+- Fix issue with prepending zero in percentage [Issue #3920](https://github.com/PHPOffice/PhpSpreadsheet/issues/3920) [PR #3921](https://github.com/PHPOffice/PhpSpreadsheet/pull/3921)
+- Incorrect SUMPRODUCT Calculation [Issue #3909](https://github.com/PHPOffice/PhpSpreadsheet/issues/3909) [PR #3916](https://github.com/PHPOffice/PhpSpreadsheet/pull/3916)
+- Formula Misidentifying Text as Cell After Insertion/Deletion [Issue #3907](https://github.com/PHPOffice/PhpSpreadsheet/issues/3907) [PR #3915](https://github.com/PHPOffice/PhpSpreadsheet/pull/3915)
+- Unexpected Absolute Address in Xlsx Rels [Issue #3730](https://github.com/PHPOffice/PhpSpreadsheet/issues/3730) [PR #3923](https://github.com/PHPOffice/PhpSpreadsheet/pull/3923)
+- Unallocated Cells Affected by Column/Row Insert/Delete [Issue #3933](https://github.com/PHPOffice/PhpSpreadsheet/issues/3933) [PR #3940](https://github.com/PHPOffice/PhpSpreadsheet/pull/3940)
+- Invalid Builtin Defined Name in Xls Reader [Issue #3935](https://github.com/PHPOffice/PhpSpreadsheet/issues/3935) [PR #3942](https://github.com/PHPOffice/PhpSpreadsheet/pull/3942)
+- Hidden Rows and Columns Tcpdf/Mpdf [PR #3945](https://github.com/PHPOffice/PhpSpreadsheet/pull/3945)
+- RTL Text Alignment in Xlsx Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4006](https://github.com/PHPOffice/PhpSpreadsheet/pull/4006)
+- Protect Sheet But Allow Sort [Issue #3951](https://github.com/PHPOffice/PhpSpreadsheet/issues/3951) [PR #3956](https://github.com/PHPOffice/PhpSpreadsheet/pull/3956)
+- Default Value for Conditional::$text [PR #3946](https://github.com/PHPOffice/PhpSpreadsheet/pull/3946)
+- Table Filter Buttons [Issue #3988](https://github.com/PHPOffice/PhpSpreadsheet/issues/3988) [PR #3992](https://github.com/PHPOffice/PhpSpreadsheet/pull/3992)
+- Improvements to Xml Reader [Issue #3999](https://github.com/PHPOffice/PhpSpreadsheet/issues/3999) [Issue #4000](https://github.com/PHPOffice/PhpSpreadsheet/issues/4000) [Issue #4001](https://github.com/PHPOffice/PhpSpreadsheet/issues/4001) [Issue #4002](https://github.com/PHPOffice/PhpSpreadsheet/issues/4002) [PR #4003](https://github.com/PHPOffice/PhpSpreadsheet/pull/4003) [PR #4007](https://github.com/PHPOffice/PhpSpreadsheet/pull/4007)
+- Html Reader non-UTF8 [Issue #3995](https://github.com/PHPOffice/PhpSpreadsheet/issues/3995) [Issue #866](https://github.com/PHPOffice/PhpSpreadsheet/issues/866) [Issue #1681](https://github.com/PHPOffice/PhpSpreadsheet/issues/1681) [PR #4019](https://github.com/PHPOffice/PhpSpreadsheet/pull/4019)
+
+## 2.0.0 - 2024-01-04
+
+### BREAKING CHANGE
+
+- Typing was strengthened by leveraging native typing. This should not change any behavior. However, if you implement
+  any interfaces or inherit from any classes, you will need to adapt your typing accordingly. If you use static analysis 
+  tools such as PHPStan or Psalm, new errors might be found. If you find actual bugs because of the new typing, please
+  open a PR that fixes it with a **detailed** explanation of the reason. We'll try to merge and release typing-related
+  fixes quickly in the coming days. [PR #3718](https://github.com/PHPOffice/PhpSpreadsheet/pull/3718)
+- All deprecated things have been removed, for details, see [816b91d0b4](https://github.com/PHPOffice/PhpSpreadsheet/commit/816b91d0b4a0c7285a9e3fc88c58f7730d922044)
+
+### Added
+
+- Split screens (Xlsx and Xml only, not 100% complete). [Issue #3601](https://github.com/PHPOffice/PhpSpreadsheet/issues/3601) [PR #3622](https://github.com/PHPOffice/PhpSpreadsheet/pull/3622)
+- Permit Meta Viewport in Html. [Issue #3565](https://github.com/PHPOffice/PhpSpreadsheet/issues/3565) [PR #3623](https://github.com/PHPOffice/PhpSpreadsheet/pull/3623)
+- Hyperlink support for Ods. [Issue #3660](https://github.com/PHPOffice/PhpSpreadsheet/issues/3660) [PR #3669](https://github.com/PHPOffice/PhpSpreadsheet/pull/3669)
+- ListWorksheetInfo/Names for Html/Csv/Slk. [Issue #3706](https://github.com/PHPOffice/PhpSpreadsheet/issues/3706) [PR #3709](https://github.com/PHPOffice/PhpSpreadsheet/pull/3709)
+- Methods to determine if cell is actually locked, or hidden on formula bar. [PR #3722](https://github.com/PHPOffice/PhpSpreadsheet/pull/3722)
+- Add iterateOnlyExistingCells to Constructors. [Issue #3721](https://github.com/PHPOffice/PhpSpreadsheet/issues/3721) [PR #3727](https://github.com/PHPOffice/PhpSpreadsheet/pull/3727)
+- Support for Conditional Formatting Color Scale. [PR #3738](https://github.com/PHPOffice/PhpSpreadsheet/pull/3738)
+- Support Additional Tags in Helper/Html. [Issue #3751](https://github.com/PHPOffice/PhpSpreadsheet/issues/3751) [PR #3752](https://github.com/PHPOffice/PhpSpreadsheet/pull/3752)
+- Writer ODS : Write Border Style for cells [Issue #3690](https://github.com/PHPOffice/PhpSpreadsheet/issues/3690) [PR #3693](https://github.com/PHPOffice/PhpSpreadsheet/pull/3693)
+- Sheet Background Images [Issue #1649](https://github.com/PHPOffice/PhpSpreadsheet/issues/1649) [PR #3795](https://github.com/PHPOffice/PhpSpreadsheet/pull/3795)
+- Check if Coordinate is Inside Range [PR #3779](https://github.com/PHPOffice/PhpSpreadsheet/pull/3779)
+- Flipping Images [Issue #731](https://github.com/PHPOffice/PhpSpreadsheet/issues/731) [PR #3801](https://github.com/PHPOffice/PhpSpreadsheet/pull/3801)
+- Chart Dynamic Title and Font Properties [Issue #3797](https://github.com/PHPOffice/PhpSpreadsheet/issues/3797) [PR #3800](https://github.com/PHPOffice/PhpSpreadsheet/pull/3800)
+- Chart Axis Display Units and Logarithmic Scale. [Issue #3833](https://github.com/PHPOffice/PhpSpreadsheet/issues/3833) [PR #3836](https://github.com/PHPOffice/PhpSpreadsheet/pull/3836)
+- Partial Support of Fill Handles. [Discussion #3847](https://github.com/PHPOffice/PhpSpreadsheet/discussions/3847) [PR #3855](https://github.com/PHPOffice/PhpSpreadsheet/pull/3855)
+
+### Changed
+
+- **Drop support for PHP 7.4**, according to https://phpspreadsheet.readthedocs.io/en/latest/#php-version-support [PR #3713](https://github.com/PHPOffice/PhpSpreadsheet/pull/3713)
+- RLM Added to NumberFormatter Currency. This happens depending on release of ICU which Php is using (it does not yet happen with any official release). PhpSpreadsheet will continue to use the value returned by Php, but a method is added to keep the result unchanged from release to release. [Issue #3571](https://github.com/PHPOffice/PhpSpreadsheet/issues/3571) [PR #3640](https://github.com/PHPOffice/PhpSpreadsheet/pull/3640)
+- `toFormattedString` will now always return a string. This was introduced with 1.28.0, but was not properly documented at the time. This can affect the results of `toArray`, `namedRangeToArray`, and `rangeToArray`. [PR #3304](https://github.com/PHPOffice/PhpSpreadsheet/pull/3304)
+- Value of constants FORMAT_CURRENCY_EUR and FORMAT_CURRENCY_USD was changed in 1.28.0, but was not properly documented at the time. [Issue #3577](https://github.com/PHPOffice/PhpSpreadsheet/issues/3577)
+- Html Writer will attempt to use Chart coordinates to determine image size. [Issue #3783](https://github.com/PHPOffice/PhpSpreadsheet/issues/3783) [PR #3787](https://github.com/PHPOffice/PhpSpreadsheet/pull/3787)
+
+### Deprecated
+
+- Functions `_translateFormulaToLocale` and `_translateFormulaEnglish` are replaced by versions without leading underscore. [PR #3828](https://github.com/PHPOffice/PhpSpreadsheet/pull/3828)
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Take advantage of mitoteam/jpgraph Extended mode to enable rendering of more graphs. [PR #3603](https://github.com/PHPOffice/PhpSpreadsheet/pull/3603)
+- Column widths, especially for ODS. [Issue #3609](https://github.com/PHPOffice/PhpSpreadsheet/issues/3609) [PR #3610](https://github.com/PHPOffice/PhpSpreadsheet/pull/3610)
+- Avoid NULL in String Function call (partial solution). [Issue #3613](https://github.com/PHPOffice/PhpSpreadsheet/issues/3613) [PR #3617](https://github.com/PHPOffice/PhpSpreadsheet/pull/3617)
+- Preserve transparency in Memory Drawing. [Issue #3624](https://github.com/PHPOffice/PhpSpreadsheet/issues/3624) [PR #3627](https://github.com/PHPOffice/PhpSpreadsheet/pull/3627)
+- Customizable padding for Exact Column Width. [Issue #3626](https://github.com/PHPOffice/PhpSpreadsheet/issues/3626) [PR #3628](https://github.com/PHPOffice/PhpSpreadsheet/pull/3628)
+- Ensure ROW function returns int (problem exposed in unreleased Php). [PR #3641](https://github.com/PHPOffice/PhpSpreadsheet/pull/3641)
+- Minor changes to Mpdf and Html Writers. [PR #3645](https://github.com/PHPOffice/PhpSpreadsheet/pull/3645)
+- Xlsx Reader Namespacing for Tables, Autofilters. [Issue #3665](https://github.com/PHPOffice/PhpSpreadsheet/issues/3665) [PR #3668](https://github.com/PHPOffice/PhpSpreadsheet/pull/3668)
+- Read Code Page for Xls ListWorksheetInfo/Names BIFF5. [Issue #3671](https://github.com/PHPOffice/PhpSpreadsheet/issues/3671) [PR #3672](https://github.com/PHPOffice/PhpSpreadsheet/pull/3672)
+- Read Data from Table on Different Sheet. [Issue #3635](https://github.com/PHPOffice/PhpSpreadsheet/issues/3635) [PR #3659](https://github.com/PHPOffice/PhpSpreadsheet/pull/3659)
+- Html Writer Styles Using Inline Css. [Issue #3678](https://github.com/PHPOffice/PhpSpreadsheet/issues/3678) [PR #3680](https://github.com/PHPOffice/PhpSpreadsheet/pull/3680)
+- Xlsx Read Ignoring Some Comments. [Issue #3654](https://github.com/PHPOffice/PhpSpreadsheet/issues/3654) [PR #3655](https://github.com/PHPOffice/PhpSpreadsheet/pull/3655)
+- Fractional Seconds in Date/Time Values. [PR #3677](https://github.com/PHPOffice/PhpSpreadsheet/pull/3677)
+- SetCalculatedValue Avoid Casting String to Numeric. [Issue #3658](https://github.com/PHPOffice/PhpSpreadsheet/issues/3658) [PR #3685](https://github.com/PHPOffice/PhpSpreadsheet/pull/3685)
+- Several Problems in a Very Complicated Spreadsheet. [Issue #3679](https://github.com/PHPOffice/PhpSpreadsheet/issues/3679) [PR #3681](https://github.com/PHPOffice/PhpSpreadsheet/pull/3681)
+- Inconsistent String Handling for Sum Functions. [Issue #3652](https://github.com/PHPOffice/PhpSpreadsheet/issues/3652) [PR #3653](https://github.com/PHPOffice/PhpSpreadsheet/pull/3653)
+- Recomputation of Relative Addresses in Defined Names. [Issue #3661](https://github.com/PHPOffice/PhpSpreadsheet/issues/3661) [PR #3673](https://github.com/PHPOffice/PhpSpreadsheet/pull/3673)
+- Writer Xls Characters Outside BMP (emojis). [Issue #642](https://github.com/PHPOffice/PhpSpreadsheet/issues/642) [PR #3696](https://github.com/PHPOffice/PhpSpreadsheet/pull/3696)
+- Xlsx Reader Improve Handling of Row and Column Styles. [Issue #3533](https://github.com/PHPOffice/PhpSpreadsheet/issues/3533) [Issue #3534](https://github.com/PHPOffice/PhpSpreadsheet/issues/3534) [PR #3688](https://github.com/PHPOffice/PhpSpreadsheet/pull/3688)
+- Avoid Allocating RowDimension Unneccesarily. [PR #3686](https://github.com/PHPOffice/PhpSpreadsheet/pull/3686)
+- Use Column Style when Row Dimension Exists Without Style. [Issue #3534](https://github.com/PHPOffice/PhpSpreadsheet/issues/3534) [PR #3688](https://github.com/PHPOffice/PhpSpreadsheet/pull/3688)
+- Inconsistency Between Cell Data and Explicitly Declared Type. [Issue #3711](https://github.com/PHPOffice/PhpSpreadsheet/issues/3711) [PR #3715](https://github.com/PHPOffice/PhpSpreadsheet/pull/3715)
+- Unexpected Namespacing in rels File. [Issue #3720](https://github.com/PHPOffice/PhpSpreadsheet/issues/3720) [PR #3722](https://github.com/PHPOffice/PhpSpreadsheet/pull/3722)
+- Break Some Circular References. [PR #3716](https://github.com/PHPOffice/PhpSpreadsheet/pull/3716) [PR #3707](https://github.com/PHPOffice/PhpSpreadsheet/pull/3707)
+- Missing Font Index in Some Xls. [PR #3734](https://github.com/PHPOffice/PhpSpreadsheet/pull/3734)
+- Load Tables even with READ_DATA_ONLY. [PR #3726](https://github.com/PHPOffice/PhpSpreadsheet/pull/3726)
+- Theme File Missing but Referenced in Spreadsheet. [Issue #3770](https://github.com/PHPOffice/PhpSpreadsheet/issues/3770) [PR #3772](https://github.com/PHPOffice/PhpSpreadsheet/pull/3772)
+- Slk Shared Formulas. [Issue #2267](https://github.com/PHPOffice/PhpSpreadsheet/issues/2267) [PR #3776](https://github.com/PHPOffice/PhpSpreadsheet/pull/3776)
+- Html omitting some charts. [Issue #3767](https://github.com/PHPOffice/PhpSpreadsheet/issues/3767) [PR #3771](https://github.com/PHPOffice/PhpSpreadsheet/pull/3771)
+- Case Insensitive Comparison for Sheet Names [PR #3791](https://github.com/PHPOffice/PhpSpreadsheet/pull/3791)
+- Performance improvement for Xlsx Reader. [Issue #3683](https://github.com/PHPOffice/PhpSpreadsheet/issues/3683) [PR #3810](https://github.com/PHPOffice/PhpSpreadsheet/pull/3810)
+- Prevent loop in Shared/File. [Issue #3807](https://github.com/PHPOffice/PhpSpreadsheet/issues/3807) [PR #3809](https://github.com/PHPOffice/PhpSpreadsheet/pull/3809)
+- Consistent handling of decimal/thousands separators between StringHelper and Php setlocale. [Issue #3811](https://github.com/PHPOffice/PhpSpreadsheet/issues/3811) [PR #3815](https://github.com/PHPOffice/PhpSpreadsheet/pull/3815)
+- Clone worksheet with tables or charts. [Issue #3820](https://github.com/PHPOffice/PhpSpreadsheet/issues/3820) [PR #3821](https://github.com/PHPOffice/PhpSpreadsheet/pull/3821)
+- COUNTIFS Does Not Require xlfn. [Issue #3819](https://github.com/PHPOffice/PhpSpreadsheet/issues/3819) [PR #3827](https://github.com/PHPOffice/PhpSpreadsheet/pull/3827)
+- Strip `xlfn.` and `xlws.` from Formula Translations. [Issue #3819](https://github.com/PHPOffice/PhpSpreadsheet/issues/3819) [PR #3828](https://github.com/PHPOffice/PhpSpreadsheet/pull/3828)
+- Recurse directories searching for font file. [Issue #2809](https://github.com/PHPOffice/PhpSpreadsheet/issues/2809) [PR #3830](https://github.com/PHPOffice/PhpSpreadsheet/pull/3830)
+- Reduce memory consumption of Worksheet::rangeToArray() when many empty rows are read. [Issue #3814](https://github.com/PHPOffice/PhpSpreadsheet/issues/3814) [PR #3834](https://github.com/PHPOffice/PhpSpreadsheet/pull/3834)
+- Reduce time used by Worksheet::rangeToArray() when many empty rows are read. [PR #3839](https://github.com/PHPOffice/PhpSpreadsheet/pull/3839)
+- Html Reader Tolerate Invalid Sheet Title. [PR #3845](https://github.com/PHPOffice/PhpSpreadsheet/pull/3845)
+- Do not include unparsed drawings when new drawing added. [Issue #3843](https://github.com/PHPOffice/PhpSpreadsheet/issues/3843) [PR #3846](https://github.com/PHPOffice/PhpSpreadsheet/pull/3846)
+- Do not include unparsed drawings when new drawing added. [Issue #3861](https://github.com/PHPOffice/PhpSpreadsheet/issues/3861) [PR #3862](https://github.com/PHPOffice/PhpSpreadsheet/pull/3862)
+- Excel omits `between` operator for data validation. [Issue #3863](https://github.com/PHPOffice/PhpSpreadsheet/issues/3863) [PR #3865](https://github.com/PHPOffice/PhpSpreadsheet/pull/3865)
+- Use less space when inserting rows and columns. [Issue #3687](https://github.com/PHPOffice/PhpSpreadsheet/issues/3687) [PR #3856](https://github.com/PHPOffice/PhpSpreadsheet/pull/3856)
+- Excel inconsistent handling of MIN/MAX/MINA/MAXA. [Issue #3866](https://github.com/PHPOffice/PhpSpreadsheet/issues/3866) [PR #3868](https://github.com/PHPOffice/PhpSpreadsheet/pull/3868)
+
+## 1.29.0 - 2023-06-15
+
+### Added
+
+- Wizards for defining Number Format masks for Dates and Times, including Durations/Intervals. [PR #3458](https://github.com/PHPOffice/PhpSpreadsheet/pull/3458)
+- Specify data type in html tags. [Issue #3444](https://github.com/PHPOffice/PhpSpreadsheet/issues/3444) [PR #3445](https://github.com/PHPOffice/PhpSpreadsheet/pull/3445)
+- Provide option to ignore hidden rows/columns in `toArray()` methods. [PR #3494](https://github.com/PHPOffice/PhpSpreadsheet/pull/3494)
+- Font/Effects/Theme support for Chart Data Labels and Axis. [PR #3476](https://github.com/PHPOffice/PhpSpreadsheet/pull/3476)
+- Font Themes support. [PR #3486](https://github.com/PHPOffice/PhpSpreadsheet/pull/3486)
+- Ability to Ignore Cell Errors in Excel. [Issue #1141](https://github.com/PHPOffice/PhpSpreadsheet/issues/1141) [PR #3508](https://github.com/PHPOffice/PhpSpreadsheet/pull/3508)
+- Unzipped Gnumeric file [PR #3591](https://github.com/PHPOffice/PhpSpreadsheet/pull/3591)
+
+### Changed
+
+- Xlsx Color schemes read in will be written out (previously Excel 2007-2010 Color scheme was always written); manipulation of those schemes before write, including restoring prior behavior, is provided [PR #3476](https://github.com/PHPOffice/PhpSpreadsheet/pull/3476)
+- Memory and speed optimisations for Read Filters with Xlsx Files and Shared Formulae. [PR #3474](https://github.com/PHPOffice/PhpSpreadsheet/pull/3474)
+- Allow `CellRange` and `CellAddress` objects for the `range` argument in the `rangeToArray()` method. [PR #3494](https://github.com/PHPOffice/PhpSpreadsheet/pull/3494)
+- Stock charts will now read and reproduce `upDownBars` and subsidiary tags; these were previously ignored on read and hard-coded on write. [PR #3515](https://github.com/PHPOffice/PhpSpreadsheet/pull/3515)
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Updates Cell formula absolute ranges/references, and Defined Name absolute ranges/references when inserting/deleting rows/columns. [Issue #3368](https://github.com/PHPOffice/PhpSpreadsheet/issues/3368) [PR #3402](https://github.com/PHPOffice/PhpSpreadsheet/pull/3402)
+- EOMONTH() and EDATE() Functions should round date value before evaluation. [Issue #3436](https://github.com/PHPOffice/PhpSpreadsheet/issues/3436) [PR #3437](https://github.com/PHPOffice/PhpSpreadsheet/pull/3437)
+- NETWORKDAYS function erroneously being converted to NETWORK_xlfn.DAYS in Xlsx Writer. [Issue #3461](https://github.com/PHPOffice/PhpSpreadsheet/issues/3461) [PR #3463](https://github.com/PHPOffice/PhpSpreadsheet/pull/3463)
+- Getting a style for a CellAddress instance fails if the worksheet is set in the CellAddress instance. [Issue #3439](https://github.com/PHPOffice/PhpSpreadsheet/issues/3439) [PR #3469](https://github.com/PHPOffice/PhpSpreadsheet/pull/3469)
+- Shared Formulae outside the filter range when reading with a filter are not always being identified. [Issue #3473](https://github.com/PHPOffice/PhpSpreadsheet/issues/3473) [PR #3474](https://github.com/PHPOffice/PhpSpreadsheet/pull/3474)
+- Xls Reader Conditional Styles. [PR #3400](https://github.com/PHPOffice/PhpSpreadsheet/pull/3400)
+- Allow use of # and 0 digit placeholders in fraction masks. [PR #3401](https://github.com/PHPOffice/PhpSpreadsheet/pull/3401)
+- Modify Date/Time check in the NumberFormatter for decimal/fractional times. [PR #3413](https://github.com/PHPOffice/PhpSpreadsheet/pull/3413)
+- Misplaced Xml Writing Chart Label FillColor. [Issue #3397](https://github.com/PHPOffice/PhpSpreadsheet/issues/3397) [PR #3404](https://github.com/PHPOffice/PhpSpreadsheet/pull/3404)
+- TEXT function ignores Time in DateTimeStamp. [Issue #3409](https://github.com/PHPOffice/PhpSpreadsheet/issues/3409) [PR #3411](https://github.com/PHPOffice/PhpSpreadsheet/pull/3411)
+- Xlsx Column Autosize Approximate for CJK. [Issue #3405](https://github.com/PHPOffice/PhpSpreadsheet/issues/3405) [PR #3416](https://github.com/PHPOffice/PhpSpreadsheet/pull/3416)
+- Correct Xlsx Parsing of quotePrefix="0". [Issue #3435](https://github.com/PHPOffice/PhpSpreadsheet/issues/3435) [PR #3438](https://github.com/PHPOffice/PhpSpreadsheet/pull/3438)
+- More Display Options for Chart Axis and Legend. [Issue #3414](https://github.com/PHPOffice/PhpSpreadsheet/issues/3414) [PR #3434](https://github.com/PHPOffice/PhpSpreadsheet/pull/3434)
+- Apply strict type checking to Complex suffix. [PR #3452](https://github.com/PHPOffice/PhpSpreadsheet/pull/3452)
+- Incorrect Font Color Read Xlsx Rich Text Indexed Color Custom Palette. [Issue #3464](https://github.com/PHPOffice/PhpSpreadsheet/issues/3464) [PR #3465](https://github.com/PHPOffice/PhpSpreadsheet/pull/3465)
+- Xlsx Writer Honor Alignment in Default Font. [Issue #3443](https://github.com/PHPOffice/PhpSpreadsheet/issues/3443) [PR #3459](https://github.com/PHPOffice/PhpSpreadsheet/pull/3459)
+- Support Border for Charts. [PR #3462](https://github.com/PHPOffice/PhpSpreadsheet/pull/3462)
+- Error in "this row" structured reference calculation (cached result from first row when using a range) [Issue #3504](https://github.com/PHPOffice/PhpSpreadsheet/issues/3504) [PR #3505](https://github.com/PHPOffice/PhpSpreadsheet/pull/3505)
+- Allow colour palette index references in Number Format masks [Issue #3511](https://github.com/PHPOffice/PhpSpreadsheet/issues/3511) [PR #3512](https://github.com/PHPOffice/PhpSpreadsheet/pull/3512)
+- Xlsx Reader formula with quotePrefix [Issue #3495](https://github.com/PHPOffice/PhpSpreadsheet/issues/3495) [PR #3497](https://github.com/PHPOffice/PhpSpreadsheet/pull/3497)
+- Handle REF error as part of range [Issue #3453](https://github.com/PHPOffice/PhpSpreadsheet/issues/3453) [PR #3467](https://github.com/PHPOffice/PhpSpreadsheet/pull/3467)
+- Handle Absolute Pathnames in Rels File [Issue #3553](https://github.com/PHPOffice/PhpSpreadsheet/issues/3553) [PR #3554](https://github.com/PHPOffice/PhpSpreadsheet/pull/3554)
+- Return Page Breaks in Order [Issue #3552](https://github.com/PHPOffice/PhpSpreadsheet/issues/3552) [PR #3555](https://github.com/PHPOffice/PhpSpreadsheet/pull/3555)
+- Add position attribute for MemoryDrawing in Html [Issue #3529](https://github.com/PHPOffice/PhpSpreadsheet/issues/3529 [PR #3535](https://github.com/PHPOffice/PhpSpreadsheet/pull/3535)
+- Allow Index_number as Array for VLOOKUP/HLOOKUP [Issue #3561](https://github.com/PHPOffice/PhpSpreadsheet/issues/3561 [PR #3570](https://github.com/PHPOffice/PhpSpreadsheet/pull/3570)
+- Add Unsupported Options in Xml Spreadsheet [Issue #3566](https://github.com/PHPOffice/PhpSpreadsheet/issues/3566 [Issue #3568](https://github.com/PHPOffice/PhpSpreadsheet/issues/3568 [Issue #3569](https://github.com/PHPOffice/PhpSpreadsheet/issues/3569 [PR #3567](https://github.com/PHPOffice/PhpSpreadsheet/pull/3567)
+- Changes to NUMBERVALUE, VALUE, DATEVALUE, TIMEVALUE [Issue #3574](https://github.com/PHPOffice/PhpSpreadsheet/issues/3574 [PR #3575](https://github.com/PHPOffice/PhpSpreadsheet/pull/3575)
+- Redo calculation of color tinting [Issue #3550](https://github.com/PHPOffice/PhpSpreadsheet/issues/3550) [PR #3580](https://github.com/PHPOffice/PhpSpreadsheet/pull/3580)
+- Accommodate Slash with preg_quote [PR #3582](https://github.com/PHPOffice/PhpSpreadsheet/pull/3582) [PR #3583](https://github.com/PHPOffice/PhpSpreadsheet/pull/3583) [PR #3584](https://github.com/PHPOffice/PhpSpreadsheet/pull/3584)
+- HyperlinkBase Property and Html Handling of Properties [Issue #3573](https://github.com/PHPOffice/PhpSpreadsheet/issues/3573) [PR #3589](https://github.com/PHPOffice/PhpSpreadsheet/pull/3589)
+- Improvements for Data Validation [Issue #3592](https://github.com/PHPOffice/PhpSpreadsheet/issues/3592) [Issue #3594](https://github.com/PHPOffice/PhpSpreadsheet/issues/3594) [PR #3605](https://github.com/PHPOffice/PhpSpreadsheet/pull/3605)
+
+## 1.28.0 - 2023-02-25
+
+### Added
+
+- Support for configuring a Chart Title's overlay [PR #3325](https://github.com/PHPOffice/PhpSpreadsheet/pull/3325)
+- Wizards for defining Number Format masks for Numbers, Percentages, Scientific, Currency and Accounting [PR #3334](https://github.com/PHPOffice/PhpSpreadsheet/pull/3334)
+- Support for fixed value divisor in fractional Number Format Masks [PR #3339](https://github.com/PHPOffice/PhpSpreadsheet/pull/3339)
+- Allow More Fonts/Fontnames for Exact Width Calculation [PR #3326](https://github.com/PHPOffice/PhpSpreadsheet/pull/3326) [Issue #3190](https://github.com/PHPOffice/PhpSpreadsheet/issues/3190)
+- Allow override of the Value Binder when setting a Cell value [PR #3361](https://github.com/PHPOffice/PhpSpreadsheet/pull/3361)
+
+### Changed
+
+- Improved handling for @ placeholder in Number Format Masks [PR #3344](https://github.com/PHPOffice/PhpSpreadsheet/pull/3344)
+- Improved handling for ? placeholder in Number Format Masks [PR #3394](https://github.com/PHPOffice/PhpSpreadsheet/pull/3394)
+- Improved support for locale settings and currency codes when matching formatted strings to numerics in the Calculation Engine [PR #3373](https://github.com/PHPOffice/PhpSpreadsheet/pull/3373) and [PR #3374](https://github.com/PHPOffice/PhpSpreadsheet/pull/3374) 
+- Improved support for locale settings and matching in the Advanced Value Binder [PR #3376](https://github.com/PHPOffice/PhpSpreadsheet/pull/3376)
+- `toFormattedString` will now always return a string. This can affect the results of `toArray`, `namedRangeToArray`, and `rangeToArray`. [PR #3304](https://github.com/PHPOffice/PhpSpreadsheet/pull/3304)
+- Value of constants FORMAT_CURRENCY_EUR and FORMAT_CURRENCY_USD is changed. [Issue #3577](https://github.com/PHPOffice/PhpSpreadsheet/issues/3577) [PR #3377](https://github.com/PHPOffice/PhpSpreadsheet/pull/3377)
+
+### Deprecated
+
+- Rationalisation of Pre-defined Currency Format Masks [PR #3377](https://github.com/PHPOffice/PhpSpreadsheet/pull/3377)
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Calculation Engine doesn't evaluate Defined Name when default cell A1 is quote-prefixed [Issue #3335](https://github.com/PHPOffice/PhpSpreadsheet/issues/3335) [PR #3336](https://github.com/PHPOffice/PhpSpreadsheet/pull/3336)
+- XLSX Writer - Array Formulas do not include function prefix [Issue #3337](https://github.com/PHPOffice/PhpSpreadsheet/issues/3337) [PR #3338](https://github.com/PHPOffice/PhpSpreadsheet/pull/3338)
+- Permit Max Column for Row Breaks [Issue #3143](https://github.com/PHPOffice/PhpSpreadsheet/issues/3143) [PR #3345](https://github.com/PHPOffice/PhpSpreadsheet/pull/3345)
+- AutoSize Columns should allow for dropdown icon when AutoFilter is for a Table [Issue #3356](https://github.com/PHPOffice/PhpSpreadsheet/issues/3356) [PR #3358](https://github.com/PHPOffice/PhpSpreadsheet/pull/3358) and for Center Alignment of Headers [Issue #3395](https://github.com/PHPOffice/PhpSpreadsheet/issues/3395) [PR #3399](https://github.com/PHPOffice/PhpSpreadsheet/pull/3399)
+- Decimal Precision for Scientific Number Format Mask [Issue #3381](https://github.com/PHPOffice/PhpSpreadsheet/issues/3381) [PR #3382](https://github.com/PHPOffice/PhpSpreadsheet/pull/3382)
+- Xls Writer Parser Handle Boolean Literals as Function Arguments [Issue #3369](https://github.com/PHPOffice/PhpSpreadsheet/issues/3369) [PR #3391](https://github.com/PHPOffice/PhpSpreadsheet/pull/3391)
+- Conditional Formatting Improvements for Xlsx [Issue #3370](https://github.com/PHPOffice/PhpSpreadsheet/issues/3370) [Issue #3202](https://github.com/PHPOffice/PhpSpreadsheet/issues/3302) [PR #3372](https://github.com/PHPOffice/PhpSpreadsheet/pull/3372)
+- Coerce Bool to Int for Mathematical Operations on Arrays [Issue #3389](https://github.com/PHPOffice/PhpSpreadsheet/issues/3389) [Issue #3396](https://github.com/PHPOffice/PhpSpreadsheet/issues/3396) [PR #3392](https://github.com/PHPOffice/PhpSpreadsheet/pull/3392)
+
+## 1.27.1 - 2023-02-08
+
+### Added
+
+- Nothing
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Nothing
+
+### Fixed
+
+- Fix Composer --dev dependency issue with dealerdirect/phpcodesniffer-composer-installer renaming their `master` branch to `main`
+
+
+## 1.27.0 - 2023-01-24
+
+### Added
+
+- Option to specify a range of columns/rows for the Row/Column `isEmpty()` methods [PR #3315](https://github.com/PHPOffice/PhpSpreadsheet/pull/3315)
+- Option for Cell Iterator to return a null value or create and return a new cell when accessing a cell that doesn't exist [PR #3314](https://github.com/PHPOffice/PhpSpreadsheet/pull/3314)
+- Support for Structured References in the Calculation Engine [PR #3261](https://github.com/PHPOffice/PhpSpreadsheet/pull/3261)
+- Limited Support for Form Controls [PR #3130](https://github.com/PHPOffice/PhpSpreadsheet/pull/3130) [Issue #2396](https://github.com/PHPOffice/PhpSpreadsheet/issues/2396) [Issue #1770](https://github.com/PHPOffice/PhpSpreadsheet/issues/1770) [Issue #2388](https://github.com/PHPOffice/PhpSpreadsheet/issues/2388) [Issue #2904](https://github.com/PHPOffice/PhpSpreadsheet/issues/2904) [Issue #2661](https://github.com/PHPOffice/PhpSpreadsheet/issues/2661)
+
+### Changed
+
+- Nothing
+
+### Deprecated
+
+- Nothing
+
+### Removed
+
+- Shared/JAMA is removed. [PR #3260](https://github.com/PHPOffice/PhpSpreadsheet/pull/3260)
+
+### Fixed
+
+- Namespace-Aware Code for SheetViewOptions, SheetProtection [PR #3230](https://github.com/PHPOffice/PhpSpreadsheet/pull/3230)
+- Additional Method for XIRR if Newton-Raphson Doesn't Converge [Issue #689](https://github.com/PHPOffice/PhpSpreadsheet/issues/689) [PR #3262](https://github.com/PHPOffice/PhpSpreadsheet/pull/3262)
+- Better Handling of Composite Charts [Issue #2333](https://github.com/PHPOffice/PhpSpreadsheet/issues/2333) [PR #3265](https://github.com/PHPOffice/PhpSpreadsheet/pull/3265)
+- Update Column Reference for Columns Beginning with Y and Z [Issue #3263](https://github.com/PHPOffice/PhpSpreadsheet/issues/3263) [PR #3264](https://github.com/PHPOffice/PhpSpreadsheet/pull/3264)
+- Honor Fit to 1-Page Height Html/Pdf [Issue #3266](https://github.com/PHPOffice/PhpSpreadsheet/issues/3266) [PR #3279](https://github.com/PHPOffice/PhpSpreadsheet/pull/3279)
+- AND/OR/XOR Handling of Literal Strings [PR #3287](https://github.com/PHPOffice/PhpSpreadsheet/pull/3287)
+- Xls Reader Vertical Break and Writer Page Order [Issue #3305](https://github.com/PHPOffice/PhpSpreadsheet/issues/3305) [PR #3306](https://github.com/PHPOffice/PhpSpreadsheet/pull/3306)
+
+
 ## 1.26.0 - 2022-12-21
 
 ### Added
@@ -112,7 +485,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
 
 ### Fixed
 
-- Fix update to defined names when inserting/deleting rows/columns [Issue #3076](https://github.com/PHPOffice/PhpSpreadsheet/issues/3076) [PR #3077](https://github.com/PHPOffice/PhpSpreadsheet/pull/3077) 
+- Fix update to defined names when inserting/deleting rows/columns [Issue #3076](https://github.com/PHPOffice/PhpSpreadsheet/issues/3076) [PR #3077](https://github.com/PHPOffice/PhpSpreadsheet/pull/3077)
 - Fix DataValidation sqRef when inserting/deleting rows/columns [Issue #3056](https://github.com/PHPOffice/PhpSpreadsheet/issues/3056) [PR #3074](https://github.com/PHPOffice/PhpSpreadsheet/pull/3074)
 - Named ranges not usable as anchors in OFFSET function [Issue #3013](https://github.com/PHPOffice/PhpSpreadsheet/issues/3013)
 - Fully flatten an array [Issue #2955](https://github.com/PHPOffice/PhpSpreadsheet/issues/2955) [PR #2956](https://github.com/PHPOffice/PhpSpreadsheet/pull/2956)
@@ -302,7 +675,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
 - Use color palette if supplied [Issue #2499](https://github.com/PHPOffice/PhpSpreadsheet/issues/2499) [PR #2595](https://github.com/PHPOffice/PhpSpreadsheet/pull/2595)
 - Xls reader treat drawing offsets as int rather than float [PR #2648](https://github.com/PHPOffice/PhpSpreadsheet/pull/2648)
 - Handle booleans in conditional styles properly [PR #2654](https://github.com/PHPOffice/PhpSpreadsheet/pull/2654)
-- Fix for reading files in the root directory of a ZipFile, which should not be prefixed by relative paths ("./") as dirname($filename) does by default. 
+- Fix for reading files in the root directory of a ZipFile, which should not be prefixed by relative paths ("./") as dirname($filename) does by default.
 - Fix invalid style of cells in empty columns with columnDimensions and rows with rowDimensions in added external sheet. [PR #2739](https://github.com/PHPOffice/PhpSpreadsheet/pull/2739)
 - Time Interval Formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772)
 
@@ -315,9 +688,9 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
 
 - Improved support for passing of array arguments to Excel function implementations to return array results (where appropriate). [Issue #2551](https://github.com/PHPOffice/PhpSpreadsheet/issues/2551)
 
-  This is the first stage in an ongoing process of adding array support to all appropriate function implementations, 
+  This is the first stage in an ongoing process of adding array support to all appropriate function implementations,
 - Support for the Excel365 Math/Trig SEQUENCE() function [PR #2536](https://github.com/PHPOffice/PhpSpreadsheet/pull/2536)
-- Support for the Excel365 Math/Trig RANDARRAY() function [PR #2540](https://github.com/PHPOffice/PhpSpreadsheet/pull/2540) 
+- Support for the Excel365 Math/Trig RANDARRAY() function [PR #2540](https://github.com/PHPOffice/PhpSpreadsheet/pull/2540)
 
   Note that the Spill Operator is not yet supported in the Calculation Engine; but this can still be useful for defining array constants.
 - Improved support for Conditional Formatting Rules [PR #2491](https://github.com/PHPOffice/PhpSpreadsheet/pull/2491)
@@ -331,7 +704,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
     - Expression
   - Provision of CF Wizards (for all the above listed rule types) to help create/modify CF Rules without having to manage all the combinations of types/operators, and the complexities of formula expressions, or the text/timePeriod attributes.
 
-    See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/conditional-formatting/) for details 
+    See [documentation](https://phpspreadsheet.readthedocs.io/en/latest/topics/conditional-formatting/) for details
 
   - Full support of the above CF Rules for the Xlsx Reader and Writer; even when the file being loaded has CF rules listed in the `<extLst><ext><ConditionalFormattings>` element for the worksheet rather than the `<ConditionalFormatting>` element.
   - Provision of a CellMatcher to identify if rules are matched for a cell, and which matching style will be applied.
@@ -521,7 +894,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
 - Xls Reader changing grey background to black in Excel template [Issue #2147](https://github.com/PHPOffice/PhpSpreadsheet/issues/2147) [PR #2156](https://github.com/PHPOffice/PhpSpreadsheet/pull/2156)
 - Column width and Row height styles in the Html Reader when the value includes a unit of measure [Issue #2145](https://github.com/PHPOffice/PhpSpreadsheet/issues/2145).
 - Data Validation flags not set correctly when reading XLSX files [Issue #2224](https://github.com/PHPOffice/PhpSpreadsheet/issues/2224) [PR #2225](https://github.com/PHPOffice/PhpSpreadsheet/pull/2225)
-- Reading XLSX files without styles.xml throws an exception [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246) 
+- Reading XLSX files without styles.xml throws an exception [Issue #2246](https://github.com/PHPOffice/PhpSpreadsheet/issues/2246)
 - Improved performance of `Style::applyFromArray()` when applied to several cells [PR #1785](https://github.com/PHPOffice/PhpSpreadsheet/issues/1785).
 - Improve XLSX parsing speed if no readFilter is applied (again) - [#772](https://github.com/PHPOffice/PhpSpreadsheet/issues/772)
 
@@ -586,7 +959,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
   `ABS()`, `ACOS()`, `ACOSH()`, `ASIN()`, `ASINH()`, `ATAN()`, `ATANH()`,
   `COS()`, `COSH()`, `DEGREES()` (rad2deg), `EXP()`, `LN()` (log), `LOG10()`,
   `RADIANS()` (deg2rad), `SIN()`, `SINH()`, `SQRT()`, `TAN()`, `TANH()`.
-  
+
   One TextData function is also affected: `REPT()` (str_repeat).
 - `formatAsDate` correctly matches language metadata, reverting c55272e
 - Formulae that previously crashed on sub function call returning excel error value now return said value.
@@ -1216,7 +1589,7 @@ Note that this will be the last 1.x branch release before the 2.x release. We wi
 - Ignore inlineStr type if formula element exists - @ncrypthic [#570](https://github.com/PHPOffice/PHPExcel/issues/570)
 - Excel 2007 Reader freezes because of conditional formatting - @rentalhost [#575](https://github.com/PHPOffice/PHPExcel/issues/575)
 - Readers will now parse files containing worksheet titles over 31 characters [#176](https://github.com/PHPOffice/PhpSpreadsheet/pull/176)
-- Fixed PHP8 deprecation warning for libxml_disable_entity_loader() [#1625](https://github.com/phpoffice/phpspreadsheet/pull/1625) 
+- Fixed PHP8 deprecation warning for libxml_disable_entity_loader() [#1625](https://github.com/phpoffice/phpspreadsheet/pull/1625)
 
 ### General
 
@@ -1239,3 +1612,6 @@ For a comprehensive list of all class changes, and a semi-automated migration pa
 ## Previous versions of PHPExcel
 
 The changelog for the project when it was called PHPExcel is [still available](./CHANGELOG.PHPExcel.md).
+
+### Changed
+- Replace ezyang/htmlpurifier (LGPL2.1) with voku/anti-xss (MIT)
diff --git a/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md b/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
index f595353..e89e99e 100644
--- a/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
+++ b/vendor/phpoffice/phpspreadsheet/CONTRIBUTING.md
@@ -2,19 +2,44 @@
 
 If you would like to contribute, here are some notes and guidelines:
 
- - All new development happens on feature/fix branches, and are then merged to the `master` branch once stable; so the `master` branch is always the most up-to-date, working code
- - Tagged releases are made from the `master` branch
- - If you are going to be submitting a pull request, please fork from `master`, and submit your pull request back as a fix/feature branch referencing the GitHub issue number
- - Code style might be automatically fixed by `composer fix`
- - All code changes must be validated by `composer check`
+ - All new development should be on feature/fix branches, which are then merged to the `master` branch once stable and approved; so the `master` branch is always the most up-to-date, working code
+ - If you are going to submit a pull request, please fork from `master`, and submit your pull request back as a fix/feature branch referencing the GitHub issue number
+ - The code must work with all PHP versions that we support.
+   - You can call `composer versions` to test version compatibility. 
+ - Code style should be maintained.
+   - `composer style` will identify any issues with Coding Style`.
+   - `composer fix` will fix most issues with Coding Style.
+ - All code changes must be validated by `composer check`.
+ - Please include Unit Tests to verify that a bug exists, and that this PR fixes it.
+ - Please include Unit Tests to show that a new Feature works as expected.
+ - Please don't "bundle" several changes into a single PR; submit a PR for each discrete change/fix.
+ - Remember to update documentation if necessary.
+
  - [Helpful article about forking](https://help.github.com/articles/fork-a-repo/ "Forking a GitHub repository")
  - [Helpful article about pull requests](https://help.github.com/articles/using-pull-requests/ "Pull Requests")
 
+## Unit Tests
+
+When writing Unit Tests, please
+ - Always try to write Unit Tests for both the happy and unhappy paths.
+ - Put all assertions in the Test itself, not in an abstract class that the Test extends (even if this means code duplication between tests).
+ - Include any necessary `setup()` and `tearDown()` in the Test itself.
+ - If you change any global settings (such as system locale, or Compatibility Mode for Excel Function tests), make sure that you reset to the default in the `tearDown()`.
+ - Use the `ExcelError` functions in assertions for Excel Error values in Excel Function implementations.
+   <br />Not only does it reduce the risk of typos; but at some point in the future, ExcelError values will be an object rather than a string, and we won't then need to update all the tests.
+ - Don't over-complicate test code by testing happy and unhappy paths in the same test.
+
+This makes it easier to see exactly what is being tested when reviewing the PR. I want to be able to see it in the PR, not have to hunt in other unchanged classes to see what the test is doing.
+
 ## How to release
 
 1. Complete CHANGELOG.md and commit
 2. Create an annotated tag
     1. `git tag -a 1.2.3`
     2. Tag subject must be the version number, eg: `1.2.3`
-    3. Tag body must be a copy-paste of the changelog entries
-3. Push tag with `git push --tags`, GitHub Actions will create a GitHub release automatically
+    3. Tag body must be a copy-paste of the changelog entries.
+3. Push the tag with `git push --tags`, GitHub Actions will create a GitHub release automatically, and the release details will automatically be sent to packagist.
+4. Github seems to remove markdown headings in the Release Notes, so you should edit to restore these.
+
+> **Note:** Tagged releases are made from the `master` branch. Only in an emergency should a tagged release be made from the `release` branch. (i.e. cherry-picked hot-fixes.)
+
diff --git a/vendor/phpoffice/phpspreadsheet/README.md b/vendor/phpoffice/phpspreadsheet/README.md
index 8ea5956..84b4b7b 100644
--- a/vendor/phpoffice/phpspreadsheet/README.md
+++ b/vendor/phpoffice/phpspreadsheet/README.md
@@ -11,79 +11,9 @@
 PhpSpreadsheet is a library written in pure PHP and offers a set of classes that
 allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc.
 
-## PHP Version Support
-
-LTS: Support for PHP versions will only be maintained for a period of six months beyond the
-[end of life](https://www.php.net/supported-versions) of that PHP version.
-
-Currently the required PHP minimum version is PHP __7.4__, and we [will support that version](https://www.php.net/eol.php) until 28th June 2023.
-
-See the `composer.json` for other requirements.
-
 ## Installation
 
-Use [composer](https://getcomposer.org) to install PhpSpreadsheet into your project:
-
-```sh
-composer require phpoffice/phpspreadsheet
-```
-
-If you are building your installation on a development machine that is on a different PHP version to the server where it will be deployed, or if your PHP CLI version is not the same as your run-time such as `php-fpm` or Apache's `mod_php`, then you might want to add the following to your `composer.json` before installing:
-```json
-{
-    "require": {
-        "phpoffice/phpspreadsheet": "^1.23"
-    },
-    "config": {
-        "platform": {
-            "php": "7.4"
-        }
-    }
-}
-```
-and then run
-```sh
-composer install
-```
-to ensure that the correct dependencies are retrieved to match your deployment environment.
-
-See [CLI vs Application run-time](https://php.watch/articles/composer-platform-check) for more details.
-
-### Additional Installation Options
-
-If you want to write to PDF, or to include Charts when you write to HTML or PDF, then you will need to install additional libraries:
-
-#### PDF
-
-For PDF Generation, you can install any of the following, and then configure PhpSpreadsheet to indicate which library you are going to use:
- - mpdf/mpdf
- - dompdf/dompdf
- - tecnickcom/tcpdf
-
-and configure PhpSpreadsheet using:
-
-```php
-// Dompdf, Mpdf or Tcpdf (as appropriate)
-$className = \PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf::class;
-IOFactory::registerWriter('Pdf', $className);
-```
-or the appropriate PDF Writer wrapper for the library that you have chosen to install.
-
-#### Chart Export
-
-For Chart export, we support following packages, which you will also need to install yourself using `composer require`
- - [jpgraph/jpgraph](https://packagist.org/packages/jpgraph/jpgraph) (this package was abandoned at version 4.0. 
-   You can manually download the latest version that supports PHP 8 and above from [jpgraph.net](https://jpgraph.net/))
- - [mitoteam/jpgraph](https://packagist.org/packages/mitoteam/jpgraph) (fork with php 8.1 support)
-
-and then configure PhpSpreadsheet using:
-```php
-Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph::class); // to use jpgraph/jpgraph
-//or
-Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer::class); // to use mitoteam/jpgraph
-```
-
-One or the other of these libraries is necessary if you want to generate HTML or PDF files that include charts.
+See the [install instructions](https://phpspreadsheet.readthedocs.io/en/latest/#installation).
 
 ## Documentation
 
@@ -91,6 +21,42 @@ Read more about it, including install instructions, in the [official documentati
 
 Please ask your support questions on [StackOverflow](https://stackoverflow.com/questions/tagged/phpspreadsheet), or have a quick chat on [Gitter](https://gitter.im/PHPOffice/PhpSpreadsheet).
 
+## Patreon
+
+I am now running a [Patreon](https://www.patreon.com/MarkBaker) to support the work that I do on PhpSpreadsheet.
+
+Supporters will receive access to articles about working with PhpSpreadsheet, and how to use some of its more advanced features.
+
+Posts already available to Patreon supporters:
+ - The Dating Game
+   - A  look at how MS Excel (and PhpSpreadsheet) handle date and time values.
+- Looping the Loop
+    - Advice on Iterating through the rows and cells in a worksheet.
+
+And for Patrons at levels actively using PhpSpreadsheet:
+ - Behind the Mask
+   - A look at Number Format Masks.
+
+The Next Article (currently Work in Progress):
+ - Formula for Success
+   - How to debug formulae that don't produce the expected result.
+
+
+My aim is to post at least one article each month, taking a detailed look at some feature of MS Excel and how to use that feature in PhpSpreadsheet, or on how to perform different activities in PhpSpreadsheet.
+
+Planned posts for the future include topics like:
+ - Tables
+ - Structured References
+ - AutoFiltering
+ - Array Formulae
+ - Conditional Formatting
+ - Data Validation
+ - Value Binders
+ - Images
+ - Charts
+
+After a period of six months exclusive to Patreon supporters, articles will be incorporated into the public documentation for the library.
+
 ## PHPExcel vs PhpSpreadsheet ?
 
 PhpSpreadsheet is the next version of PHPExcel. It breaks compatibility to dramatically improve the code base quality (namespaces, PSR compliance, use of latest PHP language features, etc.).
diff --git a/vendor/phpoffice/phpspreadsheet/composer.json b/vendor/phpoffice/phpspreadsheet/composer.json
index a130e12..a823d23 100644
--- a/vendor/phpoffice/phpspreadsheet/composer.json
+++ b/vendor/phpoffice/phpspreadsheet/composer.json
@@ -12,6 +12,9 @@
         "spreadsheet"
     ],
     "config": {
+        "platform": {
+            "php" : "8.1.99"
+        },
         "sort-packages": true,
         "allow-plugins": {
             "dealerdirect/phpcodesniffer-composer-installer": true
@@ -42,20 +45,27 @@
     ],
     "scripts": {
         "check": [
+            "./bin/check-phpdoc-types",
+            "phpcs samples/ src/ tests/ --report=checkstyle",
+            "phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- -n",
             "php-cs-fixer fix --ansi --dry-run --diff",
-            "phpcs",
             "phpunit --color=always",
-            "phpstan analyse --ansi"
+            "phpstan analyse --ansi --memory-limit=2048M"
+        ],
+        "style": [
+            "phpcs samples/ src/ tests/ --report=checkstyle",
+            "php-cs-fixer fix --ansi --dry-run --diff"
         ],
         "fix": [
-            "php-cs-fixer fix --ansi"
+            "phpcbf samples/ src/ tests/ --report=checkstyle",
+            "php-cs-fixer fix"
         ],
         "versions": [
-            "phpcs --report-width=200 samples/ src/ tests/ --ignore=samples/Header.php --standard=PHPCompatibility --runtime-set testVersion 7.4- -n"
+            "phpcs samples/ src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.0- -n"
         ]
     },
     "require": {
-        "php": "^7.4 || ^8.0",
+        "php": "^8.1",
         "ext-ctype": "*",
         "ext-dom": "*",
         "ext-fileinfo": "*",
@@ -69,8 +79,7 @@
         "ext-xmlwriter": "*",
         "ext-zip": "*",
         "ext-zlib": "*",
-        "ezyang/htmlpurifier": "^4.15",
-        "maennchen/zipstream-php": "^2.1",
+        "maennchen/zipstream-php": "^2.1 || ^3.0",
         "markbaker/complex": "^3.0",
         "markbaker/matrix": "^3.0",
         "psr/http-client": "^1.0",
@@ -78,15 +87,15 @@
         "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
     },
     "require-dev": {
-        "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
-        "dompdf/dompdf": "^1.0 || ^2.0",
+        "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+        "dompdf/dompdf": "^2.0 || ^3.0",
         "friendsofphp/php-cs-fixer": "^3.2",
-        "mitoteam/jpgraph": "^10.2.4",
+        "mitoteam/jpgraph": "^10.3",
         "mpdf/mpdf": "^8.1.1",
         "phpcompatibility/php-compatibility": "^9.3",
         "phpstan/phpstan": "^1.1",
         "phpstan/phpstan-phpunit": "^1.0",
-        "phpunit/phpunit": "^8.5 || ^9.0",
+        "phpunit/phpunit": "^9.6 || ^10.5",
         "squizlabs/php_codesniffer": "^3.7",
         "tecnickcom/tcpdf": "^6.5"
     },
diff --git a/vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon b/vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon
index 1e34628..364905f 100644
--- a/vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon
+++ b/vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon
@@ -1,2131 +1,2 @@
 parameters:
 	ignoreErrors:
-		-
-			message: "#^Variable \\$dateValue on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
-
-		-
-			message: "#^Variable \\$timeValue on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\BesselJ\\:\\:besselj2a\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\BesselJ\\:\\:besselj2b\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ConvertBase\\:\\:validatePlaces\\(\\) has parameter \\$places with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ConvertBase\\:\\:validateValue\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ConvertUOM\\:\\:getUOMDetails\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ConvertUOM\\:\\:resolveTemperatureSynonyms\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\Erf\\:\\:erfValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/Erf.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\Erf\\:\\:erfValue\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/Erf.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\Erf\\:\\:\\$twoSqrtPi has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/Erf.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ErfC\\:\\:erfcValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ErfC\\:\\:erfcValue\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ErfC\\:\\:\\$oneSqrtPi has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
-
-		-
-			message: "#^Parameter \\#1 \\$year of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:isLeapYear\\(\\) expects int\\|string, array\\|int\\|string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Amortization.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\Interest\\:\\:rateNextGuess\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\Interest\\:\\:rateNextGuess\\(\\) has parameter \\$futureValue with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\Interest\\:\\:rateNextGuess\\(\\) has parameter \\$numberOfPeriods with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\Interest\\:\\:rateNextGuess\\(\\) has parameter \\$payment with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\Interest\\:\\:rateNextGuess\\(\\) has parameter \\$presentValue with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\Interest\\:\\:rateNextGuess\\(\\) has parameter \\$rate with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\Interest\\:\\:rateNextGuess\\(\\) has parameter \\$type with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
-
-		-
-			message: "#^Binary operation \"\\-\" between float\\|string and 0\\|float results in an error\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\InterestAndPrincipal\\:\\:\\$interest has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Constant\\\\Periodic\\\\InterestAndPrincipal\\:\\:\\$principal has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\NonPeriodic\\:\\:xnpvOrdered\\(\\) should return float\\|string but returns array\\|string\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\CashFlow\\\\Variable\\\\Periodic\\:\\:presentValue\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
-
-		-
-			message: "#^Parameter \\#1 \\$year of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Helpers\\:\\:daysPerYear\\(\\) expects int\\|string, array\\|int\\|string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Coupons.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Depreciation\\:\\:validateCost\\(\\) has parameter \\$cost with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Depreciation\\:\\:validateFactor\\(\\) has parameter \\$factor with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Depreciation\\:\\:validateLife\\(\\) has parameter \\$life with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Depreciation\\:\\:validateMonth\\(\\) has parameter \\$month with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Depreciation\\:\\:validatePeriod\\(\\) has parameter \\$period with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Depreciation\\:\\:validateSalvage\\(\\) has parameter \\$salvage with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
-
-		-
-			message: "#^Binary operation \"/\" between float\\|string and float\\|string results in an error\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Securities\\\\Price\\:\\:received\\(\\) should return float\\|string but returns array\\|string\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
-
-		-
-			message: "#^Parameter \\#1 \\$year of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Helpers\\:\\:daysPerYear\\(\\) expects int\\|string, array\\|int\\|string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
-
-		-
-			message: "#^Parameter \\#1 \\$year of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Financial\\\\Helpers\\:\\:daysPerYear\\(\\) expects int\\|string, array\\|int\\|string given\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
-
-		-
-			message: "#^Cannot call method getTokenSubType\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\FormulaToken\\|null\\.$#"
-			count: 4
-			path: src/PhpSpreadsheet/Calculation/FormulaParser.php
-
-		-
-			message: "#^Cannot call method getTokenType\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\FormulaToken\\|null\\.$#"
-			count: 9
-			path: src/PhpSpreadsheet/Calculation/FormulaParser.php
-
-		-
-			message: "#^Cannot call method setTokenSubType\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\FormulaToken\\|null\\.$#"
-			count: 5
-			path: src/PhpSpreadsheet/Calculation/FormulaParser.php
-
-		-
-			message: "#^Cannot call method setValue\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\FormulaToken\\|null\\.$#"
-			count: 5
-			path: src/PhpSpreadsheet/Calculation/FormulaParser.php
-
-		-
-			message: "#^Strict comparison using \\=\\=\\= between PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\FormulaToken and null will always evaluate to false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/FormulaParser.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:ifCondition\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:ifCondition\\(\\) has parameter \\$condition with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:isCellValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:isCellValue\\(\\) has parameter \\$idx with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:isMatrixValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:isMatrixValue\\(\\) has parameter \\$idx with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:isValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:isValue\\(\\) has parameter \\$idx with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:operandSpecialHandling\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Functions\\:\\:operandSpecialHandling\\(\\) has parameter \\$operand with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Functions.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Internal\\\\MakeMatrix\\:\\:make\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
-
-		-
-			message: "#^Call to function is_string\\(\\) with null will always evaluate to false\\.$#"
-			count: 3
-			path: src/PhpSpreadsheet/Calculation/Logical/Operations.php
-
-		-
-			message: "#^Result of && is always false\\.$#"
-			count: 3
-			path: src/PhpSpreadsheet/Calculation/Logical/Operations.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\:\\:CHOOSE\\(\\) has parameter \\$chooseArgs with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\:\\:OFFSET\\(\\) should return array\\|string but returns array\\|int\\|string\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Address\\:\\:sheetName\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Lookup\\:\\:verifyResultVector\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Lookup\\:\\:verifyResultVector\\(\\) has parameter \\$resultVector with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\LookupBase\\:\\:validateIndexLookup\\(\\) has parameter \\$index_number with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Matrix\\:\\:extractRowValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:adjustEndCellColumnForWidth\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:adjustEndCellColumnForWidth\\(\\) has parameter \\$columns with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:adjustEndCellColumnForWidth\\(\\) has parameter \\$width with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:adustEndCellRowForHeight\\(\\) has parameter \\$endCellRow with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:adustEndCellRowForHeight\\(\\) has parameter \\$height with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:adustEndCellRowForHeight\\(\\) has parameter \\$rows with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:extractRequiredCells\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Offset\\:\\:extractWorksheet\\(\\) has parameter \\$cellAddress with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
-		-
-			message: "#^Binary operation \"/\" between array\\|float\\|int\\|string and array\\|float\\|int\\|string results in an error\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
-
-		-
-			message: "#^Binary operation \"/\" between array\\|float\\|int\\|string and float\\|int results in an error\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\IntClass\\:\\:evaluate\\(\\) should return array\\|string but returns int\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\:\\:MAXIFS\\(\\) should return float but returns float\\|string\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\:\\:MINIFS\\(\\) should return float but returns float\\|string\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Averages\\:\\:filterArguments\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Averages.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Averages\\:\\:filterArguments\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Averages.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Averages\\:\\:modeCalc\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Averages.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Averages\\:\\:modeCalc\\(\\) has parameter \\$data with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Averages.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Conditional\\:\\:SUMIF\\(\\) should return float\\|string but returns float\\|string\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Conditional\\:\\:buildConditionSet\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Conditional\\:\\:buildConditionSetForValueRange\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Conditional\\:\\:buildConditions\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Conditional\\:\\:buildDataSet\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Conditional\\:\\:buildDatabase\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Conditional\\:\\:buildDatabaseWithValueRange\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\Beta\\:\\:\\$logBetaCacheP has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\Beta\\:\\:\\$logBetaCacheQ has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\Beta\\:\\:\\$logBetaCacheResult has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
-
-		-
-			message: "#^Constant PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:MAX_ITERATIONS is unused\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gammp\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gammp\\(\\) has parameter \\$n with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gammp\\(\\) has parameter \\$x with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gcf\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gcf\\(\\) has parameter \\$n with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gcf\\(\\) has parameter \\$x with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gser\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gser\\(\\) has parameter \\$n with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:gser\\(\\) has parameter \\$x with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:pchisq\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:pchisq\\(\\) has parameter \\$chi2 with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:pchisq\\(\\) has parameter \\$degrees with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Parameter \\#2 \\$columns of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\ChiSquared\\:\\:degrees\\(\\) expects int, float\\|int\\<0, max\\> given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:calculateDistribution\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:calculateInverse\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:logGamma1\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:logGamma2\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:logGamma3\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:logGamma4\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:\\$logGammaCacheResult has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\GammaBase\\:\\:\\$logGammaCacheX has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\NewtonRaphson\\:\\:execute\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\NewtonRaphson\\:\\:\\$callback has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\Normal\\:\\:inverseNcdf\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\Normal\\:\\:inverseNcdf\\(\\) has parameter \\$p with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
-
-		-
-			message: "#^Binary operation \"\\-\" between float\\|string and float\\|int\\|numeric\\-string results in an error\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
-
-		-
-			message: "#^Constant PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Distributions\\\\StudentT\\:\\:MAX_ITERATIONS is unused\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\MaxMinBase\\:\\:datatypeAdjustmentAllowStrings\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\MaxMinBase\\:\\:datatypeAdjustmentAllowStrings\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Percentiles\\:\\:percentileFilterValues\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Percentiles\\:\\:rankFilterValues\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
-
-		-
-			message: "#^Binary operation \"/\" between array\\|float\\|int\\|string and array\\|float\\|int\\|string results in an error\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Trends\\:\\:GROWTH\\(\\) should return array\\<float\\> but returns array\\<int, array\\<int, array\\<int, mixed\\>\\>\\>\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Trends\\:\\:TREND\\(\\) should return array\\<float\\> but returns array\\<int, array\\<int, array\\<int, mixed\\>\\>\\>\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Trends\\:\\:checkTrendArrays\\(\\) has parameter \\$array1 with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\Trends\\:\\:checkTrendArrays\\(\\) has parameter \\$array2 with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/Trends.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\VarianceBase\\:\\:datatypeAdjustmentAllowStrings\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\VarianceBase\\:\\:datatypeAdjustmentAllowStrings\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\VarianceBase\\:\\:datatypeAdjustmentBooleans\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\\\VarianceBase\\:\\:datatypeAdjustmentBooleans\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\:\\:CONCATENATE\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/TextData.php
-
-		-
-			message: "#^Variable \\$value on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 4
-			path: src/PhpSpreadsheet/Calculation/TextData/Extract.php
-
-		-
-			message: "#^Variable \\$value on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Calculation/TextData/Text.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getHashCode\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Cell/Coordinate.php
-
-		-
-			message: "#^Parameter \\#4 \\$currentRow of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:validateRange\\(\\) expects int, string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Cell/Coordinate.php
-
-		-
-			message: "#^Parameter \\#5 \\$endRow of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:validateRange\\(\\) expects int, string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Cell/Coordinate.php
-
-		-
-			message: "#^Parameter \\#1 \\$namedRange of method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\:\\:addNamedRange\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\NamedRange, \\$this\\(PhpOffice\\\\PhpSpreadsheet\\\\DefinedName\\) given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/DefinedName.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\DefinedName\\:\\:\\$scope \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
-			count: 3
-			path: src/PhpSpreadsheet/DefinedName.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\DefinedName\\:\\:\\$worksheet \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/DefinedName.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\IOFactory\\:\\:createReader\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\IReader but returns object\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/IOFactory.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\IOFactory\\:\\:createWriter\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\IWriter but returns object\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/IOFactory.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\IOFactory\\:\\:\\$readers has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/IOFactory.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\IOFactory\\:\\:\\$writers has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/IOFactory.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\BaseReader\\:\\:getSecurityScanner\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/BaseReader.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\BaseReader\\:\\:\\$fileHandle has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/BaseReader.php
-
-		-
-			message: "#^Offset 'percentage' does not exist on SimpleXMLElement\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
-
-		-
-			message: "#^Offset 'value' does not exist on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
-
-		-
-			message: "#^Variable \\$orientation on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
-
-		-
-			message: "#^Variable \\$value on left side of \\?\\? always exists and is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Html.php
-
-		-
-			message: "#^Cannot call method children\\(\\) on SimpleXMLElement\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Cannot call method getAttributeNS\\(\\) on DOMElement\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Cannot call method getElementsByTagNameNS\\(\\) on DOMElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Cannot call method getNamedItem\\(\\) on DOMNamedNodeMap\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Cannot call method getNamespaces\\(\\) on SimpleXMLElement\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\:\\:listWorksheetNames\\(\\) should return array\\<string\\> but returns array\\<int, string\\|null\\>\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Parameter \\#1 \\$element of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\:\\:scanElementForText\\(\\) expects DOMNode, DOMElement\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Parameter \\#1 \\$settings of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\:\\:lookForActiveSheet\\(\\) expects DOMElement, DOMElement\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Parameter \\#1 \\$settings of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\:\\:lookForSelectedCells\\(\\) expects DOMElement, DOMElement\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods.php
-
-		-
-			message: "#^Cannot call method getElementsByTagNameNS\\(\\) on DOMElement\\|null\\.$#"
-			count: 3
-			path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
-		-
-			message: "#^Expression on left side of \\?\\? is not nullable\\.$#"
-			count: 6
-			path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$pageLayoutStyles has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:load\\(\\) has parameter \\$namespacesMeta with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods/Properties.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:setMetaProperties\\(\\) has parameter \\$namespacesMeta with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods/Properties.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:setMetaProperties\\(\\) has parameter \\$propertyName with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods/Properties.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:setUserDefinedProperty\\(\\) has parameter \\$propertyValue with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods/Properties.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:setUserDefinedProperty\\(\\) has parameter \\$propertyValueAttributes with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods/Properties.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:\\$spreadsheet has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Ods/Properties.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Security\\\\XmlScanner\\:\\:__construct\\(\\) has parameter \\$pattern with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Security/XmlScanner.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Security\\\\XmlScanner\\:\\:getInstance\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Security/XmlScanner.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Security\\\\XmlScanner\\:\\:threadSafeLibxmlDisableEntityLoaderAvailability\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Security/XmlScanner.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Security\\\\XmlScanner\\:\\:\\$callback has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Security/XmlScanner.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Security\\\\XmlScanner\\:\\:\\$libxmlDisableEntityLoaderValue has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Security/XmlScanner.php
-
-		-
-			message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\\\SpContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\\\BSE\\:\\:getDgContainer\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\\\SpContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\\\BSE\\:\\:getDggContainer\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getEndCoordinates\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getEndOffsetX\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getEndOffsetY\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getNestingLevel\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getOPT\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getStartCoordinates\\(\\)\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getStartOffsetX\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getStartOffsetY\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Cannot access offset 1 on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^If condition is always true\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:includeCellRangeFiltered\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:includeCellRangeFiltered\\(\\) has parameter \\$cellRangeAddress with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:parseRichText\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:parseRichText\\(\\) has parameter \\$is with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Negated boolean expression is always false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#1 \\$block of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:makeKey\\(\\) expects int, float given\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#1 \\$showSummaryBelow of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:setShowSummaryBelow\\(\\) expects bool, int given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#1 \\$showSummaryRight of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:setShowSummaryRight\\(\\) expects bool, int given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#2 \\$row of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\IReadFilter\\:\\:readCell\\(\\) expects int, string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#2 \\$row of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Xls\\:\\:sizeRow\\(\\) expects int, string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#2 \\$startRow of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Xls\\:\\:getDistanceY\\(\\) expects int, string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, int\\|string\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#4 \\$endRow of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Xls\\:\\:getDistanceY\\(\\) expects int, string given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:\\$data \\(string\\) does not accept string\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:\\$documentSummaryInformation \\(string\\) does not accept string\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:\\$documentSummaryInformation \\(string\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:\\$md5Ctxt is never written, only read\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:\\$summaryInformation \\(string\\) does not accept string\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:\\$summaryInformation \\(string\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Unreachable statement \\- code above always terminates\\.$#"
-			count: 7
-			path: src/PhpSpreadsheet/Reader/Xls.php
-
-		-
-			message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xml.php
-
-		-
-			message: "#^Parameter \\#1 \\$textValue of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:convertEncoding\\(\\) expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xml.php
-
-		-
-			message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xml.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xml\\:\\:\\$fileContents has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xml.php
-
-		-
-			message: "#^Argument of an invalid type SimpleXMLElement\\|null supplied for foreach, only iterables are supported\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xml/Properties.php
-
-		-
-			message: "#^Argument of an invalid type SimpleXMLElement\\|null supplied for foreach, only iterables are supported\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Reader/Xml/Style.php
-
-		-
-			message: "#^Elseif condition is always true\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/ReferenceHelper.php
-
-		-
-			message: "#^Expression on left side of \\?\\? is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/ReferenceHelper.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\Run\\:\\:\\$font \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/RichText/Run.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:getDgId\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:getLastSpId\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:getSpgrContainer\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:setDgId\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:setLastSpId\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:setSpgrContainer\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:setSpgrContainer\\(\\) has parameter \\$spgrContainer with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\:\\:\\$spgrContainer has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\:\\:getChildren\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\\\BSE\\:\\:\\$parent is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:arrayLeftDivide\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:arrayLeftDivideEquals\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:arrayRightDivide\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:arrayRightDivideEquals\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:arrayTimes\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:arrayTimesEquals\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:concat\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:getMatrix\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:minus\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:minusEquals\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:plus\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:plusEquals\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:power\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:times\\(\\) has parameter \\$args with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Parameter \\#3 \\$c of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\JAMA\\\\Matrix\\:\\:set\\(\\) expects float\\|int\\|null, string given\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Unreachable statement \\- code above always terminates\\.$#"
-			count: 14
-			path: src/PhpSpreadsheet/Shared/JAMA/Matrix.php
-
-		-
-			message: "#^Cannot access offset 1 on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Cannot access offset 2 on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Cannot access offset 3 on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Cannot access offset 4 on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\:\\:getData\\(\\) should return string but returns string\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\:\\:getStream\\(\\) should return resource but returns resource\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#1 \\$No of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS constructor expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#1 \\$oleTimestamp of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\:\\:OLE2LocalDate\\(\\) expects string, string\\|false given\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#2 \\$name of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS constructor expects string, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#3 \\$type of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS constructor expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#4 \\$prev of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS constructor expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#5 \\$next of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS constructor expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#6 \\$dir of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS constructor expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Parameter \\#9 \\$data of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS constructor expects string, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\:\\:\\$root \\(PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\\\Root\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:\\$_data \\(string\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:\\$startBlock \\(int\\) on left side of \\?\\? is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS.php
-
-		-
-			message: "#^Parameter \\#1 \\$No of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/File.php
-
-		-
-			message: "#^Parameter \\#4 \\$prev of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/File.php
-
-		-
-			message: "#^Parameter \\#5 \\$next of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/File.php
-
-		-
-			message: "#^Parameter \\#6 \\$dir of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/File.php
-
-		-
-			message: "#^Parameter \\#1 \\$No of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
-
-		-
-			message: "#^Parameter \\#4 \\$prev of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
-
-		-
-			message: "#^Parameter \\#5 \\$next of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
-
-		-
-			message: "#^Parameter \\#6 \\$dir of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
-
-		-
-			message: "#^Parameter \\#9 \\$data of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\:\\:__construct\\(\\) expects string, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\\\Root\\:\\:\\$bigBlockSize \\(int\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLE\\\\PPS\\\\Root\\:\\:\\$smallBlockSize \\(int\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
-
-		-
-			message: "#^Parameter \\#1 \\$data of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLERead\\:\\:getInt4d\\(\\) expects string, string\\|false given\\.$#"
-			count: 8
-			path: src/PhpSpreadsheet/Shared/OLERead.php
-
-		-
-			message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Shared/OLERead.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLERead\\:\\:\\$data has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLERead.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLERead\\:\\:\\$documentSummaryInformation has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLERead.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLERead\\:\\:\\$summaryInformation has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLERead.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\OLERead\\:\\:\\$wrkbook has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/OLERead.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$const with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$meanX with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$meanY with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$sumX with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$sumX2 with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$sumXY with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$sumY with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:calculateGoodnessOfFit\\(\\) has parameter \\$sumY2 with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:getBestFitType\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:getError\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:sumSquares\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$DFResiduals has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$SSRegression has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$SSResiduals has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$correlation has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$covariance has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$f has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$goodnessOfFit has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$intersect has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$intersectSE has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$slope has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$slopeSE has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$stdevOfResiduals has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$xOffset has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\BestFit\\:\\:\\$yOffset has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/BestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\PolynomialBestFit\\:\\:getCoefficients\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\PolynomialBestFit\\:\\:getCoefficients\\(\\) has parameter \\$dp with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
-
-		-
-			message: "#^Call to an undefined method object\\:\\:getGoodnessOfFit\\(\\)\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\Trend\\:\\:calculate\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\Trend\\:\\:calculate\\(\\) has parameter \\$const with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\Trend\\:\\:calculate\\(\\) has parameter \\$trendType with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\Trend\\:\\:calculate\\(\\) has parameter \\$xValues with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\Trend\\:\\:calculate\\(\\) has parameter \\$yValues with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBar\\:\\:setConditionalFormattingRuleExt\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBar\\:\\:setMaximumConditionalFormatValueObject\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBar\\:\\:setMinimumConditionalFormatValueObject\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBar\\:\\:setShowValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBarExtension\\:\\:getXmlAttributes\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBarExtension\\:\\:getXmlElements\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBarExtension\\:\\:setMaximumConditionalFormatValueObject\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalDataBarExtension\\:\\:setMinimumConditionalFormatValueObject\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:__construct\\(\\) has parameter \\$type with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:__construct\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:setCellFormula\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:setType\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:setValue\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:\\$cellFormula has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:\\$type has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormatValueObject\\:\\:\\$value has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
-
-		-
-			message: "#^Cannot access property \\$axisPosition on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Cannot access property \\$border on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Cannot access property \\$direction on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Cannot access property \\$gradient on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Cannot access property \\$maxLength on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Cannot access property \\$minLength on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Cannot access property \\$negativeBarBorderColorSameAsPositive on SimpleXMLElement\\|null\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormattingRuleExtension\\:\\:__construct\\(\\) has parameter \\$id with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormattingRuleExtension\\:\\:generateUuid\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormattingRuleExtension\\:\\:parseExtDataBarElementChildrenFromXml\\(\\) has parameter \\$ns with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormattingRuleExtension\\:\\:parseExtLstXml\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormattingRuleExtension\\:\\:parseExtLstXml\\(\\) has parameter \\$extLstXml with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Offset 'rgb' does not exist on SimpleXMLElement\\|null\\.$#"
-			count: 4
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Offset 'theme' does not exist on SimpleXMLElement\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Offset 'tint' does not exist on SimpleXMLElement\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\ConditionalFormatting\\\\ConditionalFormattingRuleExtension\\:\\:\\$id has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormat\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormat\\(\\) has parameter \\$sections with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormat\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormatCompare\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormatCompare\\(\\) has parameter \\$cond with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormatCompare\\(\\) has parameter \\$dfcond with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormatCompare\\(\\) has parameter \\$dfval with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormatCompare\\(\\) has parameter \\$val with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:splitFormatCompare\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\Formatter\\:\\:toFormattedString\\(\\) should return string but returns float\\|int\\|string\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\PercentageFormatter\\:\\:format\\(\\) has parameter \\$value with no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\CellIterator\\:\\:adjustForExistingOnlyRange\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/CellIterator.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\:\\:\\$color \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\PageSetup\\:\\:getPrintArea\\(\\) should return string but returns string\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/PageSetup.php
-
-		-
-			message: "#^Cannot call method getCalculatedValue\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Cannot call method getValue\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#"
-			count: 4
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Cannot call method getWorksheet\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\DefinedName\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Cannot call method getXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Cannot call method rangeToArray\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^If condition is always true\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Left side of && is always true\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:getChartByName\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\|false but returns PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Parameter \\#1 \\$index of class PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension constructor expects string, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Parameter \\#1 \\$index of class PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension constructor expects int, null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Parameter \\#1 \\$range of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:rangeDimension\\(\\) expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Parameter \\#2 \\$format of static method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\:\\:toFormattedString\\(\\) expects string, string\\|null given\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Parameter \\#3 \\$rotation of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Font\\:\\:calculateColumnWidth\\(\\) expects int, int\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:\\$parent \\(PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Right side of && is always true\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Worksheet/Worksheet.php
-
-		-
-			message: "#^Negated boolean expression is always false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Ods.php
-
-		-
-			message: "#^If condition is always true\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Writer/Ods/Cell/Style.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Ods\\\\Cell\\\\Style\\:\\:\\$writer has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Ods/Cell/Style.php
-
-		-
-			message: "#^Parameter \\#1 \\$range of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:splitRange\\(\\) expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Ods/Content.php
-
-		-
-			message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int\\<2, max\\> given\\.$#"
-			count: 3
-			path: src/PhpSpreadsheet/Writer/Ods/Content.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Ods\\\\Content\\:\\:\\$formulaConvertor has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Ods/Content.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Ods\\\\Formula\\:\\:\\$definedNames has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Ods/Formula.php
-
-		-
-			message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Cannot use array destructuring on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Offset 'endCoordinates' does not exist on array\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Offset 'endOffsetX' does not exist on array\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Offset 'endOffsetY' does not exist on array\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Offset 'startCoordinates' does not exist on array\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Offset 'startOffsetX' does not exist on array\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Offset 'startOffsetY' does not exist on array\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Parameter \\#1 \\$blipType of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\\\BSE\\:\\:setBlipType\\(\\) expects int, int\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Parameter \\#1 \\$data of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\\\BSE\\\\Blip\\:\\:setData\\(\\) expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Parameter \\#1 \\$font of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Workbook\\:\\:addFont\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font, PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\:\\:\\$documentSummaryInformation \\(string\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\:\\:\\$summaryInformation \\(string\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\BIFFwriter\\:\\:writeEof\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
-
-		-
-			message: "#^Static property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\BIFFwriter\\:\\:\\$byteOrder \\(int\\) in isset\\(\\) is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
-
-		-
-			message: "#^Elseif condition is always true\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Escher.php
-
-		-
-			message: "#^If condition is always true\\.$#"
-			count: 3
-			path: src/PhpSpreadsheet/Writer/Xls/Escher.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Escher\\:\\:\\$data has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Escher.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Escher\\:\\:\\$object has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Escher.php
-
-		-
-			message: "#^Cannot access offset 'encoding' on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Workbook.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Workbook\\:\\:writeAllDefinedNamesBiff8\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Workbook.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Workbook\\:\\:writeExternalsheetBiff8\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Workbook.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Workbook\\:\\:writeMsoDrawingGroup\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Workbook.php
-
-		-
-			message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Workbook\\:\\:writeSupbookInternal\\(\\) has no return type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Workbook.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Workbook\\:\\:\\$biffSize is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Workbook.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Workbook\\:\\:\\$colors has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Workbook.php
-
-		-
-			message: "#^Cannot access offset 'comp' on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Cannot access offset 'ident' on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Cannot access offset 'sa' on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Cannot access offset 1 on array\\|false\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Cannot access offset 2 on array\\|false\\.$#"
-			count: 2
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Expression on left side of \\?\\? is not nullable\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Parameter \\#2 \\$length of function fread expects int\\<0, max\\>, int\\<0, max\\>\\|false given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Parameter \\#4 \\$isError of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:writeBoolErr\\(\\) expects bool, int given\\.$#"
-			count: 3
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Parameter \\#5 \\$width of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:positionImage\\(\\) expects int, float given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Parameter \\#6 \\$height of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:positionImage\\(\\) expects int, float given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:\\$activePane \\(int\\) does not accept int\\|null\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:\\$colors has no type specified\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:\\$countCellStyleXfs is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:\\$outlineBelow is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:\\$outlineRight is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:\\$selection is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:\\$xlsStringMaxLength is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
-		-
-			message: "#^Parameter \\#1 \\$textRotation of static method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Xf\\:\\:mapTextRotation\\(\\) expects int, int\\|null given\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Xf.php
-
-		-
-			message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Xf\\:\\:\\$diag is never read, only written\\.$#"
-			count: 1
-			path: src/PhpSpreadsheet/Writer/Xls/Xf.php
diff --git a/vendor/phpoffice/phpspreadsheet/phpstan-conditional.php b/vendor/phpoffice/phpspreadsheet/phpstan-conditional.php
deleted file mode 100644
index eeb21d1..0000000
--- a/vendor/phpoffice/phpspreadsheet/phpstan-conditional.php
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-$config = [];
-
-if (PHP_VERSION_ID < 80000) {
-    // GdImage not available before PHP8
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '~^Method .* has invalid return type GdImage\.$~',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/Drawing.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '~^Property .* has unknown class GdImage as its type\.$~',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '~^Method .* has invalid return type GdImage\.$~',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '~^Parameter .* of method .* has invalid type GdImage\.$~',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '~^Class GdImage not found\.$~',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xls/Worksheet.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '~^Parameter .* of method .* has invalid type GdImage\.$~',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xls/Worksheet.php',
-        'count' => 1,
-    ];
-    // GdImage with Phpstan 1.9.2
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '~Class GdImage not found.*$~',
-        'path' => __DIR__ . '/tests/PhpSpreadsheetTests/Worksheet/MemoryDrawingTest.php',
-        'count' => 3,
-    ];
-    // Erroneous analysis by Phpstan before PHP8 - 3rd parameter is nullable
-    // Fixed for Php7 with Phpstan 1.9.
-    //$config['parameters']['ignoreErrors'][] = [
-    //    'message' => '#^Parameter \\#3 \\$namespace of method XMLWriter\\:\\:startElementNs\\(\\) expects string, null given\\.$#',
-    //    'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php',
-    //    'count' => 8,
-    //];
-    // Erroneous analysis by Phpstan before PHP8 - mb_strlen does not return false
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:countCharacters\\(\\) should return int but returns int(<0, max>)?\\|false\\.$#',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/StringHelper.php',
-        'count' => 1,
-    ];
-    // New with Phpstan 1.9.2 for Php7 only
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, array<int, mixed>\\|false given.$#',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '#^Parameter \\#1 \\$input of function array_chunk expects array, array<int, float\\|int>\\|false given.$#',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '#^Parameter \\#2 \\$array of function array_map expects array, array<int, float|int>\\|false given.$#',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/MathTrig/Random.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, array<int, mixed>\\|false given.$#',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/TextData/Text.php',
-        'count' => 1,
-    ];
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '#^Property PhpOffice\\PhpSpreadsheet\\Shared\\JAMA\\Matrix::$A (array) does not accept array<int, array<int, int>|false>|false.$#',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/JAMA/Matrix.php',
-        'count' => 2,
-    ];
-} else {
-    // Flagged in Php8+ - unsure how to correct code
-    $config['parameters']['ignoreErrors'][] = [
-        'message' => '#^Binary operation "/" between float and array[|]float[|]int[|]string results in an error.#',
-        'path' => __DIR__ . '/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php',
-        'count' => 2,
-    ];
-}
-
-return $config;
diff --git a/vendor/phpoffice/phpspreadsheet/phpstan.neon.dist b/vendor/phpoffice/phpspreadsheet/phpstan.neon.dist
index 30bd6c2..06fbbcd 100644
--- a/vendor/phpoffice/phpspreadsheet/phpstan.neon.dist
+++ b/vendor/phpoffice/phpspreadsheet/phpstan.neon.dist
@@ -1,23 +1,29 @@
 includes:
     - phpstan-baseline.neon
-    - phpstan-conditional.php
     - vendor/phpstan/phpstan-phpunit/extension.neon
     - vendor/phpstan/phpstan-phpunit/rules.neon
 
 parameters:
     level: 8
     paths:
+        - samples/
         - src/
         - tests/
+        - infra/
+        - bin/generate-document
+        - bin/generate-locales
+        - bin/check-phpdoc-types
     excludePaths:
         - src/PhpSpreadsheet/Chart/Renderer/JpGraph.php
         - src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php
+        - src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php
+        - src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
+        - src/PhpSpreadsheet/Writer/ZipStream2.php
+        - src/PhpSpreadsheet/Writer/ZipStream3.php
     parallel:
         processTimeout: 300.0
-    checkMissingIterableValueType: false
     ignoreErrors:
-        - '~^Parameter \#1 \$im(age)? of function (imagedestroy|imageistruecolor|imagealphablending|imagesavealpha|imagecolortransparent|imagecolorsforindex|imagesavealpha|imagesx|imagesy|imagepng) expects (GdImage|resource), GdImage\|resource given\.$~'
-        - '~^Parameter \#2 \$src_im(age)? of function imagecopy expects (GdImage|resource), GdImage\|resource given\.$~'
         # Accept a bit anything for assert methods
         - '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~'
-        - '~^Method PhpOffice\\PhpSpreadsheetTests\\.*\:\:test.*\(\) has parameter \$args with no type specified\.$~'
+        - '~^Variable \$helper might not be defined\.$~'
+        - identifier: missingType.iterableValue
diff --git a/vendor/phpoffice/phpspreadsheet/phpunit9.xml.dist b/vendor/phpoffice/phpspreadsheet/phpunit9.xml.dist
new file mode 100644
index 0000000..896b733
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/phpunit9.xml.dist
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="./tests/bootstrap.php" backupGlobals="true" colors="true" cacheResultFile="/tmp/.phpspreadsheet.phpunit.result.cache" convertDeprecationsToExceptions="true">
+  <coverage>
+    <include>
+      <directory suffix=".php">./src</directory>
+    </include>
+  </coverage>
+  <php>
+    <ini name="memory_limit" value="2048M"/>
+    <ini name="error_reporting" value="E_ALL"/>
+  </php>
+  <testsuite name="PhpSpreadsheet Unit Test Suite">
+    <directory>./tests/PhpSpreadsheetTests</directory>
+  </testsuite>
+</phpunit>
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php
index 1e3f697..7b78b6f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php
@@ -7,18 +7,18 @@ use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentProcessor;
 
 trait ArrayEnabled
 {
-    /**
-     * @var ArrayArgumentHelper
-     */
-    private static $arrayArgumentHelper;
+    private static bool $initializationNeeded = true;
+
+    private static ArrayArgumentHelper $arrayArgumentHelper;
 
     /**
      * @param array|false $arguments Can be changed to array for Php8.1+
      */
     private static function initialiseHelper($arguments): void
     {
-        if (self::$arrayArgumentHelper === null) {
+        if (self::$initializationNeeded === true) {
             self::$arrayArgumentHelper = new ArrayArgumentHelper();
+            self::$initializationNeeded = false;
         }
         self::$arrayArgumentHelper->initialise(($arguments === false) ? [] : $arguments);
     }
@@ -43,10 +43,8 @@ trait ArrayEnabled
      *     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
+    protected static function evaluateArrayArguments(callable $method, mixed ...$arguments): array
     {
         self::initialiseHelper($arguments);
         $arguments = self::$arrayArgumentHelper->arguments();
@@ -60,10 +58,8 @@ trait ArrayEnabled
      * 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
+    protected static function evaluateArrayArgumentsSubset(callable $method, int $limit, mixed ...$arguments): array
     {
         self::initialiseHelper(array_slice($arguments, 0, $limit));
         $trailingArguments = array_slice($arguments, $limit);
@@ -73,10 +69,7 @@ trait ArrayEnabled
         return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments);
     }
 
-    /**
-     * @param mixed $value
-     */
-    private static function testFalse($value): bool
+    private static function testFalse(mixed $value): bool
     {
         return $value === false;
     }
@@ -87,10 +80,8 @@ trait ArrayEnabled
      * 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
+    protected static function evaluateArrayArgumentsSubsetFrom(callable $method, int $start, mixed ...$arguments): array
     {
         $arrayArgumentsSubset = array_combine(
             range($start, count($arguments) - $start),
@@ -114,10 +105,8 @@ trait ArrayEnabled
      * 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
+    protected static function evaluateArrayArgumentsIgnore(callable $method, int $ignore, mixed ...$arguments): array
     {
         $leadingArguments = array_slice($arguments, 0, $ignore);
         $ignoreArgument = array_slice($arguments, $ignore, 1);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php
index 5d4f261..e4bc156 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php
@@ -17,7 +17,7 @@ class BinaryComparison
      * @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
+    private static function strcmpLowercaseFirst(?string $str1, ?string $str2): int
     {
         $inversedStr1 = StringHelper::strCaseReverse($str1 ?? '');
         $inversedStr2 = StringHelper::strCaseReverse($str2 ?? '');
@@ -31,16 +31,12 @@ class BinaryComparison
      * @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
+    private static function strcmpAllowNull(?string $str1, ?string $str2): int
     {
         return strcmp($str1 ?? '', $str2 ?? '');
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     */
-    public static function compare($operand1, $operand2, string $operator): bool
+    public static function compare(mixed $operand1, mixed $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) {
@@ -60,48 +56,27 @@ class BinaryComparison
             }
         }
 
-        $useLowercaseFirstComparison = is_string($operand1) &&
-            is_string($operand2) &&
-            Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE;
+        $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
+    private static function evaluateComparison(mixed $operand1, mixed $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');
-        }
+        return match ($operator) {
+            '=' => self::equal($operand1, $operand2),
+            '>' => self::greaterThan($operand1, $operand2, $useLowercaseFirstComparison),
+            '<' => self::lessThan($operand1, $operand2, $useLowercaseFirstComparison),
+            '>=' => self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
+            '<=' => self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
+            '<>' => 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
+    private static function equal(mixed $operand1, mixed $operand2): bool
     {
         if (is_numeric($operand1) && is_numeric($operand2)) {
             $result = (abs($operand1 - $operand2) < self::DELTA);
@@ -114,11 +89,7 @@ class BinaryComparison
         return $result;
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     */
-    private static function greaterThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+    private static function greaterThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
     {
         if (is_numeric($operand1) && is_numeric($operand2)) {
             $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 > $operand2));
@@ -133,11 +104,7 @@ class BinaryComparison
         return $result;
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     */
-    private static function lessThanOrEqual($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+    private static function lessThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
     {
         if (is_numeric($operand1) && is_numeric($operand2)) {
             $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 < $operand2));
@@ -152,29 +119,17 @@ class BinaryComparison
         return $result;
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     */
-    private static function greaterThan($operand1, $operand2, bool $useLowercaseFirstComparison): bool
+    private static function greaterThan(mixed $operand1, mixed $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
+    private static function lessThan(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
     {
         return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     */
-    private static function notEqual($operand1, $operand2): bool
+    private static function notEqual(mixed $operand1, mixed $operand2): bool
     {
         return self::equal($operand1, $operand2) !== true;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
index 240e5a0..8d622d4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
@@ -6,7 +6,6 @@ use PhpOffice\PhpSpreadsheet\Calculation\Engine\BranchPruner;
 use PhpOffice\PhpSpreadsheet\Calculation\Engine\CyclicReferenceStack;
 use PhpOffice\PhpSpreadsheet\Calculation\Engine\Logger;
 use PhpOffice\PhpSpreadsheet\Calculation\Engine\Operands;
-use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Calculation\Token\Stack;
 use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
@@ -14,6 +13,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\DefinedName;
+use PhpOffice\PhpSpreadsheet\NamedRange;
 use PhpOffice\PhpSpreadsheet\ReferenceHelper;
 use PhpOffice\PhpSpreadsheet\Shared;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -21,6 +21,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 use ReflectionClassConstant;
 use ReflectionMethod;
 use ReflectionParameter;
+use Throwable;
 
 class Calculation
 {
@@ -34,6 +35,8 @@ class Calculation
     const CALCULATION_REGEXP_OPENBRACE = '\(';
     //    Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
     const CALCULATION_REGEXP_FUNCTION = '@?(?:_xlfn\.)?(?:_xlws\.)?([\p{L}][\p{L}\p{N}\.]*)[\s]*\(';
+    //    Strip xlfn and xlws prefixes from function name
+    const CALCULATION_REGEXP_STRIP_XLFN_XLWS = '/(_xlfn[.])?(_xlws[.])?(?=[\p{L}][\p{L}\p{N}\.]*[\s]*[(])/';
     //    Cell reference (cell or range of cells, with or without a sheet reference)
     const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
     //    Cell reference (with or without a sheet reference) ensuring absolute/relative
@@ -62,46 +65,31 @@ class Calculation
     const FORMULA_CLOSE_MATRIX_BRACE = '}';
     const FORMULA_STRING_QUOTE = '"';
 
-    /** @var string */
-    private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
+    private static string $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
 
     /**
      * Instance of this class.
-     *
-     * @var ?Calculation
      */
-    private static $instance;
+    private static ?Calculation $instance = null;
 
     /**
      * Instance of the spreadsheet this Calculation Engine is using.
-     *
-     * @var ?Spreadsheet
      */
-    private $spreadsheet;
+    private ?Spreadsheet $spreadsheet;
 
     /**
      * Calculation cache.
-     *
-     * @var array
      */
-    private $calculationCache = [];
+    private array $calculationCache = [];
 
     /**
      * Calculation cache enabled.
-     *
-     * @var bool
      */
-    private $calculationCacheEnabled = true;
+    private bool $calculationCacheEnabled = true;
 
-    /**
-     * @var BranchPruner
-     */
-    private $branchPruner;
+    private BranchPruner $branchPruner;
 
-    /**
-     * @var bool
-     */
-    private $branchPruningEnabled = true;
+    private bool $branchPruningEnabled = true;
 
     /**
      * List of operators that can be used within formulae
@@ -127,74 +115,46 @@ class Calculation
 
     /**
      * The debug log generated by the calculation engine.
-     *
-     * @var Logger
      */
-    private $debugLog;
+    private Logger $debugLog;
 
-    /**
-     * Flag to determine how formula errors should be handled
-     *        If true, then a user error will be triggered
-     *        If false, then an exception will be thrown.
-     *
-     * @var ?bool
-     *
-     * @deprecated 1.25.2 use setSuppressFormulaErrors() instead
-     */
-    public $suppressFormulaErrors;
-
-    /** @var bool */
-    private $suppressFormulaErrorsNew = false;
+    private bool $suppressFormulaErrors = false;
 
     /**
      * Error message for any error that was raised/thrown by the calculation engine.
-     *
-     * @var null|string
      */
-    public $formulaError;
+    public ?string $formulaError = null;
 
     /**
      * Reference Helper.
-     *
-     * @var ReferenceHelper
      */
-    private static $referenceHelper;
+    private static ReferenceHelper $referenceHelper;
 
     /**
      * An array of the nested cell references accessed by the calculation engine, used for the debug log.
-     *
-     * @var CyclicReferenceStack
      */
-    private $cyclicReferenceStack;
+    private CyclicReferenceStack $cyclicReferenceStack;
 
-    /** @var array */
-    private $cellStack = [];
+    private array $cellStack = [];
 
     /**
      * Current iteration counter for cyclic formulae
      * If the value is 0 (or less) then cyclic formulae will throw an exception,
      * otherwise they will iterate to the limit defined here before returning a result.
-     *
-     * @var int
      */
-    private $cyclicFormulaCounter = 1;
+    private int $cyclicFormulaCounter = 1;
 
-    /** @var string */
-    private $cyclicFormulaCell = '';
+    private string $cyclicFormulaCell = '';
 
     /**
      * Number of iterations for cyclic formulae.
-     *
-     * @var int
      */
-    public $cyclicFormulaCount = 1;
+    public int $cyclicFormulaCount = 1;
 
     /**
      * The current locale setting.
-     *
-     * @var string
      */
-    private static $localeLanguage = 'en_us'; //    US English    (default locale)
+    private static string $localeLanguage = 'en_us'; //    US English    (default locale)
 
     /**
      * List of available locale settings
@@ -202,26 +162,23 @@ class Calculation
      *
      * @var string[]
      */
-    private static $validLocaleLanguages = [
+    private static array $validLocaleLanguages = [
         'en', //    English        (default language)
     ];
 
     /**
      * Locale-specific argument separator for function arguments.
-     *
-     * @var string
      */
-    private static $localeArgumentSeparator = ',';
+    private static string $localeArgumentSeparator = ',';
 
-    /** @var array */
-    private static $localeFunctions = [];
+    private static array $localeFunctions = [];
 
     /**
      * Locale-specific translations for Excel constants (True, False and Null).
      *
      * @var array<string, string>
      */
-    private static $localeBoolean = [
+    private static array $localeBoolean = [
         'TRUE' => 'TRUE',
         'FALSE' => 'FALSE',
         'NULL' => 'NULL',
@@ -236,9 +193,9 @@ class Calculation
      * Excel constant string translations to their PHP equivalents
      * Constant conversion from text name/value to actual (datatyped) value.
      *
-     * @var array<string, mixed>
+     * @var array<string, null|bool>
      */
-    private static $excelConstants = [
+    private static array $excelConstants = [
         'TRUE' => true,
         'FALSE' => false,
         'NULL' => null,
@@ -249,8 +206,7 @@ class Calculation
         return array_key_exists($key, self::$excelConstants);
     }
 
-    /** @return mixed */
-    public static function getExcelConstants(string $key)
+    public static function getExcelConstants(string $key): bool|null
     {
         return self::$excelConstants[$key];
     }
@@ -259,10 +215,8 @@ class Calculation
      * Array of functions usable on Spreadsheet.
      * In theory, this could be const rather than static;
      *   however, Phpstan breaks trying to analyze it when attempted.
-     *
-     *@var array
      */
-    private static $phpSpreadsheetFunctions = [
+    private static array $phpSpreadsheetFunctions = [
         'ABS' => [
             'category' => Category::CATEGORY_MATH_AND_TRIG,
             'functionCall' => [MathTrig\Absolute::class, 'evaluate'],
@@ -1042,7 +996,7 @@ class Calculation
         ],
         'ERROR.TYPE' => [
             'category' => Category::CATEGORY_INFORMATION,
-            'functionCall' => [Information\ExcelError::class, 'type'],
+            'functionCall' => [ExcelError::class, 'type'],
             'argumentCount' => '1',
         ],
         'EVEN' => [
@@ -1351,7 +1305,7 @@ class Calculation
         'IF' => [
             'category' => Category::CATEGORY_LOGICAL,
             'functionCall' => [Logical\Conditional::class, 'statementIf'],
-            'argumentCount' => '1-3',
+            'argumentCount' => '2-3',
         ],
         'IFERROR' => [
             'category' => Category::CATEGORY_LOGICAL,
@@ -1870,7 +1824,7 @@ class Calculation
         ],
         'NA' => [
             'category' => Category::CATEGORY_INFORMATION,
-            'functionCall' => [Information\ExcelError::class, 'NA'],
+            'functionCall' => [ExcelError::class, 'NA'],
             'argumentCount' => '0',
         ],
         'NEGBINOMDIST' => [
@@ -2902,17 +2856,15 @@ class Calculation
 
     /**
      *    Internal functions used for special control purposes.
-     *
-     * @var array
      */
-    private static $controlFunctions = [
+    private static array $controlFunctions = [
         'MKMATRIX' => [
             'argumentCount' => '*',
             'functionCall' => [Internal\MakeMatrix::class, 'make'],
         ],
         'NAME.ERROR' => [
             'argumentCount' => '*',
-            'functionCall' => [Functions::class, 'NAME'],
+            'functionCall' => [ExcelError::class, 'NAME'],
         ],
         'WILDCARDMATCH' => [
             'argumentCount' => '2',
@@ -2956,7 +2908,7 @@ class Calculation
             }
         }
 
-        if (!isset(self::$instance) || (self::$instance === null)) {
+        if (!self::$instance) {
             self::$instance = new self();
         }
 
@@ -2975,10 +2927,8 @@ class Calculation
 
     /**
      * Get the Logger for this calculation engine instance.
-     *
-     * @return Logger
      */
-    public function getDebugLog()
+    public function getDebugLog(): Logger
     {
         return $this->debugLog;
     }
@@ -3018,12 +2968,12 @@ class Calculation
      *
      * @return bool Success or failure
      */
-    public static function setArrayReturnType($returnType)
+    public static function setArrayReturnType(string $returnType): bool
     {
         if (
-            ($returnType == self::RETURN_ARRAY_AS_VALUE) ||
-            ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
-            ($returnType == self::RETURN_ARRAY_AS_ARRAY)
+            ($returnType == self::RETURN_ARRAY_AS_VALUE)
+            || ($returnType == self::RETURN_ARRAY_AS_ERROR)
+            || ($returnType == self::RETURN_ARRAY_AS_ARRAY)
         ) {
             self::$returnArrayAsType = $returnType;
 
@@ -3038,27 +2988,23 @@ class Calculation
      *
      * @return string $returnType Array return type
      */
-    public static function getArrayReturnType()
+    public static function getArrayReturnType(): string
     {
         return self::$returnArrayAsType;
     }
 
     /**
      * Is calculation caching enabled?
-     *
-     * @return bool
      */
-    public function getCalculationCacheEnabled()
+    public function getCalculationCacheEnabled(): bool
     {
         return $this->calculationCacheEnabled;
     }
 
     /**
      * Enable/disable calculation cache.
-     *
-     * @param bool $calculationCacheEnabled
      */
-    public function setCalculationCacheEnabled($calculationCacheEnabled): void
+    public function setCalculationCacheEnabled(bool $calculationCacheEnabled): void
     {
         $this->calculationCacheEnabled = $calculationCacheEnabled;
         $this->clearCalculationCache();
@@ -3090,10 +3036,8 @@ class Calculation
 
     /**
      * Clear calculation cache for a specified worksheet.
-     *
-     * @param string $worksheetName
      */
-    public function clearCalculationCacheForWorksheet($worksheetName): void
+    public function clearCalculationCacheForWorksheet(string $worksheetName): void
     {
         if (isset($this->calculationCache[$worksheetName])) {
             unset($this->calculationCache[$worksheetName]);
@@ -3102,11 +3046,8 @@ class Calculation
 
     /**
      * Rename calculation cache for a specified worksheet.
-     *
-     * @param string $fromWorksheetName
-     * @param string $toWorksheetName
      */
-    public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName): void
+    public function renameCalculationCacheForWorksheet(string $fromWorksheetName, string $toWorksheetName): void
     {
         if (isset($this->calculationCache[$fromWorksheetName])) {
             $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
@@ -3116,10 +3057,8 @@ class Calculation
 
     /**
      * Enable/disable calculation cache.
-     *
-     * @param mixed $enabled
      */
-    public function setBranchPruningEnabled($enabled): void
+    public function setBranchPruningEnabled(mixed $enabled): void
     {
         $this->branchPruningEnabled = $enabled;
         $this->branchPruner = new BranchPruner($this->branchPruningEnabled);
@@ -3137,18 +3076,16 @@ class Calculation
 
     /**
      * Get the currently defined locale code.
-     *
-     * @return string
      */
-    public function getLocale()
+    public function getLocale(): string
     {
         return self::$localeLanguage;
     }
 
     private function getLocaleFile(string $localeDir, string $locale, string $language, string $file): string
     {
-        $localeFileName = $localeDir . str_replace('_', DIRECTORY_SEPARATOR, $locale) .
-            DIRECTORY_SEPARATOR . $file;
+        $localeFileName = $localeDir . str_replace('_', DIRECTORY_SEPARATOR, $locale)
+            . DIRECTORY_SEPARATOR . $file;
         if (!file_exists($localeFileName)) {
             //    If there isn't a locale specific file, look for a language specific file
             $localeFileName = $localeDir . $language . DIRECTORY_SEPARATOR . $file;
@@ -3164,14 +3101,12 @@ class Calculation
      * Set the locale code.
      *
      * @param string $locale The locale to use for formula translation, eg: 'en_us'
-     *
-     * @return bool
      */
-    public function setLocale(string $locale)
+    public function setLocale(string $locale): bool
     {
         //    Identify our locale and language
         $language = $locale = strtolower($locale);
-        if (strpos($locale, '_') !== false) {
+        if (str_contains($locale, '_')) {
             [$language] = explode('_', $locale);
         }
         if (count(self::$validLocaleLanguages) == 1) {
@@ -3188,6 +3123,7 @@ class Calculation
             //    Default is US English, if user isn't requesting US english, then read the necessary data from the locale files
             if ($locale !== 'en_us') {
                 $localeDir = implode(DIRECTORY_SEPARATOR, [__DIR__, 'locale', null]);
+
                 //    Search for a file with a list of function names for locale
                 try {
                     $functionNamesFile = $this->getLocaleFile($localeDir, $locale, $language, 'functions');
@@ -3199,9 +3135,9 @@ class Calculation
                 $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
                 foreach ($localeFunctions as $localeFunction) {
                     [$localeFunction] = explode('##', $localeFunction); //    Strip out comments
-                    if (strpos($localeFunction, '=') !== false) {
+                    if (str_contains($localeFunction, '=')) {
                         [$fName, $lfName] = array_map('trim', explode('=', $localeFunction));
-                        if ((substr($fName, 0, 1) === '*' || isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
+                        if ((str_starts_with($fName, '*') || isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
                             self::$localeFunctions[$fName] = $lfName;
                         }
                     }
@@ -3216,14 +3152,14 @@ class Calculation
 
                 try {
                     $configFile = $this->getLocaleFile($localeDir, $locale, $language, 'config');
-                } catch (Exception $e) {
+                } catch (Exception) {
                     return false;
                 }
 
                 $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
                 foreach ($localeSettings as $localeSetting) {
                     [$localeSetting] = explode('##', $localeSetting); //    Strip out comments
-                    if (strpos($localeSetting, '=') !== false) {
+                    if (str_contains($localeSetting, '=')) {
                         [$settingName, $settingValue] = array_map('trim', explode('=', $localeSetting));
                         $settingName = strtoupper($settingName);
                         if ($settingValue !== '') {
@@ -3238,8 +3174,8 @@ class Calculation
                 }
             }
 
-            self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
-            self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
+            self::$functionReplaceFromExcel = self::$functionReplaceToExcel
+            = self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
             self::$localeLanguage = $locale;
 
             return true;
@@ -3310,7 +3246,7 @@ class Calculation
             $inFunctionBracesLevel = 0;
             $inMatrixBracesLevel = 0;
             //    If there is the possibility of separators within a quoted string, then we treat them as literals
-            if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) {
+            if (str_contains($formula, self::FORMULA_STRING_QUOTE)) {
                 //    So instead we skip replacing in any quoted strings by only replacing in every other array element
                 //       after we've exploded the formula
                 $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
@@ -3334,19 +3270,23 @@ class Calculation
         return $formula;
     }
 
-    /** @var ?array */
-    private static $functionReplaceFromExcel;
+    private static ?array $functionReplaceFromExcel;
 
-    /** @var ?array */
-    private static $functionReplaceToLocale;
+    private static ?array $functionReplaceToLocale;
 
     /**
-     * @param string $formula
+     * @deprecated 1.30.0 use translateFormulaToLocale() instead
      *
-     * @return string
+     * @codeCoverageIgnore
      */
-    public function _translateFormulaToLocale($formula)
+    public function _translateFormulaToLocale(string $formula): string
     {
+        return $this->translateFormulaToLocale($formula);
+    }
+
+    public function translateFormulaToLocale(string $formula): string
+    {
+        $formula = preg_replace(self::CALCULATION_REGEXP_STRIP_XLFN_XLWS, '', $formula) ?? '';
         // Build list of function names and constants for translation
         if (self::$functionReplaceFromExcel === null) {
             self::$functionReplaceFromExcel = [];
@@ -3377,18 +3317,21 @@ class Calculation
         );
     }
 
-    /** @var ?array */
-    private static $functionReplaceFromLocale;
+    private static ?array $functionReplaceFromLocale;
 
-    /** @var ?array */
-    private static $functionReplaceToExcel;
+    private static ?array $functionReplaceToExcel;
 
     /**
-     * @param string $formula
+     * @deprecated 1.30.0 use translateFormulaToEnglish() instead
      *
-     * @return string
+     * @codeCoverageIgnore
      */
-    public function _translateFormulaToEnglish($formula)
+    public function _translateFormulaToEnglish(string $formula): string
+    {
+        return $this->translateFormulaToEnglish($formula);
+    }
+
+    public function translateFormulaToEnglish(string $formula): string
     {
         if (self::$functionReplaceFromLocale === null) {
             self::$functionReplaceFromLocale = [];
@@ -3413,12 +3356,7 @@ class Calculation
         return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
     }
 
-    /**
-     * @param string $function
-     *
-     * @return string
-     */
-    public static function localeFunc($function)
+    public static function localeFunc(string $function): string
     {
         if (self::$localeLanguage !== 'en_us') {
             $functionName = trim($function, '(');
@@ -3436,12 +3374,8 @@ class Calculation
 
     /**
      * Wrap string values in quotes.
-     *
-     * @param mixed $value
-     *
-     * @return mixed
      */
-    public static function wrapResult($value)
+    public static function wrapResult(mixed $value): mixed
     {
         if (is_string($value)) {
             //    Error values cannot be "wrapped"
@@ -3454,7 +3388,7 @@ class Calculation
             return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
         } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
             //    Convert numeric errors to NaN error
-            return Information\ExcelError::NAN();
+            return ExcelError::NAN();
         }
 
         return $value;
@@ -3462,12 +3396,8 @@ class Calculation
 
     /**
      * Remove quotes used as a wrapper to identify string values.
-     *
-     * @param mixed $value
-     *
-     * @return mixed
      */
-    public static function unwrapResult($value)
+    public static function unwrapResult(mixed $value): mixed
     {
         if (is_string($value)) {
             if ((isset($value[0])) && ($value[0] == self::FORMULA_STRING_QUOTE) && (substr($value, -1) == self::FORMULA_STRING_QUOTE)) {
@@ -3475,7 +3405,7 @@ class Calculation
             }
             //    Convert numeric errors to NAN error
         } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
-            return Information\ExcelError::NAN();
+            return ExcelError::NAN();
         }
 
         return $value;
@@ -3485,11 +3415,9 @@ class Calculation
      * Calculate cell value (using formula from a cell ID)
      * Retained for backward compatibility.
      *
-     * @param Cell $cell Cell to calculate
-     *
-     * @return mixed
+     * @param ?Cell $cell Cell to calculate
      */
-    public function calculate(?Cell $cell = null)
+    public function calculate(?Cell $cell = null): mixed
     {
         try {
             return $this->calculateCellValue($cell);
@@ -3501,12 +3429,10 @@ class Calculation
     /**
      * Calculate the value of a cell formula.
      *
-     * @param Cell $cell Cell to calculate
+     * @param ?Cell $cell Cell to calculate
      * @param bool $resetLog Flag indicating whether the debug log should be reset or not
-     *
-     * @return mixed
      */
-    public function calculateCellValue(?Cell $cell = null, $resetLog = true)
+    public function calculateCellValue(?Cell $cell = null, bool $resetLog = true): mixed
     {
         if ($cell === null) {
             return null;
@@ -3558,14 +3484,14 @@ class Calculation
                 }
             }
 
-            throw new Exception($e->getMessage());
+            throw new Exception($e->getMessage(), $e->getCode(), $e);
         }
 
         if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
             self::$returnArrayAsType = $returnArrayAsType;
             $testResult = Functions::flattenArray($result);
             if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
-                return Information\ExcelError::VALUE();
+                return ExcelError::VALUE();
             }
             //    If there's only a single cell in the array, then we allow it
             if (count($testResult) != 1) {
@@ -3573,13 +3499,13 @@ class Calculation
                 $r = array_keys($result);
                 $r = array_shift($r);
                 if (!is_numeric($r)) {
-                    return Information\ExcelError::VALUE();
+                    return ExcelError::VALUE();
                 }
                 if (is_array($result[$r])) {
                     $c = array_keys($result[$r]);
                     $c = array_shift($c);
                     if (!is_numeric($c)) {
-                        return Information\ExcelError::VALUE();
+                        return ExcelError::VALUE();
                     }
                 }
             }
@@ -3590,7 +3516,7 @@ class Calculation
         if ($result === null && $cell->getWorksheet()->getSheetView()->getShowZeros()) {
             return 0;
         } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
-            return Information\ExcelError::NAN();
+            return ExcelError::NAN();
         }
 
         return $result;
@@ -3600,10 +3526,8 @@ class Calculation
      * Validate and parse a formula string.
      *
      * @param string $formula Formula to parse
-     *
-     * @return array|bool
      */
-    public function parseFormula($formula)
+    public function parseFormula(string $formula): array|bool
     {
         //    Basic validation that this is indeed a formula
         //    We return an empty array if not
@@ -3624,12 +3548,10 @@ class Calculation
      * Calculate the value of a formula.
      *
      * @param string $formula Formula to parse
-     * @param string $cellID Address of the cell to calculate
-     * @param Cell $cell Cell to calculate
-     *
-     * @return mixed
+     * @param ?string $cellID Address of the cell to calculate
+     * @param ?Cell $cell Cell to calculate
      */
-    public function calculateFormula($formula, $cellID = null, ?Cell $cell = null)
+    public function calculateFormula(string $formula, ?string $cellID = null, ?Cell $cell = null): mixed
     {
         //    Initialise the logging settings
         $this->formulaError = null;
@@ -3661,10 +3583,7 @@ class Calculation
         return $result;
     }
 
-    /**
-     * @param mixed $cellValue
-     */
-    public function getValueFromCache(string $cellReference, &$cellValue): bool
+    public function getValueFromCache(string $cellReference, mixed &$cellValue): bool
     {
         $this->debugLog->writeDebugLog('Testing cache value for cell %s', $cellReference);
         // Is calculation cacheing enabled?
@@ -3681,11 +3600,7 @@ class Calculation
         return false;
     }
 
-    /**
-     * @param string $cellReference
-     * @param mixed $cellValue
-     */
-    public function saveValueToCache($cellReference, $cellValue): void
+    public function saveValueToCache(string $cellReference, mixed $cellValue): void
     {
         if ($this->calculationCacheEnabled) {
             $this->calculationCache[$cellReference] = $cellValue;
@@ -3696,17 +3611,16 @@ class Calculation
      * Parse a cell formula and calculate its value.
      *
      * @param string $formula The formula to parse and calculate
-     * @param string $cellID The ID (e.g. A3) of the cell that we are calculating
-     * @param Cell $cell Cell to calculate
-     *
-     * @return mixed
+     * @param ?string $cellID The ID (e.g. A3) of the cell that we are calculating
+     * @param ?Cell $cell Cell to calculate
+     * @param bool $ignoreQuotePrefix If set to true, evaluate the formyla even if the referenced cell is quote prefixed
      */
-    public function _calculateFormulaValue($formula, $cellID = null, ?Cell $cell = null)
+    public function _calculateFormulaValue(string $formula, ?string $cellID = null, ?Cell $cell = null, bool $ignoreQuotePrefix = false): mixed
     {
         $cellValue = null;
 
         //  Quote-Prefixed cell values cannot be formulae, but are treated as strings
-        if ($cell !== null && $cell->getStyle()->getQuotePrefix() === true) {
+        if ($cell !== null && $ignoreQuotePrefix === false && $cell->getStyle()->getQuotePrefix() === true) {
             return self::wrapResult((string) $formula);
         }
 
@@ -3781,10 +3695,8 @@ class Calculation
      *                                            0 = no resize
      *                                            1 = shrink to fit
      *                                            2 = extend to fit
-     *
-     * @return array
      */
-    private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1)
+    private static function checkMatrixOperands(mixed &$operand1, mixed &$operand2, int $resize = 1): array
     {
         //    Examine each of the two operands, and turn them into an array if they aren't one already
         //    Note that this function should only be called if one or both of the operand is already an array
@@ -3811,6 +3723,8 @@ class Calculation
             //    Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
             self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
         }
+        [$matrix1Rows, $matrix1Columns] = self::getMatrixDimensions($operand1);
+        [$matrix2Rows, $matrix2Columns] = self::getMatrixDimensions($operand2);
 
         return [$matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns];
     }
@@ -3822,7 +3736,7 @@ class Calculation
      *
      * @return int[] An array comprising the number of rows, and number of columns
      */
-    public static function getMatrixDimensions(array &$matrix)
+    public static function getMatrixDimensions(array &$matrix): array
     {
         $matrixRows = count($matrix);
         $matrixColumns = 0;
@@ -3843,14 +3757,14 @@ class Calculation
     /**
      * Ensure that paired matrix operands are both matrices of the same size.
      *
-     * @param mixed $matrix1 First matrix operand
-     * @param mixed $matrix2 Second matrix operand
+     * @param array $matrix1 First matrix operand
+     * @param array $matrix2 Second matrix operand
      * @param int $matrix1Rows Row size of first matrix operand
      * @param int $matrix1Columns Column size of first matrix operand
      * @param int $matrix2Rows Row size of second matrix operand
      * @param int $matrix2Columns Column size of second matrix operand
      */
-    private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
+    private static function resizeMatricesShrink(array &$matrix1, array &$matrix2, int $matrix1Rows, int $matrix1Columns, int $matrix2Rows, int $matrix2Columns): void
     {
         if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
             if ($matrix2Rows < $matrix1Rows) {
@@ -3886,14 +3800,14 @@ class Calculation
     /**
      * Ensure that paired matrix operands are both matrices of the same size.
      *
-     * @param mixed $matrix1 First matrix operand
-     * @param mixed $matrix2 Second matrix operand
+     * @param array $matrix1 First matrix operand
+     * @param array $matrix2 Second matrix operand
      * @param int $matrix1Rows Row size of first matrix operand
      * @param int $matrix1Columns Column size of first matrix operand
      * @param int $matrix2Rows Row size of second matrix operand
      * @param int $matrix2Columns Column size of second matrix operand
      */
-    private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns): void
+    private static function resizeMatricesExtend(array &$matrix1, array &$matrix2, int $matrix1Rows, int $matrix1Columns, int $matrix2Rows, int $matrix2Columns): void
     {
         if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
             if ($matrix2Columns < $matrix1Columns) {
@@ -3934,10 +3848,8 @@ class Calculation
      * Format details of an operand for display in the log (based on operand type).
      *
      * @param mixed $value First matrix operand
-     *
-     * @return mixed
      */
-    private function showValue($value)
+    private function showValue(mixed $value): mixed
     {
         if ($this->debugLog->getWriteDebugLog()) {
             $testArray = Functions::flattenArray($value);
@@ -3974,10 +3886,8 @@ class Calculation
      * Format type and details of an operand for display in the log (based on operand type).
      *
      * @param mixed $value First matrix operand
-     *
-     * @return null|string
      */
-    private function showTypeDetails($value)
+    private function showTypeDetails(mixed $value): ?string
     {
         if ($this->debugLog->getWriteDebugLog()) {
             $testArray = Functions::flattenArray($value);
@@ -4011,19 +3921,17 @@ class Calculation
     }
 
     /**
-     * @param string $formula
-     *
      * @return false|string False indicates an error
      */
-    private function convertMatrixReferences($formula)
+    private function convertMatrixReferences(string $formula): false|string
     {
         static $matrixReplaceFrom = [self::FORMULA_OPEN_MATRIX_BRACE, ';', self::FORMULA_CLOSE_MATRIX_BRACE];
         static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))'];
 
         //    Convert any Excel matrix references to the MKMATRIX() function
-        if (strpos($formula, self::FORMULA_OPEN_MATRIX_BRACE) !== false) {
+        if (str_contains($formula, self::FORMULA_OPEN_MATRIX_BRACE)) {
             //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
-            if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) {
+            if (str_contains($formula, self::FORMULA_STRING_QUOTE)) {
                 //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
                 //        the formula
                 $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
@@ -4071,10 +3979,8 @@ class Calculation
      *    Binary Operators.
      *    These operators always work on two values.
      *    Array key is the operator, the value indicates whether this is a left or right associative operator.
-     *
-     * @var array
      */
-    private static $operatorAssociativity = [
+    private static array $operatorAssociativity = [
         '^' => 0, //    Exponentiation
         '*' => 0, '/' => 0, //    Multiplication and Division
         '+' => 0, '-' => 0, //    Addition and Subtraction
@@ -4086,19 +3992,15 @@ class Calculation
     /**
      *    Comparison (Boolean) Operators.
      *    These operators work on two values, but always return a boolean result.
-     *
-     * @var array
      */
-    private static $comparisonOperators = ['>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true];
+    private static array $comparisonOperators = ['>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true];
 
     /**
      *    Operator Precedence.
      *    This list includes all valid operators, whether binary (including boolean) or unary (such as %).
      *    Array key is the operator, the value is its precedence.
-     *
-     * @var array
      */
-    private static $operatorPrecedence = [
+    private static array $operatorPrecedence = [
         ':' => 9, //    Range
         '∩' => 8, //    Intersect
         '∪' => 7, //    Union
@@ -4114,11 +4016,9 @@ class Calculation
     // Convert infix to postfix notation
 
     /**
-     * @param string $formula
-     *
      * @return array<int, mixed>|false
      */
-    private function internalParseFormula($formula, ?Cell $cell = null)
+    private function internalParseFormula(string $formula, ?Cell $cell = null): bool|array
     {
         if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
             return false;
@@ -4128,17 +4028,17 @@ class Calculation
         //        so we store the parent worksheet so that we can re-attach it when necessary
         $pCellParent = ($cell !== null) ? $cell->getWorksheet() : null;
 
-        $regexpMatchString = '/^((?<string>' . self::CALCULATION_REGEXP_STRING .
-                                ')|(?<function>' . self::CALCULATION_REGEXP_FUNCTION .
-                                ')|(?<cellRef>' . self::CALCULATION_REGEXP_CELLREF .
-                                ')|(?<colRange>' . self::CALCULATION_REGEXP_COLUMN_RANGE .
-                                ')|(?<rowRange>' . self::CALCULATION_REGEXP_ROW_RANGE .
-                                ')|(?<number>' . self::CALCULATION_REGEXP_NUMBER .
-                                ')|(?<openBrace>' . self::CALCULATION_REGEXP_OPENBRACE .
-                                ')|(?<structuredReference>' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE .
-                                ')|(?<definedName>' . self::CALCULATION_REGEXP_DEFINEDNAME .
-                                ')|(?<error>' . self::CALCULATION_REGEXP_ERROR .
-                                '))/sui';
+        $regexpMatchString = '/^((?<string>' . self::CALCULATION_REGEXP_STRING
+                                . ')|(?<function>' . self::CALCULATION_REGEXP_FUNCTION
+                                . ')|(?<cellRef>' . self::CALCULATION_REGEXP_CELLREF
+                                . ')|(?<colRange>' . self::CALCULATION_REGEXP_COLUMN_RANGE
+                                . ')|(?<rowRange>' . self::CALCULATION_REGEXP_ROW_RANGE
+                                . ')|(?<number>' . self::CALCULATION_REGEXP_NUMBER
+                                . ')|(?<openBrace>' . self::CALCULATION_REGEXP_OPENBRACE
+                                . ')|(?<structuredReference>' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE
+                                . ')|(?<definedName>' . self::CALCULATION_REGEXP_DEFINEDNAME
+                                . ')|(?<error>' . self::CALCULATION_REGEXP_ERROR
+                                . '))/sui';
 
         //    Start with initialisation
         $index = 0;
@@ -4159,7 +4059,7 @@ class Calculation
             $opCharacter = $formula[$index]; //    Get the first character of the value at the current index position
 
             // Check for two-character operators (e.g. >=, <=, <>)
-            if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula[$index + 1]]))) {
+            if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && isset($formula[$index + 1], self::$comparisonOperators[$formula[$index + 1]])) {
                 $opCharacter .= $formula[++$index];
             }
             //    Find out if we're currently at the beginning of a number, variable, cell/row/column reference,
@@ -4182,10 +4082,10 @@ class Calculation
                 return $this->raiseFormulaError("Formula Error: Illegal character '~'"); //        on the stack but not in the input expression
             } elseif ((isset(self::CALCULATION_OPERATORS[$opCharacter]) || $isOperandOrFunction) && $expectingOperator) {    //    Are we putting an operator on the stack?
                 while (
-                    $stack->count() > 0 &&
-                    ($o2 = $stack->last()) &&
-                    isset(self::CALCULATION_OPERATORS[$o2['value']]) &&
-                    @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
+                    $stack->count() > 0
+                    && ($o2 = $stack->last())
+                    && isset(self::CALCULATION_OPERATORS[$o2['value']])
+                    && @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
                 ) {
                     $output[] = $stack->pop(); //    Swap operands and higher precedence operators from the stack to the output
                 }
@@ -4211,7 +4111,7 @@ class Calculation
                     try {
                         $this->branchPruner->closingBrace($d['value']);
                     } catch (Exception $e) {
-                        return $this->raiseFormulaError($e->getMessage());
+                        return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
                     }
 
                     $functionName = $matches[1]; //    Get the function name
@@ -4221,14 +4121,8 @@ class Calculation
                     $output[] = $stack->pop(); //    Pop the function and push onto the output
                     if (isset(self::$controlFunctions[$functionName])) {
                         $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
-                        // Scrutinizer says functionCall is unused after this assignment.
-                        // It might be right, but I'm too lazy to confirm.
-                        $functionCall = self::$controlFunctions[$functionName]['functionCall'];
-                        self::doNothing($functionCall);
                     } elseif (isset(self::$phpSpreadsheetFunctions[$functionName])) {
                         $expectedArgumentCount = self::$phpSpreadsheetFunctions[$functionName]['argumentCount'];
-                        $functionCall = self::$phpSpreadsheetFunctions[$functionName]['functionCall'];
-                        self::doNothing($functionCall);
                     } else {    // did we somehow push a non-function on the stack? this should never happen
                         return $this->raiseFormulaError('Formula Error: Internal error, non-function on stack');
                     }
@@ -4248,9 +4142,8 @@ class Calculation
                             }
                         }
                     } elseif ($expectedArgumentCount != '*') {
-                        $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
-                        self::doNothing($isOperandOrFunction);
-                        switch ($argMatch[2]) {
+                        preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
+                        switch ($argMatch[2] ?? '') {
                             case '+':
                                 if ($argumentCount < $argMatch[1]) {
                                     $argumentCountError = true;
@@ -4283,7 +4176,7 @@ class Calculation
                 try {
                     $this->branchPruner->argumentSeparator();
                 } catch (Exception $e) {
-                    return $this->raiseFormulaError($e->getMessage());
+                    return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
                 }
 
                 while (($o2 = $stack->pop()) && $o2['value'] !== '(') {        //    Pop off the stack back to the last (
@@ -4292,7 +4185,7 @@ class Calculation
                 //    If we've a comma when we're expecting an operand, then what we actually have is a null operand;
                 //        so push a null onto the stack
                 if (($expectingOperand) || (!$expectingOperator)) {
-                    $output[] = ['type' => 'Empty Argument', 'value' => self::$excelConstants['NULL'], 'reference' => 'NULL'];
+                    $output[] = $stack->getStackItem('Empty Argument', null, 'NULL');
                 }
                 // make sure there was a function
                 $d = $stack->last(2);
@@ -4365,8 +4258,12 @@ class Calculation
                                 $rangeStartCellRef = $output[count($output) - 2]['value'] ?? '';
                             }
                             preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/miu', $rangeStartCellRef, $rangeStartMatches);
-                            if ($rangeStartMatches[2] > '') {
-                                $val = $rangeStartMatches[2] . '!' . $val;
+                            if (array_key_exists(2, $rangeStartMatches)) {
+                                if ($rangeStartMatches[2] > '') {
+                                    $val = $rangeStartMatches[2] . '!' . $val;
+                                }
+                            } else {
+                                $val = ExcelError::REF();
                             }
                         } else {
                             $rangeStartCellRef = $output[count($output) - 1]['value'] ?? '';
@@ -4379,7 +4276,7 @@ class Calculation
                                 return $this->raiseFormulaError('3D Range references are not yet supported');
                             }
                         }
-                    } elseif (strpos($val, '!') === false && $pCellParent !== null) {
+                    } elseif (!str_contains($val, '!') && $pCellParent !== null) {
                         $worksheet = $pCellParent->getTitle();
                         $val = "'{$worksheet}'!{$val}";
                     }
@@ -4392,7 +4289,7 @@ class Calculation
                     try {
                         $structuredReference = Operands\StructuredReference::fromParser($formula, $index, $matches);
                     } catch (Exception $e) {
-                        return $this->raiseFormulaError($e->getMessage());
+                        return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
                     }
 
                     $val = $structuredReference->value();
@@ -4413,17 +4310,17 @@ class Calculation
                         $stackItemType = 'Cell Reference';
 
                         if (
-                            !is_numeric($val) &&
-                            ((ctype_alpha($val) === false || strlen($val) > 3)) &&
-                            (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $val) !== false) &&
-                            ($this->spreadsheet === null || $this->spreadsheet->getNamedRange($val) !== null)
+                            !is_numeric($val)
+                            && ((ctype_alpha($val) === false || strlen($val) > 3))
+                            && (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $val) !== false)
+                            && ($this->spreadsheet === null || $this->spreadsheet->getNamedRange($val) !== null)
                         ) {
                             $namedRange = ($this->spreadsheet === null) ? null : $this->spreadsheet->getNamedRange($val);
                             if ($namedRange !== null) {
                                 $stackItemType = 'Defined Name';
                                 $address = str_replace('$', '', $namedRange->getValue());
                                 $stackItemReference = $val;
-                                if (strpos($address, ':') !== false) {
+                                if (str_contains($address, ':')) {
                                     // We'll need to manipulate the stack for an actual named range rather than a named cell
                                     $fromTo = explode(':', $address);
                                     $to = array_pop($fromTo);
@@ -4435,7 +4332,10 @@ class Calculation
                                 }
                                 $val = $address;
                             }
+                        } elseif ($val === ExcelError::REF()) {
+                            $stackItemReference = $val;
                         } else {
+                            /** @var non-empty-string $startRowColRef */
                             $startRowColRef = $output[count($output) - 1]['value'] ?? '';
                             [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true);
                             $rangeSheetRef = $rangeWS1;
@@ -4452,7 +4352,7 @@ class Calculation
 
                             $refSheet = $pCellParent;
                             if ($pCellParent !== null && $rangeSheetRef !== '' && $rangeSheetRef !== $pCellParent->getTitle()) {
-                                $refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef);
+                                $refSheet = $pCellParent->getParentOrThrow()->getSheetByName($rangeSheetRef);
                             }
 
                             if (ctype_digit($val) && $val <= 1048576) {
@@ -4462,7 +4362,7 @@ class Calculation
                                 $valx = $val;
                                 $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($valx) : AddressRange::MAX_COLUMN; //    Max 16,384 columns for Excel2007
                                 $val = "{$rangeWS2}{$endRowColRef}{$val}";
-                            } elseif (ctype_alpha($val) && strlen($val) <= 3) {
+                            } elseif (ctype_alpha($val) && strlen($val ?? '') <= 3) {
                                 //    Column range
                                 $stackItemType = 'Column Reference';
                                 $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow($val) : AddressRange::MAX_ROW; //    Max 1,048,576 rows for Excel2007
@@ -4514,7 +4414,7 @@ class Calculation
                         $stackItemType = 'Defined Name';
                         $stackItemReference = $val;
                     } elseif (is_numeric($val)) {
-                        if ((strpos((string) $val, '.') !== false) || (stripos((string) $val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
+                        if ((str_contains((string) $val, '.')) || (stripos((string) $val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
                             $val = (float) $val;
                         } else {
                             $val = (int) $val;
@@ -4532,7 +4432,7 @@ class Calculation
                 ++$index;
             } elseif ($opCharacter === ')') { // miscellaneous error checking
                 if ($expectingOperand) {
-                    $output[] = ['type' => 'Empty Argument', 'value' => self::$excelConstants['NULL'], 'reference' => 'NULL'];
+                    $output[] = $stack->getStackItem('Empty Argument', null, 'NULL');
                     $expectingOperand = false;
                     $expectingOperator = true;
                 } else {
@@ -4567,24 +4467,24 @@ class Calculation
                 //        Cell References, Defined Names or Structured References) then we have an INTERSECTION operator
                 $countOutputMinus1 = count($output) - 1;
                 if (
-                    ($expectingOperator) &&
-                    array_key_exists($countOutputMinus1, $output) &&
-                    is_array($output[$countOutputMinus1]) &&
-                    array_key_exists('type', $output[$countOutputMinus1]) &&
-                    (
-                        (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/miu', substr($formula, $index), $match)) &&
-                            ($output[$countOutputMinus1]['type'] === 'Cell Reference') ||
-                        (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', substr($formula, $index), $match)) &&
-                            ($output[$countOutputMinus1]['type'] === 'Defined Name' || $output[$countOutputMinus1]['type'] === 'Value') ||
-                        (preg_match('/^' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE . '.*/miu', substr($formula, $index), $match)) &&
-                            ($output[$countOutputMinus1]['type'] === Operands\StructuredReference::NAME || $output[$countOutputMinus1]['type'] === 'Value')
+                    ($expectingOperator)
+                    && array_key_exists($countOutputMinus1, $output)
+                    && is_array($output[$countOutputMinus1])
+                    && array_key_exists('type', $output[$countOutputMinus1])
+                    && (
+                        (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '.*/miu', substr($formula, $index), $match))
+                            && ($output[$countOutputMinus1]['type'] === 'Cell Reference')
+                        || (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', substr($formula, $index), $match))
+                            && ($output[$countOutputMinus1]['type'] === 'Defined Name' || $output[$countOutputMinus1]['type'] === 'Value')
+                        || (preg_match('/^' . self::CALCULATION_REGEXP_STRUCTURED_REFERENCE . '.*/miu', substr($formula, $index), $match))
+                            && ($output[$countOutputMinus1]['type'] === Operands\StructuredReference::NAME || $output[$countOutputMinus1]['type'] === 'Value')
                     )
                 ) {
                     while (
-                        $stack->count() > 0 &&
-                        ($o2 = $stack->last()) &&
-                        isset(self::CALCULATION_OPERATORS[$o2['value']]) &&
-                        @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
+                        $stack->count() > 0
+                        && ($o2 = $stack->last())
+                        && isset(self::CALCULATION_OPERATORS[$o2['value']])
+                        && @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])
                     ) {
                         $output[] = $stack->pop(); //    Swap operands and higher precedence operators from the stack to the output
                     }
@@ -4605,12 +4505,7 @@ class Calculation
         return $output;
     }
 
-    /**
-     * @param array $operandData
-     *
-     * @return mixed
-     */
-    private static function dataTestReference(&$operandData)
+    private static function dataTestReference(array &$operandData): mixed
     {
         $operand = $operandData['value'];
         if (($operandData['reference'] === null) && (is_array($operand))) {
@@ -4632,21 +4527,10 @@ class Calculation
         return $operand;
     }
 
-    private const NUMERIC_BINARY_OPERATIONS = [
-        '+' => 'plusEquals',
-        '-' => 'minusEquals',
-        '*' => 'arrayTimesEquals',
-        '/' => 'arrayRightDivide',
-        '^' => 'power',
-    ];
-
     /**
-     * @param mixed $tokens
-     * @param null|string $cellID
-     *
      * @return array<int, mixed>|false
      */
-    private function processTokenStack($tokens, $cellID = null, ?Cell $cell = null)
+    private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell $cell = null)
     {
         if ($tokens === false) {
             return false;
@@ -4670,8 +4554,8 @@ class Calculation
             if ($this->branchPruningEnabled && isset($tokenData['onlyIf'])) {
                 $onlyIfStoreKey = $tokenData['onlyIf'];
                 $storeValue = $branchStore[$onlyIfStoreKey] ?? null;
-                $storeValueAsBool = ($storeValue === null) ?
-                    true : (bool) Functions::flattenSingleValue($storeValue);
+                $storeValueAsBool = ($storeValue === null)
+                    ? true : (bool) Functions::flattenSingleValue($storeValue);
                 if (is_array($storeValue)) {
                     $wrappedItem = end($storeValue);
                     $storeValue = is_array($wrappedItem) ? end($wrappedItem) : $wrappedItem;
@@ -4679,7 +4563,7 @@ class Calculation
 
                 if (
                     (isset($storeValue) || $tokenData['reference'] === 'NULL')
-                    && (!$storeValueAsBool || ErrorValue::isError($storeValue) || ($storeValue === 'Pruned branch'))
+                    && (!$storeValueAsBool || Information\ErrorValue::isError($storeValue) || ($storeValue === 'Pruned branch'))
                 ) {
                     // If branching value is not true, we don't need to compute
                     if (!isset($fakedForBranchPruning['onlyIf-' . $onlyIfStoreKey])) {
@@ -4702,8 +4586,8 @@ class Calculation
             if ($this->branchPruningEnabled && isset($tokenData['onlyIfNot'])) {
                 $onlyIfNotStoreKey = $tokenData['onlyIfNot'];
                 $storeValue = $branchStore[$onlyIfNotStoreKey] ?? null;
-                $storeValueAsBool = ($storeValue === null) ?
-                    true : (bool) Functions::flattenSingleValue($storeValue);
+                $storeValueAsBool = ($storeValue === null)
+                    ? true : (bool) Functions::flattenSingleValue($storeValue);
                 if (is_array($storeValue)) {
                     $wrappedItem = end($storeValue);
                     $storeValue = is_array($wrappedItem) ? end($wrappedItem) : $wrappedItem;
@@ -4711,7 +4595,7 @@ class Calculation
 
                 if (
                     (isset($storeValue) || $tokenData['reference'] === 'NULL')
-                    && ($storeValueAsBool || ErrorValue::isError($storeValue) || ($storeValue === 'Pruned branch'))
+                    && ($storeValueAsBool || Information\ErrorValue::isError($storeValue) || ($storeValue === 'Pruned branch'))
                 ) {
                     // If branching value is true, we don't need to compute
                     if (!isset($fakedForBranchPruning['onlyIfNot-' . $onlyIfNotStoreKey])) {
@@ -4732,18 +4616,40 @@ class Calculation
             }
 
             if ($token instanceof Operands\StructuredReference) {
-                throw new Exception('Structured References are not currently supported');
-                // The next step is converting any structured reference to a cell value of range
-                //     to a new $token value, which can then be processed in the following code.
-            }
+                if ($cell === null) {
+                    return $this->raiseFormulaError('Structured References must exist in a Cell context');
+                }
 
-            // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
-            if (!is_numeric($token) && !is_object($token) && isset(self::BINARY_OPERATORS[$token])) {
+                try {
+                    $cellRange = $token->parse($cell);
+                    if (str_contains($cellRange, ':')) {
+                        $this->debugLog->writeDebugLog('Evaluating Structured Reference %s as Cell Range %s', $token->value(), $cellRange);
+                        $rangeValue = self::getInstance($cell->getWorksheet()->getParent())->_calculateFormulaValue("={$cellRange}", $cellRange, $cell);
+                        $stack->push('Value', $rangeValue);
+                        $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as value %s', $token->value(), $this->showValue($rangeValue));
+                    } else {
+                        $this->debugLog->writeDebugLog('Evaluating Structured Reference %s as Cell %s', $token->value(), $cellRange);
+                        $cellValue = $cell->getWorksheet()->getCell($cellRange)->getCalculatedValue(false);
+                        $stack->push('Cell Reference', $cellValue, $cellRange);
+                        $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as value %s', $token->value(), $this->showValue($cellValue));
+                    }
+                } catch (Exception $e) {
+                    if ($e->getCode() === Exception::CALCULATION_ENGINE_PUSH_TO_STACK) {
+                        $stack->push('Error', ExcelError::REF(), null);
+                        $this->debugLog->writeDebugLog('Evaluated Structured Reference %s as error value %s', $token->value(), ExcelError::REF());
+                    } else {
+                        return $this->raiseFormulaError($e->getMessage(), $e->getCode(), $e);
+                    }
+                }
+            } elseif (!is_numeric($token) && !is_object($token) && isset(self::BINARY_OPERATORS[$token])) {
+                // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
                 //    We must have two operands, error if we don't
-                if (($operand2Data = $stack->pop()) === null) {
+                $operand2Data = $stack->pop();
+                if ($operand2Data === null) {
                     return $this->raiseFormulaError('Internal error - Operand value missing from stack');
                 }
-                if (($operand1Data = $stack->pop()) === null) {
+                $operand1Data = $stack->pop();
+                if ($operand1Data === null) {
                     return $this->raiseFormulaError('Internal error - Operand value missing from stack');
                 }
 
@@ -4782,11 +4688,12 @@ class Calculation
                                 }
                             }
                         }
-                        if (strpos($operand1Data['reference'], '!') !== false) {
+                        if (str_contains($operand1Data['reference'] ?? '', '!')) {
                             [$sheet1, $operand1Data['reference']] = Worksheet::extractSheetTitle($operand1Data['reference'], true);
                         } else {
                             $sheet1 = ($pCellWorksheet !== null) ? $pCellWorksheet->getTitle() : '';
                         }
+                        $sheet1 ??= '';
 
                         [$sheet2, $operand2Data['reference']] = Worksheet::extractSheetTitle($operand2Data['reference'], true);
                         if (empty($sheet2)) {
@@ -4817,12 +4724,23 @@ class Calculation
                                 }
                             }
 
-                            $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
+                            $oData = array_merge(explode(':', $operand1Data['reference'] ?? ''), explode(':', $operand2Data['reference'] ?? ''));
                             $oCol = $oRow = [];
+                            $breakNeeded = false;
                             foreach ($oData as $oDatum) {
-                                $oCR = Coordinate::coordinateFromString($oDatum);
-                                $oCol[] = Coordinate::columnIndexFromString($oCR[0]) - 1;
-                                $oRow[] = $oCR[1];
+                                try {
+                                    $oCR = Coordinate::coordinateFromString($oDatum);
+                                    $oCol[] = Coordinate::columnIndexFromString($oCR[0]) - 1;
+                                    $oRow[] = $oCR[1];
+                                } catch (\Exception) {
+                                    $stack->push('Error', ExcelError::REF(), null);
+                                    $breakNeeded = true;
+
+                                    break;
+                                }
+                            }
+                            if ($breakNeeded) {
+                                break;
                             }
                             $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
                             if ($pCellParent !== null && $this->spreadsheet !== null) {
@@ -4831,9 +4749,11 @@ class Calculation
                                 return $this->raiseFormulaError('Unable to access Cell Reference');
                             }
 
+                            $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellValue));
                             $stack->push('Cell Reference', $cellValue, $cellRef);
                         } else {
-                            $stack->push('Error', Information\ExcelError::REF(), null);
+                            $this->debugLog->writeDebugLog('Evaluation Result is a #REF! Error');
+                            $stack->push('Error', ExcelError::REF(), null);
                         }
 
                         break;
@@ -4842,7 +4762,7 @@ class Calculation
                     case '*':            //    Multiplication
                     case '/':            //    Division
                     case '^':            //    Exponential
-                        $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, self::NUMERIC_BINARY_OPERATIONS[$token], $stack);
+                        $result = $this->executeNumericBinaryOperation($operand1, $operand2, $token, $stack);
                         if (isset($storeKey)) {
                             $branchStore[$storeKey] = $result;
                         }
@@ -4852,36 +4772,50 @@ class Calculation
                         //    If either of the operands is a matrix, we need to treat them both as matrices
                         //        (converting the other operand to a matrix if need be); then perform the required
                         //        matrix operation
-                        if (is_bool($operand1)) {
-                            $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
-                        }
-                        if (is_bool($operand2)) {
-                            $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
-                        }
-                        if ((is_array($operand1)) || (is_array($operand2))) {
-                            //    Ensure that both operands are arrays/matrices
-                            self::checkMatrixOperands($operand1, $operand2, 2);
-
-                            try {
-                                //    Convert operand 1 from a PHP array to a matrix
-                                $matrix = new Shared\JAMA\Matrix($operand1);
-                                //    Perform the required operation against the operand 1 matrix, passing in operand 2
-                                $matrixResult = $matrix->concat($operand2);
-                                $result = $matrixResult->getArray();
-                                if (isset($result[0][0])) {
-                                    $result[0][0] = Shared\StringHelper::substring($result[0][0], 0, DataType::MAX_STRING_LENGTH);
-                                }
-                            } catch (\Exception $ex) {
-                                $this->debugLog->writeDebugLog('JAMA Matrix Exception: %s', $ex->getMessage());
-                                $result = '#VALUE!';
+                        $operand1 = self::boolToString($operand1);
+                        $operand2 = self::boolToString($operand2);
+                        if (is_array($operand1) || is_array($operand2)) {
+                            if (is_string($operand1)) {
+                                $operand1 = self::unwrapResult($operand1);
                             }
+                            if (is_string($operand2)) {
+                                $operand2 = self::unwrapResult($operand2);
+                            }
+                            //    Ensure that both operands are arrays/matrices
+                            [$rows, $columns] = self::checkMatrixOperands($operand1, $operand2, 2);
+
+                            for ($row = 0; $row < $rows; ++$row) {
+                                for ($column = 0; $column < $columns; ++$column) {
+                                    $op1x = self::boolToString($operand1[$row][$column]);
+                                    $op2x = self::boolToString($operand2[$row][$column]);
+                                    if (Information\ErrorValue::isError($op1x)) {
+                                        // no need to do anything
+                                    } elseif (Information\ErrorValue::isError($op2x)) {
+                                        $operand1[$row][$column] = $op2x;
+                                    } else {
+                                        $operand1[$row][$column]
+                                            = Shared\StringHelper::substring(
+                                                $op1x . $op2x,
+                                                0,
+                                                DataType::MAX_STRING_LENGTH
+                                            );
+                                    }
+                                }
+                            }
+                            $result = $operand1;
                         } else {
                             // In theory, we should truncate here.
                             // But I can't figure out a formula
                             // using the concatenation operator
                             // with literals that fits in 32K,
                             // so I don't think we can overflow here.
-                            $result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE;
+                            if (Information\ErrorValue::isError($operand1)) {
+                                $result = $operand1;
+                            } elseif (Information\ErrorValue::isError($operand2)) {
+                                $result = $operand2;
+                            } else {
+                                $result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE;
+                            }
                         }
                         $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
                         $stack->push('Value', $result);
@@ -4903,10 +4837,10 @@ class Calculation
                         }
                         if (count(Functions::flattenArray($cellIntersect)) === 0) {
                             $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellIntersect));
-                            $stack->push('Error', Information\ExcelError::null(), null);
+                            $stack->push('Error', ExcelError::null(), null);
                         } else {
-                            $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' .
-                                Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
+                            $cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':'
+                                . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
                             $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($cellIntersect));
                             $stack->push('Value', $cellIntersect, $cellRef);
                         }
@@ -4927,23 +4861,26 @@ class Calculation
                     $multiplier = 0.01;
                 }
                 if (is_array($arg)) {
-                    self::checkMatrixOperands($arg, $multiplier, 2);
-
-                    try {
-                        $matrix1 = new Shared\JAMA\Matrix($arg);
-                        $matrixResult = $matrix1->arrayTimesEquals($multiplier);
-                        $result = $matrixResult->getArray();
-                    } catch (\Exception $ex) {
-                        $this->debugLog->writeDebugLog('JAMA Matrix Exception: %s', $ex->getMessage());
-                        $result = '#VALUE!';
+                    $operand2 = $multiplier;
+                    $result = $arg;
+                    [$rows, $columns] = self::checkMatrixOperands($result, $operand2, 0);
+                    for ($row = 0; $row < $rows; ++$row) {
+                        for ($column = 0; $column < $columns; ++$column) {
+                            if (self::isNumericOrBool($result[$row][$column])) {
+                                $result[$row][$column] *= $multiplier;
+                            } else {
+                                $result[$row][$column] = self::makeError($result[$row][$column]);
+                            }
+                        }
                     }
+
                     $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
                     $stack->push('Value', $result);
                     if (isset($storeKey)) {
                         $branchStore[$storeKey] = $result;
                     }
                 } else {
-                    $this->executeNumericBinaryOperation($multiplier, $arg, '*', 'arrayTimesEquals', $stack);
+                    $this->executeNumericBinaryOperation($multiplier, $arg, '*', $stack);
                 }
             } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token ?? '', $matches)) {
                 $cellRef = null;
@@ -4951,12 +4888,12 @@ class Calculation
                 if (isset($matches[8])) {
                     if ($cell === null) {
                         // We can't access the range, so return a REF error
-                        $cellValue = Information\ExcelError::REF();
+                        $cellValue = ExcelError::REF();
                     } else {
                         $cellRef = $matches[6] . $matches[7] . ':' . $matches[9] . $matches[10];
                         if ($matches[2] > '') {
                             $matches[2] = trim($matches[2], "\"'");
-                            if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
+                            if ((str_contains($matches[2], '[')) || (str_contains($matches[2], ']'))) {
                                 //    It's a Reference to an external spreadsheet (not currently supported)
                                 return $this->raiseFormulaError('Unable to access External Workbook');
                             }
@@ -4981,12 +4918,12 @@ class Calculation
                 } else {
                     if ($cell === null) {
                         // We can't access the cell, so return a REF error
-                        $cellValue = Information\ExcelError::REF();
+                        $cellValue = ExcelError::REF();
                     } else {
                         $cellRef = $matches[6] . $matches[7];
                         if ($matches[2] > '') {
                             $matches[2] = trim($matches[2], "\"'");
-                            if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
+                            if ((str_contains($matches[2], '[')) || (str_contains($matches[2], ']'))) {
                                 //    It's a Reference to an external spreadsheet (not currently supported)
                                 return $this->raiseFormulaError('Unable to access External Workbook');
                             }
@@ -4998,7 +4935,7 @@ class Calculation
                                     $cell->attach($pCellParent);
                                 } else {
                                     $cellRef = ($cellSheet !== null) ? "'{$matches[2]}'!{$cellRef}" : $cellRef;
-                                    $cellValue = ($cellSheet !== null) ? null : Information\ExcelError::REF();
+                                    $cellValue = ($cellSheet !== null) ? null : ExcelError::REF();
                                 }
                             } else {
                                 return $this->raiseFormulaError('Unable to access Cell Reference');
@@ -5054,9 +4991,9 @@ class Calculation
                         $arg = $stack->pop();
                         $a = $argCount - $i - 1;
                         if (
-                            ($passByReference) &&
-                            (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])) &&
-                            (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])
+                            ($passByReference)
+                            && (isset(self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a]))
+                            && (self::$phpSpreadsheetFunctions[$functionName]['passByReference'][$a])
                         ) {
                             if ($arg['reference'] === null) {
                                 $args[] = $cellID;
@@ -5070,8 +5007,14 @@ class Calculation
                                 }
                             }
                         } else {
-                            $emptyArguments[] = ($arg['type'] === 'Empty Argument');
-                            $args[] = self::unwrapResult($arg['value']);
+                            if ($arg['type'] === 'Empty Argument' && in_array($functionName, ['MIN', 'MINA', 'MAX', 'MAXA', 'IF'], true)) {
+                                $emptyArguments[] = false;
+                                $args[] = $arg['value'] = 0;
+                                $this->debugLog->writeDebugLog('Empty Argument reevaluated as 0');
+                            } else {
+                                $emptyArguments[] = $arg['type'] === 'Empty Argument';
+                                $args[] = self::unwrapResult($arg['value']);
+                            }
                             if ($functionName !== 'MKMATRIX') {
                                 $argArrayVals[] = $this->showValue($arg['value']);
                             }
@@ -5082,7 +5025,7 @@ class Calculation
                     krsort($args);
                     krsort($emptyArguments);
 
-                    if ($argCount > 0) {
+                    if ($argCount > 0 && is_array($functionCall)) {
                         $args = $this->addDefaultArgumentValues($functionCall, $args, $emptyArguments);
                     }
 
@@ -5138,14 +5081,35 @@ class Calculation
                     if ($cell === null || $pCellWorksheet === null) {
                         return $this->raiseFormulaError("undefined name '$token'");
                     }
+                    $specifiedWorksheet = trim($matches[2], "'");
 
                     $this->debugLog->writeDebugLog('Evaluating Defined Name %s', $definedName);
-                    $namedRange = DefinedName::resolveName($definedName, $pCellWorksheet);
+                    $namedRange = DefinedName::resolveName($definedName, $pCellWorksheet, $specifiedWorksheet);
+                    // If not Defined Name, try as Table.
+                    if ($namedRange === null && $this->spreadsheet !== null) {
+                        $table = $this->spreadsheet->getTableByName($definedName);
+                        if ($table !== null) {
+                            $tableRange = Coordinate::getRangeBoundaries($table->getRange());
+                            if ($table->getShowHeaderRow()) {
+                                ++$tableRange[0][1];
+                            }
+                            if ($table->getShowTotalsRow()) {
+                                --$tableRange[1][1];
+                            }
+                            $tableRangeString
+                                = '$' . $tableRange[0][0]
+                                . '$' . $tableRange[0][1]
+                                . ':'
+                                . '$' . $tableRange[1][0]
+                                . '$' . $tableRange[1][1];
+                            $namedRange = new NamedRange($definedName, $table->getWorksheet(), $tableRangeString);
+                        }
+                    }
                     if ($namedRange === null) {
                         return $this->raiseFormulaError("undefined name '$definedName'");
                     }
 
-                    $result = $this->evaluateDefinedName($cell, $namedRange, $pCellWorksheet, $stack);
+                    $result = $this->evaluateDefinedName($cell, $namedRange, $pCellWorksheet, $stack, $specifiedWorksheet !== '');
                     if (isset($storeKey)) {
                         $branchStore[$storeKey] = $result;
                     }
@@ -5164,13 +5128,7 @@ class Calculation
         return $output;
     }
 
-    /**
-     * @param mixed $operand
-     * @param mixed $stack
-     *
-     * @return bool
-     */
-    private function validateBinaryOperand(&$operand, &$stack)
+    private function validateBinaryOperand(mixed &$operand, mixed &$stack): bool
     {
         if (is_array($operand)) {
             if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
@@ -5208,14 +5166,7 @@ class Calculation
         return true;
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     * @param string $operation
-     *
-     * @return array
-     */
-    private function executeArrayComparison($operand1, $operand2, $operation, Stack &$stack, bool $recursingArrays)
+    private function executeArrayComparison(mixed $operand1, mixed $operand2, string $operation, Stack &$stack, bool $recursingArrays): array
     {
         $result = [];
         if (!is_array($operand2)) {
@@ -5254,15 +5205,7 @@ class Calculation
         return $result;
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     * @param string $operation
-     * @param bool $recursingArrays
-     *
-     * @return mixed
-     */
-    private function executeBinaryComparisonOperation($operand1, $operand2, $operation, Stack &$stack, $recursingArrays = false)
+    private function executeBinaryComparisonOperation(mixed $operand1, mixed $operand2, string $operation, Stack &$stack, bool $recursingArrays = false): array|bool
     {
         //    If we're dealing with matrix operations, we want a matrix result
         if ((is_array($operand1)) || (is_array($operand2))) {
@@ -5279,89 +5222,123 @@ class Calculation
         return $result;
     }
 
-    /**
-     * @param mixed $operand1
-     * @param mixed $operand2
-     * @param string $operation
-     * @param string $matrixFunction
-     * @param Stack $stack
-     *
-     * @return bool|mixed
-     */
-    private function executeNumericBinaryOperation($operand1, $operand2, $operation, $matrixFunction, &$stack)
+    private function executeNumericBinaryOperation(mixed $operand1, mixed $operand2, string $operation, Stack &$stack): mixed
     {
         //    Validate the two operands
         if (
-            ($this->validateBinaryOperand($operand1, $stack) === false) ||
-            ($this->validateBinaryOperand($operand2, $stack) === false)
+            ($this->validateBinaryOperand($operand1, $stack) === false)
+            || ($this->validateBinaryOperand($operand2, $stack) === false)
         ) {
             return false;
         }
 
-        //    If either of the operands is a matrix, we need to treat them both as matrices
-        //        (converting the other operand to a matrix if need be); then perform the required
-        //        matrix operation
-        if ((is_array($operand1)) || (is_array($operand2))) {
-            //    Ensure that both operands are arrays/matrices of the same size
-            self::checkMatrixOperands($operand1, $operand2, 2);
-
-            try {
-                //    Convert operand 1 from a PHP array to a matrix
-                $matrix = new Shared\JAMA\Matrix($operand1);
-                //    Perform the required operation against the operand 1 matrix, passing in operand 2
-                $matrixResult = $matrix->$matrixFunction($operand2);
-                $result = $matrixResult->getArray();
-            } catch (\Exception $ex) {
-                $this->debugLog->writeDebugLog('JAMA Matrix Exception: %s', $ex->getMessage());
-                $result = '#VALUE!';
-            }
-        } else {
-            if (
-                (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) &&
-                ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1) > 0) ||
-                    (is_string($operand2) && !is_numeric($operand2) && strlen($operand2) > 0))
-            ) {
-                $result = Information\ExcelError::VALUE();
-            } else {
-                //    If we're dealing with non-matrix operations, execute the necessary operation
-                switch ($operation) {
-                    //    Addition
-                    case '+':
-                        $result = $operand1 + $operand2;
-
-                        break;
-                    //    Subtraction
-                    case '-':
-                        $result = $operand1 - $operand2;
-
-                        break;
-                    //    Multiplication
-                    case '*':
-                        $result = $operand1 * $operand2;
-
-                        break;
-                    //    Division
-                    case '/':
-                        if ($operand2 == 0) {
-                            //    Trap for Divide by Zero error
-                            $stack->push('Error', ExcelError::DIV0());
-                            $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails(ExcelError::DIV0()));
-
-                            return false;
-                        }
-                        $result = $operand1 / $operand2;
-
-                        break;
-                    //    Power
-                    case '^':
-                        $result = $operand1 ** $operand2;
-
-                        break;
-
-                    default:
-                        throw new Exception('Unsupported numeric binary operation');
+        if (
+            (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE)
+            && ((is_string($operand1) && !is_numeric($operand1) && $operand1 !== '')
+                || (is_string($operand2) && !is_numeric($operand2) && $operand2 !== ''))
+        ) {
+            $result = ExcelError::VALUE();
+        } elseif (is_array($operand1) || is_array($operand2)) {
+            //    Ensure that both operands are arrays/matrices
+            if (is_array($operand1)) {
+                foreach ($operand1 as $key => $value) {
+                    $operand1[$key] = Functions::flattenArray($value);
                 }
             }
+            if (is_array($operand2)) {
+                foreach ($operand2 as $key => $value) {
+                    $operand2[$key] = Functions::flattenArray($value);
+                }
+            }
+            [$rows, $columns] = self::checkMatrixOperands($operand1, $operand2, 2);
+
+            for ($row = 0; $row < $rows; ++$row) {
+                for ($column = 0; $column < $columns; ++$column) {
+                    if ($operand1[$row][$column] === null) {
+                        $operand1[$row][$column] = 0;
+                    } elseif (!self::isNumericOrBool($operand1[$row][$column])) {
+                        $operand1[$row][$column] = self::makeError($operand1[$row][$column]);
+
+                        continue;
+                    }
+                    if ($operand2[$row][$column] === null) {
+                        $operand2[$row][$column] = 0;
+                    } elseif (!self::isNumericOrBool($operand2[$row][$column])) {
+                        $operand1[$row][$column] = self::makeError($operand2[$row][$column]);
+
+                        continue;
+                    }
+                    switch ($operation) {
+                        case '+':
+                            $operand1[$row][$column] += $operand2[$row][$column];
+
+                            break;
+                        case '-':
+                            $operand1[$row][$column] -= $operand2[$row][$column];
+
+                            break;
+                        case '*':
+                            $operand1[$row][$column] *= $operand2[$row][$column];
+
+                            break;
+                        case '/':
+                            if ($operand2[$row][$column] == 0) {
+                                $operand1[$row][$column] = ExcelError::DIV0();
+                            } else {
+                                $operand1[$row][$column] /= $operand2[$row][$column];
+                            }
+
+                            break;
+                        case '^':
+                            $operand1[$row][$column] = $operand1[$row][$column] ** $operand2[$row][$column];
+
+                            break;
+
+                        default:
+                            throw new Exception('Unsupported numeric binary operation');
+                    }
+                }
+            }
+            $result = $operand1;
+        } else {
+            //    If we're dealing with non-matrix operations, execute the necessary operation
+            switch ($operation) {
+                //    Addition
+                case '+':
+                    $result = $operand1 + $operand2;
+
+                    break;
+                //    Subtraction
+                case '-':
+                    $result = $operand1 - $operand2;
+
+                    break;
+                //    Multiplication
+                case '*':
+                    $result = $operand1 * $operand2;
+
+                    break;
+                //    Division
+                case '/':
+                    if ($operand2 == 0) {
+                        //    Trap for Divide by Zero error
+                        $stack->push('Error', ExcelError::DIV0());
+                        $this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails(ExcelError::DIV0()));
+
+                        return false;
+                    }
+                    $result = $operand1 / $operand2;
+
+                    break;
+                //    Power
+                case '^':
+                    $result = $operand1 ** $operand2;
+
+                    break;
+
+                default:
+                    throw new Exception('Unsupported numeric binary operation');
+            }
         }
 
         //    Log the result details
@@ -5377,13 +5354,13 @@ class Calculation
      *
      * @return false
      */
-    protected function raiseFormulaError(string $errorMessage)
+    protected function raiseFormulaError(string $errorMessage, int $code = 0, ?Throwable $exception = null): bool
     {
         $this->formulaError = $errorMessage;
         $this->cyclicReferenceStack->clear();
-        $suppress = /** @scrutinizer ignore-deprecated */ $this->suppressFormulaErrors ?? $this->suppressFormulaErrorsNew;
+        $suppress = $this->suppressFormulaErrors;
         if (!$suppress) {
-            throw new Exception($errorMessage);
+            throw new Exception($errorMessage, $code, $exception);
         }
 
         return false;
@@ -5393,12 +5370,12 @@ class Calculation
      * Extract range values.
      *
      * @param string $range String based range representation
-     * @param Worksheet $worksheet Worksheet
+     * @param ?Worksheet $worksheet Worksheet
      * @param bool $resetLog Flag indicating whether calculation log should be reset or not
      *
-     * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+     * @return array Array of values in range if range contains more than one element. Otherwise, a single value is returned.
      */
-    public function extractCellRange(&$range = 'A1', ?Worksheet $worksheet = null, $resetLog = true)
+    public function extractCellRange(string &$range = 'A1', ?Worksheet $worksheet = null, bool $resetLog = true): array
     {
         // Return value
         $returnValue = [];
@@ -5406,7 +5383,7 @@ class Calculation
         if ($worksheet !== null) {
             $worksheetName = $worksheet->getTitle();
 
-            if (strpos($range, '!') !== false) {
+            if (str_contains($range, '!')) {
                 [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
                 $worksheet = ($this->spreadsheet === null) ? null : $this->spreadsheet->getSheetByName($worksheetName);
             }
@@ -5448,15 +5425,15 @@ class Calculation
      * @param null|Worksheet $worksheet Worksheet
      * @param bool $resetLog Flag indicating whether calculation log should be reset or not
      *
-     * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+     * @return array|string Array of values in range if range contains more than one element. Otherwise, a single value is returned.
      */
-    public function extractNamedRange(string &$range = 'A1', ?Worksheet $worksheet = null, $resetLog = true)
+    public function extractNamedRange(string &$range = 'A1', ?Worksheet $worksheet = null, bool $resetLog = true): string|array
     {
         // Return value
         $returnValue = [];
 
         if ($worksheet !== null) {
-            if (strpos($range, '!') !== false) {
+            if (str_contains($range, '!')) {
                 [$worksheetName, $range] = Worksheet::extractSheetTitle($range, true);
                 $worksheet = ($this->spreadsheet === null) ? null : $this->spreadsheet->getSheetByName($worksheetName);
             }
@@ -5464,7 +5441,7 @@ class Calculation
             // Named range?
             $namedRange = ($worksheet === null) ? null : DefinedName::resolveName($range, $worksheet);
             if ($namedRange === null) {
-                return Information\ExcelError::REF();
+                return ExcelError::REF();
             }
 
             $worksheet = $namedRange->getWorksheet();
@@ -5508,10 +5485,8 @@ class Calculation
      * Is a specific function implemented?
      *
      * @param string $function Function Name
-     *
-     * @return bool
      */
-    public function isImplemented($function)
+    public function isImplemented(string $function): bool
     {
         $function = strtoupper($function);
         $notImplemented = !isset(self::$phpSpreadsheetFunctions[$function]) || (is_array(self::$phpSpreadsheetFunctions[$function]['functionCall']) && self::$phpSpreadsheetFunctions[$function]['functionCall'][1] === 'DUMMY');
@@ -5529,10 +5504,8 @@ class Calculation
 
     /**
      * Get a list of implemented Excel function names.
-     *
-     * @return array
      */
-    public function getImplementedFunctionNames()
+    public function getImplementedFunctionNames(): array
     {
         $returnValue = [];
         foreach (self::$phpSpreadsheetFunctions as $functionName => $function) {
@@ -5546,7 +5519,7 @@ class Calculation
 
     private function addDefaultArgumentValues(array $functionCall, array $args, array $emptyArguments): array
     {
-        $reflector = new ReflectionMethod(implode('::', $functionCall));
+        $reflector = new ReflectionMethod($functionCall[0], $functionCall[1]);
         $methodArguments = $reflector->getParameters();
 
         if (count($methodArguments) > 0) {
@@ -5555,8 +5528,8 @@ class Calculation
                 if ($isArgumentEmpty === true) {
                     $reflectedArgumentId = count($args) - (int) $argumentId - 1;
                     if (
-                        !array_key_exists($reflectedArgumentId, $methodArguments) ||
-                        $methodArguments[$reflectedArgumentId]->isVariadic()
+                        !array_key_exists($reflectedArgumentId, $methodArguments)
+                        || $methodArguments[$reflectedArgumentId]->isVariadic()
                     ) {
                         break;
                     }
@@ -5569,10 +5542,7 @@ class Calculation
         return $args;
     }
 
-    /**
-     * @return null|mixed
-     */
-    private function getArgumentDefaultValue(ReflectionParameter $methodArgument)
+    private function getArgumentDefaultValue(ReflectionParameter $methodArgument): mixed
     {
         $defaultValue = null;
 
@@ -5581,7 +5551,7 @@ class Calculation
             if ($methodArgument->isDefaultValueConstant()) {
                 $constantName = $methodArgument->getDefaultValueConstantName() ?? '';
                 // read constant value
-                if (strpos($constantName, '::') !== false) {
+                if (str_contains($constantName, '::')) {
                     [$className, $constantName] = explode('::', $constantName);
                     $constantReflector = new ReflectionClassConstant($className, $constantName);
 
@@ -5597,13 +5567,8 @@ class Calculation
 
     /**
      * Add cell reference if needed while making sure that it is the last argument.
-     *
-     * @param bool $passCellReference
-     * @param array|string $functionCall
-     *
-     * @return array
      */
-    private function addCellReference(array $args, $passCellReference, $functionCall, ?Cell $cell = null)
+    private function addCellReference(array $args, bool $passCellReference, array|string $functionCall, ?Cell $cell = null): array
     {
         if ($passCellReference) {
             if (is_array($functionCall)) {
@@ -5623,34 +5588,12 @@ class Calculation
         return $args;
     }
 
-    /**
-     * @param array $tokens
-     *
-     * @return string
-     */
-    private function getTokensAsString($tokens)
-    {
-        $tokensStr = array_map(function ($token) {
-            $value = $token['value'] ?? 'no value';
-            while (is_array($value)) {
-                $value = array_pop($value);
-            }
-
-            return $value;
-        }, $tokens);
-
-        return '[ ' . implode(' | ', $tokensStr) . ' ]';
-    }
-
-    /**
-     * @return mixed|string
-     */
-    private function evaluateDefinedName(Cell $cell, DefinedName $namedRange, Worksheet $cellWorksheet, Stack $stack)
+    private function evaluateDefinedName(Cell $cell, DefinedName $namedRange, Worksheet $cellWorksheet, Stack $stack, bool $ignoreScope = false): mixed
     {
         $definedNameScope = $namedRange->getScope();
-        if ($definedNameScope !== null && $definedNameScope !== $cellWorksheet) {
+        if ($definedNameScope !== null && $definedNameScope !== $cellWorksheet && !$ignoreScope) {
             // The defined name isn't in our current scope, so #REF
-            $result = Information\ExcelError::REF();
+            $result = ExcelError::REF();
             $stack->push('Error', $result, $namedRange->getName());
 
             return $result;
@@ -5666,7 +5609,8 @@ class Calculation
 
         $this->debugLog->writeDebugLog('Defined Name is a %s with a value of %s', $definedNameType, $definedNameValue);
 
-        $recursiveCalculationCell = ($definedNameWorksheet !== null && $definedNameWorksheet !== $cellWorksheet)
+        $originalCoordinate = $cell->getCoordinate();
+        $recursiveCalculationCell = ($definedNameType !== 'Formula' && $definedNameWorksheet !== null && $definedNameWorksheet !== $cellWorksheet)
             ? $definedNameWorksheet->getCell('A1')
             : $cell;
         $recursiveCalculationCellAddress = $recursiveCalculationCell->getCoordinate();
@@ -5683,7 +5627,8 @@ class Calculation
         $recursiveCalculator = new self($this->spreadsheet);
         $recursiveCalculator->getDebugLog()->setWriteDebugLog($this->getDebugLog()->getWriteDebugLog());
         $recursiveCalculator->getDebugLog()->setEchoDebugLog($this->getDebugLog()->getEchoDebugLog());
-        $result = $recursiveCalculator->_calculateFormulaValue($definedNameValue, $recursiveCalculationCellAddress, $recursiveCalculationCell);
+        $result = $recursiveCalculator->_calculateFormulaValue($definedNameValue, $recursiveCalculationCellAddress, $recursiveCalculationCell, true);
+        $cellWorksheet->getCell($originalCoordinate);
 
         if ($this->getDebugLog()->getWriteDebugLog()) {
             $this->debugLog->mergeDebugLog(array_slice($recursiveCalculator->getDebugLog()->getLog(), 3));
@@ -5697,17 +5642,32 @@ class Calculation
 
     public function setSuppressFormulaErrors(bool $suppressFormulaErrors): void
     {
-        $this->suppressFormulaErrorsNew = $suppressFormulaErrors;
+        $this->suppressFormulaErrors = $suppressFormulaErrors;
     }
 
     public function getSuppressFormulaErrors(): bool
     {
-        return $this->suppressFormulaErrorsNew;
+        return $this->suppressFormulaErrors;
     }
 
-    /** @param mixed $arg */
-    private static function doNothing($arg): bool
+    private static function boolToString(mixed $operand1): mixed
     {
-        return (bool) $arg;
+        if (is_bool($operand1)) {
+            $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
+        } elseif ($operand1 === null) {
+            $operand1 = '';
+        }
+
+        return $operand1;
+    }
+
+    private static function isNumericOrBool(mixed $operand): bool
+    {
+        return is_numeric($operand) || is_bool($operand);
+    }
+
+    private static function makeError(mixed $operand = ''): string
+    {
+        return Information\ErrorValue::isError($operand) ? $operand : ExcelError::VALUE();
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
deleted file mode 100644
index 017c571..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database.php
+++ /dev/null
@@ -1,430 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-/**
- * @deprecated 1.17.0
- *
- * @codeCoverageIgnore
- */
-class Database
-{
-    /**
-     * DAVERAGE.
-     *
-     * Averages the values in a column of a list or database that match conditions you specify.
-     *
-     * Excel Function:
-     *        DAVERAGE(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DAverage class instead
-     * @see Database\DAverage::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return float|string
-     */
-    public static function DAVERAGE($database, $field, $criteria)
-    {
-        return Database\DAverage::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DCOUNT.
-     *
-     * Counts the cells that contain numbers in a column of a list or database that match conditions
-     * that you specify.
-     *
-     * Excel Function:
-     *        DCOUNT(database,[field],criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DCount class instead
-     * @see Database\DCount::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param null|int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return int|string
-     *
-     * @TODO    The field argument is optional. If field is omitted, DCOUNT counts all records in the
-     *            database that match the criteria.
-     */
-    public static function DCOUNT($database, $field, $criteria)
-    {
-        return Database\DCount::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DCOUNTA.
-     *
-     * Counts the nonblank cells in a column of a list or database that match conditions that you specify.
-     *
-     * Excel Function:
-     *        DCOUNTA(database,[field],criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DCountA class instead
-     * @see Database\DCountA::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return int|string
-     */
-    public static function DCOUNTA($database, $field, $criteria)
-    {
-        return Database\DCountA::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DGET.
-     *
-     * Extracts a single value from a column of a list or database that matches conditions that you
-     * specify.
-     *
-     * Excel Function:
-     *        DGET(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DGet class instead
-     * @see Database\DGet::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return mixed
-     */
-    public static function DGET($database, $field, $criteria)
-    {
-        return Database\DGet::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DMAX.
-     *
-     * Returns the largest number in a column of a list or database that matches conditions you that
-     * specify.
-     *
-     * Excel Function:
-     *        DMAX(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DMax class instead
-     * @see Database\DMax::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return null|float|string
-     */
-    public static function DMAX($database, $field, $criteria)
-    {
-        return Database\DMax::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DMIN.
-     *
-     * Returns the smallest number in a column of a list or database that matches conditions you that
-     * specify.
-     *
-     * Excel Function:
-     *        DMIN(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DMin class instead
-     * @see Database\DMin::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return null|float|string
-     */
-    public static function DMIN($database, $field, $criteria)
-    {
-        return Database\DMin::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DPRODUCT.
-     *
-     * Multiplies the values in a column of a list or database that match conditions that you specify.
-     *
-     * Excel Function:
-     *        DPRODUCT(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DProduct class instead
-     * @see Database\DProduct::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return float|string
-     */
-    public static function DPRODUCT($database, $field, $criteria)
-    {
-        return Database\DProduct::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DSTDEV.
-     *
-     * Estimates the standard deviation of a population based on a sample by using the numbers in a
-     * column of a list or database that match conditions that you specify.
-     *
-     * Excel Function:
-     *        DSTDEV(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DStDev class instead
-     * @see Database\DStDev::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return float|string
-     */
-    public static function DSTDEV($database, $field, $criteria)
-    {
-        return Database\DStDev::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DSTDEVP.
-     *
-     * Calculates the standard deviation of a population based on the entire population by using the
-     * numbers in a column of a list or database that match conditions that you specify.
-     *
-     * Excel Function:
-     *        DSTDEVP(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DStDevP class instead
-     * @see Database\DStDevP::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return float|string
-     */
-    public static function DSTDEVP($database, $field, $criteria)
-    {
-        return Database\DStDevP::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DSUM.
-     *
-     * Adds the numbers in a column of a list or database that match conditions that you specify.
-     *
-     * Excel Function:
-     *        DSUM(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DSum class instead
-     * @see Database\DSum::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return null|float|string
-     */
-    public static function DSUM($database, $field, $criteria)
-    {
-        return Database\DSum::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DVAR.
-     *
-     * Estimates the variance of a population based on a sample by using the numbers in a column
-     * of a list or database that match conditions that you specify.
-     *
-     * Excel Function:
-     *        DVAR(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DVar class instead
-     * @see Database\DVar::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return float|string (string if result is an error)
-     */
-    public static function DVAR($database, $field, $criteria)
-    {
-        return Database\DVar::evaluate($database, $field, $criteria);
-    }
-
-    /**
-     * DVARP.
-     *
-     * Calculates the variance of a population based on the entire population by using the numbers
-     * in a column of a list or database that match conditions that you specify.
-     *
-     * Excel Function:
-     *        DVARP(database,field,criteria)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the Database\DVarP class instead
-     * @see Database\DVarP::evaluate()
-     *
-     * @param mixed[] $database The range of cells that makes up the list or database.
-     *                                        A database is a list of related data in which rows of related
-     *                                        information are records, and columns of data are fields. The
-     *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
-     *                                        column label enclosed between double quotation marks, such as
-     *                                        "Age" or "Yield," or a number (without quotation marks) that
-     *                                        represents the position of the column within the list: 1 for
-     *                                        the first column, 2 for the second column, and so on.
-     * @param mixed[] $criteria The range of cells that contains the conditions you specify.
-     *                                        You can use any range for the criteria argument, as long as it
-     *                                        includes at least one column label and at least one cell below
-     *                                        the column label in which you specify a condition for the
-     *                                        column.
-     *
-     * @return float|string (string if result is an error)
-     */
-    public static function DVARP($database, $field, $criteria)
-    {
-        return Database\DVarP::evaluate($database, $field, $criteria);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php
index 245e970..e54f1bb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DAverage.php
@@ -19,7 +19,7 @@ class DAverage extends DatabaseAbstract
      *                          A database is a list of related data in which rows of related
      *                              information are records, and columns of data are fields. The
      *                              first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                              column label enclosed between double quotation marks, such as
      *                              "Age" or "Yield," or a number (without quotation marks) that
      *                              represents the position of the column within the list: 1 for
@@ -29,10 +29,8 @@ class DAverage extends DatabaseAbstract
      *                              includes at least one column label and at least one cell below
      *                              the column label in which you specify a condition for the
      *                              column.
-     *
-     * @return float|string
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|int|float
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php
index 66e1987..fff7ab0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php
@@ -20,7 +20,7 @@ class DCount extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param null|int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -30,10 +30,8 @@ class DCount extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return int|string
      */
-    public static function evaluate($database, $field, $criteria, bool $returnError = true)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnError = true): string|int
     {
         $field = self::fieldExtract($database, $field);
         if ($returnError && $field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php
index 214c20b..f1a68c1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php
@@ -19,7 +19,7 @@ class DCountA extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -29,10 +29,8 @@ class DCountA extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return int|string
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|int
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php
index 5647cba..dd0f006 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DGet.php
@@ -19,7 +19,7 @@ class DGet extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -29,10 +29,8 @@ class DGet extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return mixed
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): null|float|int|string
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
index 748fd2f..23b95a7 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMax.php
@@ -20,7 +20,7 @@ class DMax extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -30,10 +30,8 @@ class DMax extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return null|float|string
      */
-    public static function evaluate($database, $field, $criteria, bool $returnError = true)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnError = true): null|float|string
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMin.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMin.php
index 544cbff..541803d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMin.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DMin.php
@@ -20,7 +20,7 @@ class DMin extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -30,10 +30,8 @@ class DMin extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return null|float|string
      */
-    public static function evaluate($database, $field, $criteria, bool $returnError = true)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnError = true): float|string|null
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DProduct.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DProduct.php
index 351ee6f..b60aa0d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DProduct.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DProduct.php
@@ -19,7 +19,7 @@ class DProduct extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -29,10 +29,8 @@ class DProduct extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return float|string
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|float
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDev.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDev.php
index a1c5653..dc35405 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDev.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDev.php
@@ -20,7 +20,7 @@ class DStDev extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -30,10 +30,8 @@ class DStDev extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return float|string
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): float|string
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDevP.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDevP.php
index 5bca4e7..a05d596 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDevP.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DStDevP.php
@@ -20,7 +20,7 @@ class DStDevP extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -30,10 +30,8 @@ class DStDevP extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return float|string
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): float|string
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DSum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DSum.php
index ef4d10e..f9f926b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DSum.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DSum.php
@@ -19,7 +19,7 @@ class DSum extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -29,10 +29,8 @@ class DSum extends DatabaseAbstract
      *                                        includes at least one column label and at least one cell below
      *                                        the column label in which you specify a condition for the
      *                                        column.
-     *
-     * @return null|float|string
      */
-    public static function evaluate($database, $field, $criteria, bool $returnNull = false)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria, bool $returnNull = false): null|float|string
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVar.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVar.php
index 9cfa971..33b5b56 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVar.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVar.php
@@ -20,7 +20,7 @@ class DVar extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -33,7 +33,7 @@ class DVar extends DatabaseAbstract
      *
      * @return float|string (string if result is an error)
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|float
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVarP.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVarP.php
index 67dbd68..942a4a1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVarP.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DVarP.php
@@ -20,7 +20,7 @@ class DVarP extends DatabaseAbstract
      *                                        A database is a list of related data in which rows of related
      *                                        information are records, and columns of data are fields. The
      *                                        first row of the list contains labels for each column.
-     * @param int|string $field Indicates which column is used in the function. Enter the
+     * @param null|array|int|string $field Indicates which column is used in the function. Enter the
      *                                        column label enclosed between double quotation marks, such as
      *                                        "Age" or "Yield," or a number (without quotation marks) that
      *                                        represents the position of the column within the list: 1 for
@@ -33,7 +33,7 @@ class DVarP extends DatabaseAbstract
      *
      * @return float|string (string if result is an error)
      */
-    public static function evaluate($database, $field, $criteria)
+    public static function evaluate(array $database, array|null|int|string $field, array $criteria): string|float
     {
         $field = self::fieldExtract($database, $field);
         if ($field === null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php
index 0b8c36d..7d9885e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php
@@ -8,14 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
 
 abstract class DatabaseAbstract
 {
-    /**
-     * @param array $database
-     * @param int|string $field
-     * @param array $criteria
-     *
-     * @return null|float|int|string
-     */
-    abstract public static function evaluate($database, $field, $criteria);
+    abstract public static function evaluate(array $database, array|null|int|string $field, array $criteria): null|float|int|string;
 
     /**
      * fieldExtract.
@@ -32,7 +25,7 @@ abstract class DatabaseAbstract
      *                                        represents the position of the column within the list: 1 for
      *                                        the first column, 2 for the second column, and so on.
      */
-    protected static function fieldExtract(array $database, $field): ?int
+    protected static function fieldExtract(array $database, mixed $field): ?int
     {
         $field = strtoupper(Functions::flattenSingleValue($field) ?? '');
         if ($field === '') {
@@ -115,19 +108,14 @@ abstract class DatabaseAbstract
         }
 
         $rowQuery = array_map(
-            function ($rowValue) {
-                return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? '');
-            },
+            fn ($rowValue): string => (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? ''),
             $baseQuery
         );
 
         return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : ($rowQuery[0] ?? '');
     }
 
-    /**
-     * @param mixed $criterion
-     */
-    private static function buildCondition($criterion, string $criterionName): string
+    private static function buildCondition(mixed $criterion, string $criterionName): string
     {
         $ifCondition = Functions::ifCondition($criterion);
 
@@ -168,10 +156,7 @@ abstract class DatabaseAbstract
         return $database;
     }
 
-    /**
-     * @return mixed
-     */
-    private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions)
+    private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions): string
     {
         $key = array_search($criterion, $fields, true);
 
@@ -181,7 +166,7 @@ abstract class DatabaseAbstract
         } elseif ($dataValues[$key] !== null) {
             $dataValue = $dataValues[$key];
             // escape quotes if we have a string containing quotes
-            if (is_string($dataValue) && strpos($dataValue, '"') !== false) {
+            if (is_string($dataValue) && str_contains($dataValue, '"')) {
                 $dataValue = str_replace('"', '""', $dataValue);
             }
             $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
deleted file mode 100644
index 26b667c..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
+++ /dev/null
@@ -1,890 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-use DateTimeInterface;
-
-/**
- * @deprecated 1.18.0
- */
-class DateTime
-{
-    /**
-     * Identify if a year is a leap year or not.
-     *
-     * @deprecated 1.18.0
-     *      Use the isLeapYear method in the DateTimeExcel\Helpers class instead
-     * @see DateTimeExcel\Helpers::isLeapYear()
-     *
-     * @param int|string $year The year to test
-     *
-     * @return bool TRUE if the year is a leap year, otherwise FALSE
-     */
-    public static function isLeapYear($year)
-    {
-        return DateTimeExcel\Helpers::isLeapYear($year);
-    }
-
-    /**
-     * getDateValue.
-     *
-     * @deprecated 1.18.0
-     *      Use the getDateValue method in the DateTimeExcel\Helpers class instead
-     * @see DateTimeExcel\Helpers::getDateValue()
-     *
-     * @param mixed $dateValue
-     *
-     * @return mixed Excel date/time serial value, or string if error
-     */
-    public static function getDateValue($dateValue)
-    {
-        try {
-            return DateTimeExcel\Helpers::getDateValue($dateValue);
-        } catch (Exception $e) {
-            return $e->getMessage();
-        }
-    }
-
-    /**
-     * DATETIMENOW.
-     *
-     * Returns the current date and time.
-     * The NOW function is useful when you need to display the current date and time on a worksheet or
-     * calculate a value based on the current date and time, and have that value updated each time you
-     * open the worksheet.
-     *
-     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
-     * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
-     *
-     * Excel Function:
-     *        NOW()
-     *
-     * @deprecated 1.18.0
-     *      Use the now method in the DateTimeExcel\Current class instead
-     * @see DateTimeExcel\Current::now()
-     *
-     * @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
-     */
-    public static function DATETIMENOW()
-    {
-        return DateTimeExcel\Current::now();
-    }
-
-    /**
-     * DATENOW.
-     *
-     * Returns the current date.
-     * The NOW function is useful when you need to display the current date and time on a worksheet or
-     * calculate a value based on the current date and time, and have that value updated each time you
-     * open the worksheet.
-     *
-     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
-     * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
-     *
-     * Excel Function:
-     *        TODAY()
-     *
-     * @deprecated 1.18.0
-     *      Use the today method in the DateTimeExcel\Current class instead
-     * @see DateTimeExcel\Current::today()
-     *
-     * @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
-     */
-    public static function DATENOW()
-    {
-        return DateTimeExcel\Current::today();
-    }
-
-    /**
-     * DATE.
-     *
-     * The DATE function returns a value that represents a particular date.
-     *
-     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
-     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
-     *
-     *
-     * Excel Function:
-     *        DATE(year,month,day)
-     *
-     * @deprecated 1.18.0
-     *      Use the fromYMD method in the DateTimeExcel\Date class instead
-     * @see DateTimeExcel\Date::fromYMD()
-     *
-     * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function.
-     * 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.
-     *                                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
-     *                                value to 1900 to calculate the year. For example, DATE(108,1,2)
-     *                                returns January 2, 2008 (1900+108).
-     *                                If year is between 1900 and 9999 (inclusive), Excel uses that
-     *                                value as the year. For example, DATE(2008,1,2) returns January 2,
-     *                                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
-     *                                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)
-     *                                returns the serial number representing February 2, 2009.
-     *                                If month is less than 1, month subtracts the magnitude of that
-     *                                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
-     *                                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
-     *                                example, DATE(2008,1,35) returns the serial number representing
-     *                                February 4, 2008.
-     *                                If day is less than 1, day subtracts the magnitude that number of
-     *                                days, plus one, from the first day of the month specified. For
-     *                                example, DATE(2008,1,-15) returns the serial number representing
-     *                                December 16, 2007.
-     *
-     * @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
-     */
-    public static function DATE($year = 0, $month = 1, $day = 1)
-    {
-        return DateTimeExcel\Date::fromYMD($year, $month, $day);
-    }
-
-    /**
-     * TIME.
-     *
-     * The TIME function returns a value that represents a particular time.
-     *
-     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
-     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
-     *
-     * Excel Function:
-     *        TIME(hour,minute,second)
-     *
-     * @deprecated 1.18.0
-     *      Use the fromHMS method in the DateTimeExcel\Time class instead
-     * @see DateTimeExcel\Time::fromHMS()
-     *
-     * @param 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 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 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
-     *
-     * @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
-     */
-    public static function TIME($hour = 0, $minute = 0, $second = 0)
-    {
-        return DateTimeExcel\Time::fromHMS($hour, $minute, $second);
-    }
-
-    /**
-     * DATEVALUE.
-     *
-     * Returns a value that represents a particular date.
-     * Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
-     * value.
-     *
-     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
-     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
-     *
-     * Excel Function:
-     *        DATEVALUE(dateValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the fromString method in the DateTimeExcel\DateValue class instead
-     * @see DateTimeExcel\DateValue::fromString()
-     *
-     * @param 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
-     *                                    January 1, 1900, to December 31, 9999. Using the default date
-     *                                    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.
-     *
-     * @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
-     */
-    public static function DATEVALUE($dateValue)
-    {
-        return DateTimeExcel\DateValue::fromString($dateValue);
-    }
-
-    /**
-     * TIMEVALUE.
-     *
-     * Returns a value that represents a particular time.
-     * Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
-     * value.
-     *
-     * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
-     * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
-     *
-     * Excel Function:
-     *        TIMEVALUE(timeValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the fromString method in the DateTimeExcel\TimeValue class instead
-     * @see DateTimeExcel\TimeValue::fromString()
-     *
-     * @param 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.
-     *
-     * @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
-     */
-    public static function TIMEVALUE($timeValue)
-    {
-        return DateTimeExcel\TimeValue::fromString($timeValue);
-    }
-
-    /**
-     * DATEDIF.
-     *
-     * Excel Function:
-     *        DATEDIF(startdate, enddate, unit)
-     *
-     * @deprecated 1.18.0
-     *      Use the interval method in the DateTimeExcel\Difference class instead
-     * @see DateTimeExcel\Difference::interval()
-     *
-     * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
-     *                                    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 array|string $unit
-     *
-     * @return array|int|string Interval between the dates
-     */
-    public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
-    {
-        return DateTimeExcel\Difference::interval($startDate, $endDate, $unit);
-    }
-
-    /**
-     * DAYS.
-     *
-     * Returns the number of days between two dates
-     *
-     * Excel Function:
-     *        DAYS(endDate, startDate)
-     *
-     * @deprecated 1.18.0
-     *      Use the between method in the DateTimeExcel\Days class instead
-     * @see DateTimeExcel\Days::between()
-     *
-     * @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 array|DateTimeInterface|float|int|string $startDate Excel date serial value (float),
-     * PHP date timestamp (integer), PHP DateTime object, or a standard date string
-     *
-     * @return array|int|string Number of days between start date and end date or an error
-     */
-    public static function DAYS($endDate = 0, $startDate = 0)
-    {
-        return DateTimeExcel\Days::between($endDate, $startDate);
-    }
-
-    /**
-     * DAYS360.
-     *
-     * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
-     * which is used in some accounting calculations. Use this function to help compute payments if
-     * your accounting system is based on twelve 30-day months.
-     *
-     * Excel Function:
-     *        DAYS360(startDate,endDate[,method])
-     *
-     * @deprecated 1.18.0
-     *      Use the between method in the DateTimeExcel\Days360 class instead
-     * @see DateTimeExcel\Days360::between()
-     *
-     * @param 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),
-     *                                        PHP DateTime object, or a standard date string
-     * @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
-     *                                        the starting date is earlier than the 30th of a month, the
-     *                                        ending date becomes equal to the 1st of the next month;
-     *                                        otherwise the ending date becomes equal to the 30th of the
-     *                                        same month.
-     *                                        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.
-     *
-     * @return array|int|string Number of days between start date and end date
-     */
-    public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
-    {
-        return DateTimeExcel\Days360::between($startDate, $endDate, $method);
-    }
-
-    /**
-     * YEARFRAC.
-     *
-     * Calculates the fraction of the year represented by the number of whole days between two dates
-     * (the start_date and the end_date).
-     * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
-     * obligations to assign to a specific term.
-     *
-     * Excel Function:
-     *        YEARFRAC(startDate,endDate[,method])
-     *
-     * @deprecated 1.18.0
-     *      Use the fraction method in the DateTimeExcel\YearFrac class instead
-     * @see DateTimeExcel\YearFrac::fraction()
-     *
-     * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
-     *     for description of algorithm used in Excel
-     *
-     * @param 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),
-     *                                    PHP DateTime object, or a standard date string
-     * @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 array|float|string fraction of the year, or a string containing an error
-     */
-    public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
-    {
-        return DateTimeExcel\YearFrac::fraction($startDate, $endDate, $method);
-    }
-
-    /**
-     * NETWORKDAYS.
-     *
-     * Returns the number of whole working days between start_date and end_date. Working days
-     * exclude weekends and any dates identified in holidays.
-     * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
-     * worked during a specific term.
-     *
-     * Excel Function:
-     *        NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
-     *
-     * @deprecated 1.18.0
-     *      Use the count method in the DateTimeExcel\NetworkDays class instead
-     * @see DateTimeExcel\NetworkDays::count()
-     *
-     * @param 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),
-     *                                            PHP DateTime object, or a standard date string
-     * @param mixed $dateArgs
-     *
-     * @return array|int|string Interval between the dates
-     */
-    public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs)
-    {
-        return DateTimeExcel\NetworkDays::count($startDate, $endDate, ...$dateArgs);
-    }
-
-    /**
-     * WORKDAY.
-     *
-     * Returns the date that is the indicated number of working days before or after a date (the
-     * starting date). Working days exclude weekends and any dates identified as holidays.
-     * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
-     * delivery times, or the number of days of work performed.
-     *
-     * Excel Function:
-     *        WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
-     *
-     * @deprecated 1.18.0
-     *      Use the date method in the DateTimeExcel\WorkDay class instead
-     * @see DateTimeExcel\WorkDay::date()
-     *
-     * @param 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
-     *                                        startDate. A positive value for days yields a future date; a
-     *                                        negative value yields a past date.
-     * @param mixed $dateArgs
-     *
-     * @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
-     */
-    public static function WORKDAY($startDate, $endDays, ...$dateArgs)
-    {
-        return DateTimeExcel\WorkDay::date($startDate, $endDays, ...$dateArgs);
-    }
-
-    /**
-     * DAYOFMONTH.
-     *
-     * Returns the day of the month, for a specified date. The day is given as an integer
-     * ranging from 1 to 31.
-     *
-     * Excel Function:
-     *        DAY(dateValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the day method in the DateTimeExcel\DateParts class instead
-     * @see DateTimeExcel\DateParts::day()
-     *
-     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard date string
-     *
-     * @return array|int|string Day of the month
-     */
-    public static function DAYOFMONTH($dateValue = 1)
-    {
-        return DateTimeExcel\DateParts::day($dateValue);
-    }
-
-    /**
-     * WEEKDAY.
-     *
-     * Returns the day of the week for a specified date. The day is given as an integer
-     * ranging from 0 to 7 (dependent on the requested style).
-     *
-     * Excel Function:
-     *        WEEKDAY(dateValue[,style])
-     *
-     * @deprecated 1.18.0
-     *      Use the day method in the DateTimeExcel\Week class instead
-     * @see DateTimeExcel\Week::day()
-     *
-     * @param float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard date string
-     * @param int $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).
-     *
-     * @return array|int|string Day of the week value
-     */
-    public static function WEEKDAY($dateValue = 1, $style = 1)
-    {
-        return DateTimeExcel\Week::day($dateValue, $style);
-    }
-
-    /**
-     * STARTWEEK_SUNDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_SUNDAY
-     * @see DateTimeExcel\Constants::STARTWEEK_SUNDAY
-     */
-    const STARTWEEK_SUNDAY = 1;
-
-    /**
-     * STARTWEEK_MONDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_MONDAY
-     * @see DateTimeExcel\Constants::STARTWEEK_MONDAY
-     */
-    const STARTWEEK_MONDAY = 2;
-
-    /**
-     * STARTWEEK_MONDAY_ALT.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
-     * @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ALT
-     */
-    const STARTWEEK_MONDAY_ALT = 11;
-
-    /**
-     * STARTWEEK_TUESDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_TUESDAY
-     * @see DateTimeExcel\Constants::STARTWEEK_TUESDAY
-     */
-    const STARTWEEK_TUESDAY = 12;
-
-    /**
-     * STARTWEEK_WEDNESDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
-     * @see DateTimeExcel\Constants::STARTWEEK_WEDNESDAY
-     */
-    const STARTWEEK_WEDNESDAY = 13;
-
-    /**
-     * STARTWEEK_THURSDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_THURSDAY
-     * @see DateTimeExcel\Constants::STARTWEEK_THURSDAY
-     */
-    const STARTWEEK_THURSDAY = 14;
-
-    /**
-     * STARTWEEK_FRIDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_FRIDAY
-     * @see DateTimeExcel\Constants::STARTWEEK_FRIDAY
-     */
-    const STARTWEEK_FRIDAY = 15;
-
-    /**
-     * STARTWEEK_SATURDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_SATURDAY
-     * @see DateTimeExcel\Constants::STARTWEEK_SATURDAY
-     */
-    const STARTWEEK_SATURDAY = 16;
-
-    /**
-     * STARTWEEK_SUNDAY_ALT.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
-     * @see DateTimeExcel\Constants::STARTWEEK_SUNDAY_ALT
-     */
-    const STARTWEEK_SUNDAY_ALT = 17;
-
-    /**
-     * DOW_SUNDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::DOW_SUNDAY
-     * @see DateTimeExcel\Constants::DOW_SUNDAY
-     */
-    const DOW_SUNDAY = 1;
-
-    /**
-     * DOW_MONDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::DOW_MONDAY
-     * @see DateTimeExcel\Constants::DOW_MONDAY
-     */
-    const DOW_MONDAY = 2;
-
-    /**
-     * DOW_TUESDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::DOW_TUESDAY
-     * @see DateTimeExcel\Constants::DOW_TUESDAY
-     */
-    const DOW_TUESDAY = 3;
-
-    /**
-     * DOW_WEDNESDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::DOW_WEDNESDAY
-     * @see DateTimeExcel\Constants::DOW_WEDNESDAY
-     */
-    const DOW_WEDNESDAY = 4;
-
-    /**
-     * DOW_THURSDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::DOW_THURSDAY
-     * @see DateTimeExcel\Constants::DOW_THURSDAY
-     */
-    const DOW_THURSDAY = 5;
-
-    /**
-     * DOW_FRIDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::DOW_FRIDAY
-     * @see DateTimeExcel\Constants::DOW_FRIDAY
-     */
-    const DOW_FRIDAY = 6;
-
-    /**
-     * DOW_SATURDAY.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::DOW_SATURDAY
-     * @see DateTimeExcel\Constants::DOW_SATURDAY
-     */
-    const DOW_SATURDAY = 7;
-
-    /**
-     * STARTWEEK_MONDAY_ISO.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
-     * @see DateTimeExcel\Constants::STARTWEEK_MONDAY_ISO
-     */
-    const STARTWEEK_MONDAY_ISO = 21;
-
-    /**
-     * METHODARR.
-     *
-     * @deprecated 1.18.0
-     *  Use DateTimeExcel\Constants::METHODARR
-     * @see DateTimeExcel\Constants::METHODARR
-     */
-    const METHODARR = [
-        self::STARTWEEK_SUNDAY => self::DOW_SUNDAY,
-        self::DOW_MONDAY,
-        self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
-        self::DOW_TUESDAY,
-        self::DOW_WEDNESDAY,
-        self::DOW_THURSDAY,
-        self::DOW_FRIDAY,
-        self::DOW_SATURDAY,
-        self::DOW_SUNDAY,
-        self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
-    ];
-
-    /**
-     * WEEKNUM.
-     *
-     * Returns the week of the year for a specified date.
-     * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
-     * However, there is a European standard that defines the first week as the one with the majority
-     * of days (four or more) falling in the new year. This means that for years in which there are
-     * three days or less in the first week of January, the WEEKNUM function returns week numbers
-     * that are incorrect according to the European standard.
-     *
-     * Excel Function:
-     *        WEEKNUM(dateValue[,style])
-     *
-     * @deprecated 1.18.0
-     *      Use the number method in the DateTimeExcel\Week class instead
-     * @see DateTimeExcel\Week::number()
-     *
-     * @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
-     *                                        1 or omitted    Week begins on Sunday.
-     *                                        2                Week begins on Monday.
-     *                                        11               Week begins on Monday.
-     *                                        12               Week begins on Tuesday.
-     *                                        13               Week begins on Wednesday.
-     *                                        14               Week begins on Thursday.
-     *                                        15               Week begins on Friday.
-     *                                        16               Week begins on Saturday.
-     *                                        17               Week begins on Sunday.
-     *                                        21               ISO (Jan. 4 is week 1, begins on Monday).
-     *
-     * @return array|int|string Week Number
-     */
-    public static function WEEKNUM($dateValue = 1, $method = /** @scrutinizer ignore-deprecated */ self::STARTWEEK_SUNDAY)
-    {
-        return DateTimeExcel\Week::number($dateValue, $method);
-    }
-
-    /**
-     * ISOWEEKNUM.
-     *
-     * Returns the ISO 8601 week number of the year for a specified date.
-     *
-     * Excel Function:
-     *        ISOWEEKNUM(dateValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the isoWeekNumber method in the DateTimeExcel\Week class instead
-     * @see DateTimeExcel\Week::isoWeekNumber()
-     *
-     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard date string
-     *
-     * @return array|int|string Week Number
-     */
-    public static function ISOWEEKNUM($dateValue = 1)
-    {
-        return DateTimeExcel\Week::isoWeekNumber($dateValue);
-    }
-
-    /**
-     * MONTHOFYEAR.
-     *
-     * Returns the month of a date represented by a serial number.
-     * The month is given as an integer, ranging from 1 (January) to 12 (December).
-     *
-     * Excel Function:
-     *        MONTH(dateValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the month method in the DateTimeExcel\DateParts class instead
-     * @see DateTimeExcel\DateParts::month()
-     *
-     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard date string
-     *
-     * @return array|int|string Month of the year
-     */
-    public static function MONTHOFYEAR($dateValue = 1)
-    {
-        return DateTimeExcel\DateParts::month($dateValue);
-    }
-
-    /**
-     * YEAR.
-     *
-     * Returns the year corresponding to a date.
-     * The year is returned as an integer in the range 1900-9999.
-     *
-     * Excel Function:
-     *        YEAR(dateValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the ear method in the DateTimeExcel\DateParts class instead
-     * @see DateTimeExcel\DateParts::year()
-     *
-     * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard date string
-     *
-     * @return array|int|string Year
-     */
-    public static function YEAR($dateValue = 1)
-    {
-        return DateTimeExcel\DateParts::year($dateValue);
-    }
-
-    /**
-     * HOUROFDAY.
-     *
-     * Returns the hour of a time value.
-     * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
-     *
-     * Excel Function:
-     *        HOUR(timeValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the hour method in the DateTimeExcel\TimeParts class instead
-     * @see DateTimeExcel\TimeParts::hour()
-     *
-     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard time string
-     *
-     * @return array|int|string Hour
-     */
-    public static function HOUROFDAY($timeValue = 0)
-    {
-        return DateTimeExcel\TimeParts::hour($timeValue);
-    }
-
-    /**
-     * MINUTE.
-     *
-     * Returns the minutes of a time value.
-     * The minute is given as an integer, ranging from 0 to 59.
-     *
-     * Excel Function:
-     *        MINUTE(timeValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the minute method in the DateTimeExcel\TimeParts class instead
-     * @see DateTimeExcel\TimeParts::minute()
-     *
-     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard time string
-     *
-     * @return array|int|string Minute
-     */
-    public static function MINUTE($timeValue = 0)
-    {
-        return DateTimeExcel\TimeParts::minute($timeValue);
-    }
-
-    /**
-     * SECOND.
-     *
-     * Returns the seconds of a time value.
-     * The second is given as an integer in the range 0 (zero) to 59.
-     *
-     * Excel Function:
-     *        SECOND(timeValue)
-     *
-     * @deprecated 1.18.0
-     *      Use the second method in the DateTimeExcel\TimeParts class instead
-     * @see DateTimeExcel\TimeParts::second()
-     *
-     * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
-     *                                    PHP DateTime object, or a standard time string
-     *
-     * @return array|int|string Second
-     */
-    public static function SECOND($timeValue = 0)
-    {
-        return DateTimeExcel\TimeParts::second($timeValue);
-    }
-
-    /**
-     * EDATE.
-     *
-     * Returns the serial number that represents the date that is the indicated number of months
-     * before or after a specified date (the start_date).
-     * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
-     * as the date of issue.
-     *
-     * Excel Function:
-     *        EDATE(dateValue,adjustmentMonths)
-     *
-     * @deprecated 1.18.0
-     *      Use the adjust method in the DateTimeExcel\Edate class instead
-     * @see DateTimeExcel\Month::adjust()
-     *
-     * @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.
-     *                                        A positive value for months yields a future date;
-     *                                        a negative value yields a past 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
-     */
-    public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
-    {
-        return DateTimeExcel\Month::adjust($dateValue, $adjustmentMonths);
-    }
-
-    /**
-     * EOMONTH.
-     *
-     * Returns the date value for the last day of the month that is the indicated number of months
-     * before or after start_date.
-     * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
-     *
-     * Excel Function:
-     *        EOMONTH(dateValue,adjustmentMonths)
-     *
-     * @deprecated 1.18.0
-     *      Use the lastDay method in the DateTimeExcel\EoMonth class instead
-     * @see DateTimeExcel\Month::lastDay()
-     *
-     * @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.
-     *                                        A positive value for months yields a future date;
-     *                                        a negative value yields a past 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
-     */
-    public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
-    {
-        return DateTimeExcel\Month::lastDay($dateValue, $adjustmentMonths);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
index 5de671d..088e379 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use DateTime;
 use DateTimeImmutable;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
@@ -21,10 +22,10 @@ class Current
      * Excel Function:
      *        TODAY()
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
      */
-    public static function today()
+    public static function today(): DateTime|float|int|string
     {
         $dti = new DateTimeImmutable();
         $dateArray = Helpers::dateParse($dti->format('c'));
@@ -46,10 +47,10 @@ class Current
      * Excel Function:
      *        NOW()
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,
      *                        depending on the value of the ReturnDateType flag
      */
-    public static function now()
+    public static function now(): DateTime|float|int|string
     {
         $dti = new DateTimeImmutable();
         $dateArray = Helpers::dateParse($dti->format('c'));
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
index 6b55e79..e0e4b25 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use DateTime;
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
@@ -27,7 +28,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 array|int $year The value of the year argument can include one to four digits.
+     * @param array|float|int|string $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
@@ -38,7 +39,7 @@ class Date
      *                                2008.
      *                                If year is less than 0 or is 10000 or greater, Excel returns the
      *                                #NUM! error value.
-     * @param array|int $month A positive or negative integer representing the month of the year
+     * @param array|float|int|string $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)
@@ -47,7 +48,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 array|int $day A positive or negative integer representing the day of the month
+     * @param array|float|int|string $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
@@ -58,12 +59,12 @@ class Date
      *                                example, DATE(2008,1,-15) returns the serial number representing
      *                                December 16, 2007.
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|DateTime|float|int|string 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)
+    public static function fromYMD(array|float|int|string $year, array|float|int|string $month, array|float|int|string $day): float|int|DateTime|string|array
     {
         if (is_array($year) || is_array($month) || is_array($day)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $year, $month, $day);
@@ -72,26 +73,24 @@ class Date
         $baseYear = SharedDateHelper::getExcelCalendar();
 
         try {
-            $year = self::getYear($year, $baseYear); // must be int - Scrutinizer is wrong
+            $year = self::getYear($year, $baseYear);
             $month = self::getMonth($month);
             $day = self::getDay($day);
-            self::adjustYearMonth(/** @scrutinizer ignore-type */ $year, $month, $baseYear);
+            self::adjustYearMonth($year, $month, $baseYear);
         } catch (Exception $e) {
             return $e->getMessage();
         }
 
         // Execute function
-        $excelDateValue = SharedDateHelper::formattedPHPToExcel(/** @scrutinizer ignore-type */ $year, $month, $day);
+        $excelDateValue = SharedDateHelper::formattedPHPToExcel($year, $month, $day);
 
         return Helpers::returnIn3FormatsFloat($excelDateValue);
     }
 
     /**
      * Convert year from multiple formats to int.
-     *
-     * @param mixed $year
      */
-    private static function getYear($year, int $baseYear): int
+    private static function getYear(mixed $year, int $baseYear): int
     {
         $year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
         if (!is_numeric($year)) {
@@ -115,10 +114,8 @@ class Date
 
     /**
      * Convert month from multiple formats to int.
-     *
-     * @param mixed $month
      */
-    private static function getMonth($month): int
+    private static function getMonth(mixed $month): int
     {
         if (($month !== null) && (!is_numeric($month))) {
             $month = SharedDateHelper::monthStringToNumber($month);
@@ -134,10 +131,8 @@ class Date
 
     /**
      * Convert day from multiple formats to int.
-     *
-     * @param mixed $day
      */
-    private static function getDay($day): int
+    private static function getDay(mixed $day): int
     {
         if (($day !== null) && (!is_numeric($day))) {
             $day = SharedDateHelper::dayStringToNumber($day);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
index b669eb0..60e4de1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php
@@ -28,7 +28,7 @@ class DateParts
      *         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)
+    public static function day(mixed $dateValue): array|int|string
     {
         if (is_array($dateValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -47,6 +47,7 @@ class DateParts
 
         // Execute function
         $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+        SharedDateHelper::roundMicroseconds($PHPDateObject);
 
         return (int) $PHPDateObject->format('j');
     }
@@ -68,7 +69,7 @@ class DateParts
      *         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)
+    public static function month(mixed $dateValue): array|string|int
     {
         if (is_array($dateValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -85,6 +86,7 @@ class DateParts
 
         // Execute function
         $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+        SharedDateHelper::roundMicroseconds($PHPDateObject);
 
         return (int) $PHPDateObject->format('n');
     }
@@ -106,7 +108,7 @@ class DateParts
      *         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)
+    public static function year(mixed $dateValue): array|string|int
     {
         if (is_array($dateValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -123,6 +125,7 @@ class DateParts
         }
         // Execute function
         $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
+        SharedDateHelper::roundMicroseconds($PHPDateObject);
 
         return (int) $PHPDateObject->format('Y');
     }
@@ -131,7 +134,7 @@ class DateParts
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
      */
-    private static function weirdCondition($dateValue): int
+    private static function weirdCondition(mixed $dateValue): int
     {
         // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR)
         if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
index 52543a7..8c5fa71 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use DateTime;
 use DateTimeImmutable;
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
@@ -24,7 +25,7 @@ class DateValue
      * Excel Function:
      *        DATEVALUE(dateValue)
      *
-     * @param array|string $dateValue Text that represents a date in a Microsoft Excel date format.
+     * @param null|array|bool|float|int|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
@@ -34,20 +35,25 @@ class DateValue
      *                                    #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,
+     * @return array|DateTime|float|int|string 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)
+    public static function fromString(null|array|string|int|bool|float $dateValue): array|string|float|int|DateTime
     {
         if (is_array($dateValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
         }
 
+        // try to parse as date iff there is at least one digit
+        if (is_string($dateValue) && preg_match('/\\d/', $dateValue) !== 1) {
+            return ExcelError::VALUE();
+        }
+
         $dti = new DateTimeImmutable();
         $baseYear = SharedDateHelper::getExcelCalendar();
-        $dateValue = trim($dateValue ?? '', '"');
+        $dateValue = trim((string) $dateValue, '"');
         //    Strip any ordinals because they're allowed in Excel (English only)
         $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)
@@ -69,7 +75,7 @@ class DateValue
         }
         if (count($t1) === 1) {
             //    We've been fed a time value without any date
-            return ((strpos((string) $t, ':') === false)) ? ExcelError::Value() : 0.0;
+            return ((!str_contains((string) $t, ':'))) ? ExcelError::Value() : 0.0;
         }
         unset($t);
 
@@ -126,10 +132,10 @@ class DateValue
     /**
      * Final results.
      *
-     * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return DateTime|float|int|string 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(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear)
+    private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear): string|float|int|DateTime
     {
         $retValue = ExcelError::Value();
         if (Helpers::dateParseSucceeded($PHPDateArray)) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
index a3b9745..6c6fd3d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php
@@ -31,7 +31,7 @@ class Days
      *         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)
+    public static function between(array|DateTimeInterface|float|int|string $endDate, array|DateTimeInterface|float|int|string $startDate): array|int|string
     {
         if (is_array($endDate) || is_array($startDate)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $endDate, $startDate);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
index 6f71621..c7e03fc 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php
@@ -44,7 +44,7 @@ class Days360
      *         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)
+    public static function between(mixed $startDate = 0, mixed $endDate = 0, mixed $method = false): array|string|int
     {
         if (is_array($startDate) || is_array($endDate) || is_array($method)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $method);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
index fd71c9b..199d5d8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php
@@ -22,14 +22,13 @@ class Difference
      * @param mixed $endDate 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 array|string $unit
-     *                         Or can be an array of unit values
+     * @param array|string $unit Or can be an array of unit values
      *
      * @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')
+    public static function interval(mixed $startDate, mixed $endDate, array|string $unit = 'D')
     {
         if (is_array($startDate) || is_array($endDate) || is_array($unit)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $unit);
@@ -80,12 +79,8 @@ class Difference
 
     /**
      * Decide whether it's time to set retVal.
-     *
-     * @param bool|int $retVal
-     *
-     * @return null|bool|int
      */
-    private static function replaceRetValue($retVal, string $unit, string $compare)
+    private static function replaceRetValue(bool|int $retVal, string $unit, string $compare): null|bool|int
     {
         if ($retVal !== false || $unit !== $compare) {
             return $retVal;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
index 2384515..1e9af6c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
@@ -17,19 +17,19 @@ class Helpers
      *
      * @return bool TRUE if the year is a leap year, otherwise FALSE
      */
-    public static function isLeapYear($year): bool
+    public static function isLeapYear(int|string $year): bool
     {
+        $year = (int) $year;
+
         return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
     }
 
     /**
      * getDateValue.
      *
-     * @param mixed $dateValue
-     *
      * @return float Excel date/time serial value
      */
-    public static function getDateValue($dateValue, bool $allowBool = true): float
+    public static function getDateValue(mixed $dateValue, bool $allowBool = true): float
     {
         if (is_object($dateValue)) {
             $retval = SharedDateHelper::PHPToExcel($dateValue);
@@ -60,14 +60,13 @@ class Helpers
     /**
      * getTimeValue.
      *
-     * @param string $timeValue
-     *
-     * @return mixed Excel date/time serial value, or string if error
+     * @return float|string Excel date/time serial value, or string if error
      */
-    public static function getTimeValue($timeValue)
+    public static function getTimeValue(string $timeValue): string|float
     {
         $saveReturnDateType = Functions::getReturnDateType();
         Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
+        /** @var float|string $timeValue */
         $timeValue = TimeValue::fromString($timeValue);
         Functions::setReturnDateType($saveReturnDateType);
 
@@ -76,10 +75,8 @@ class Helpers
 
     /**
      * Adjust date by given months.
-     *
-     * @param mixed $dateValue
      */
-    public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime
+    public static function adjustDateByMonths(mixed $dateValue = 0, float $adjustmentMonths = 0): DateTime
     {
         // Execute function
         $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
@@ -108,11 +105,8 @@ class Helpers
 
     /**
      * Help reduce perceived complexity of some tests.
-     *
-     * @param mixed $value
-     * @param mixed $altValue
      */
-    public static function replaceIfEmpty(&$value, $altValue): void
+    public static function replaceIfEmpty(mixed &$value, mixed $altValue): void
     {
         $value = $value ?: $altValue;
     }
@@ -133,10 +127,8 @@ class Helpers
 
     /**
      * Return result in one of three formats.
-     *
-     * @return mixed
      */
-    public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false)
+    public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false): DateTime|float|int
     {
         $retType = Functions::getReturnDateType();
         if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
@@ -149,8 +141,8 @@ class Helpers
                 . ':' . $dateArray['second']
             );
         }
-        $excelDateValue =
-            SharedDateHelper::formattedPHPToExcel(
+        $excelDateValue
+            = SharedDateHelper::formattedPHPToExcel(
                 $dateArray['year'],
                 $dateArray['month'],
                 $dateArray['day'],
@@ -159,26 +151,24 @@ class Helpers
                 $dateArray['second']
             );
         if ($retType === Functions::RETURNDATE_EXCEL) {
-            return $noFrac ? floor($excelDateValue) : (float) $excelDateValue;
+            return $noFrac ? floor($excelDateValue) : $excelDateValue;
         }
         // RETURNDATE_UNIX_TIMESTAMP)
 
-        return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
+        return SharedDateHelper::excelToTimestamp($excelDateValue);
     }
 
     /**
      * Return result in one of three formats.
-     *
-     * @return mixed
      */
-    public static function returnIn3FormatsFloat(float $excelDateValue)
+    public static function returnIn3FormatsFloat(float $excelDateValue): float|int|DateTime
     {
         $retType = Functions::getReturnDateType();
         if ($retType === Functions::RETURNDATE_EXCEL) {
             return $excelDateValue;
         }
         if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
-            return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
+            return SharedDateHelper::excelToTimestamp($excelDateValue);
         }
         // RETURNDATE_PHP_DATETIME_OBJECT
 
@@ -187,10 +177,8 @@ class Helpers
 
     /**
      * Return result in one of three formats.
-     *
-     * @return mixed
      */
-    public static function returnIn3FormatsObject(DateTime $PHPDateObject)
+    public static function returnIn3FormatsObject(DateTime $PHPDateObject): DateTime|float|int
     {
         $retType = Functions::getReturnDateType();
         if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
@@ -203,7 +191,7 @@ class Helpers
         $stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
         $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
 
-        return (int) SharedDateHelper::excelToTimestamp($stamp);
+        return SharedDateHelper::excelToTimestamp($stamp);
     }
 
     private static function baseDate(): int
@@ -220,10 +208,8 @@ class Helpers
 
     /**
      * Many functions accept null/false/true argument treated as 0/0/1.
-     *
-     * @param mixed $number
      */
-    public static function nullFalseTrueToNumber(&$number, bool $allowBool = true): void
+    public static function nullFalseTrueToNumber(mixed &$number, bool $allowBool = true): void
     {
         $number = Functions::flattenSingleValue($number);
         $nullVal = self::baseDate();
@@ -236,12 +222,8 @@ class Helpers
 
     /**
      * Many functions accept null argument treated as 0.
-     *
-     * @param mixed $number
-     *
-     * @return float|int
      */
-    public static function validateNumericNull($number)
+    public static function validateNumericNull(mixed $number): int|float
     {
         $number = Functions::flattenSingleValue($number);
         if ($number === null) {
@@ -260,11 +242,9 @@ class Helpers
     /**
      * Many functions accept null/false/true argument treated as 0/0/1.
      *
-     * @param mixed $number
-     *
-     * @return float
+     * @phpstan-assert float $number
      */
-    public static function validateNotNegative($number)
+    public static function validateNotNegative(mixed $number): float
     {
         if (!is_numeric($number)) {
             throw new Exception(ExcelError::VALUE());
@@ -300,7 +280,7 @@ class Helpers
      *
      * @param array|false $dateArray
      */
-    private static function forceArray($dateArray): array
+    private static function forceArray(array|bool $dateArray): array
     {
         return is_array($dateArray) ? $dateArray : ['error_count' => 1];
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
index c72d006..a90c051 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use DateTime;
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 
@@ -28,12 +29,12 @@ class Month
      *                                        a negative value yields a past date.
      *                         Or can be an array of adjustment values
      *
-     * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|DateTime|float|int|string 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)
+    public static function adjust(mixed $dateValue, array|string|bool|float|int $adjustmentMonths): DateTime|float|int|string|array
     {
         if (is_array($dateValue) || is_array($adjustmentMonths)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
@@ -45,6 +46,7 @@ class Month
         } catch (Exception $e) {
             return $e->getMessage();
         }
+        $dateValue = floor($dateValue);
         $adjustmentMonths = floor($adjustmentMonths);
 
         // Execute function
@@ -71,12 +73,12 @@ class Month
      *                                        a negative value yields a past date.
      *                         Or can be an array of adjustment values
      *
-     * @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|DateTime|float|int|string 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)
+    public static function lastDay(mixed $dateValue, array|float|int|bool|string $adjustmentMonths): array|string|DateTime|float|int
     {
         if (is_array($dateValue) || is_array($adjustmentMonths)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
@@ -88,6 +90,7 @@ class Month
         } catch (Exception $e) {
             return $e->getMessage();
         }
+        $dateValue = floor($dateValue);
         $adjustmentMonths = floor($adjustmentMonths);
 
         // Execute function
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
index 3b8942b..503e30e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php
@@ -33,7 +33,7 @@ class NetworkDays
      *         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)
+    public static function count(mixed $startDate, mixed $endDate, mixed ...$dateArgs): array|string|int
     {
         if (is_array($startDate) || is_array($endDate)) {
             return self::evaluateArrayArgumentsSubset(
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
index 4ff7198..3f8f324 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
@@ -24,26 +24,26 @@ class Time
      * Excel Function:
      *        TIME(hour,minute,second)
      *
-     * @param array|int $hour A number from 0 (zero) to 32767 representing the hour.
+     * @param null|array|bool|float|int|string $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 array|int $minute A number from 0 to 32767 representing the minute.
+     * @param null|array|bool|float|int|string $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 array|int $second A number from 0 to 32767 representing the second.
+     * @param null|array|bool|float|int|string $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 array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|DateTime|float|int|string 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)
+    public static function fromHMS(array|int|float|bool|null|string $hour, array|int|float|bool|null|string $minute, array|int|float|bool|null|string $second): array|string|float|int|DateTime
     {
         if (is_array($hour) || is_array($minute) || is_array($second)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $hour, $minute, $second);
@@ -115,7 +115,7 @@ class Time
     /**
      * @param mixed $value expect int
      */
-    private static function toIntWithNullBool($value): int
+    private static function toIntWithNullBool(mixed $value): int
     {
         $value = $value ?? 0;
         if (is_bool($value)) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
index d9b99f3..de52269 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
@@ -27,7 +27,7 @@ class TimeParts
      *         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)
+    public static function hour(mixed $timeValue): array|string|int
     {
         if (is_array($timeValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
@@ -46,6 +46,7 @@ class TimeParts
         // Execute function
         $timeValue = fmod($timeValue, 1);
         $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+        SharedDateHelper::roundMicroseconds($timeValue);
 
         return (int) $timeValue->format('H');
     }
@@ -67,7 +68,7 @@ class TimeParts
      *         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)
+    public static function minute(mixed $timeValue): array|string|int
     {
         if (is_array($timeValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
@@ -86,6 +87,7 @@ class TimeParts
         // Execute function
         $timeValue = fmod($timeValue, 1);
         $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+        SharedDateHelper::roundMicroseconds($timeValue);
 
         return (int) $timeValue->format('i');
     }
@@ -107,7 +109,7 @@ class TimeParts
      *         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)
+    public static function second(mixed $timeValue): array|string|int
     {
         if (is_array($timeValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
@@ -126,6 +128,7 @@ class TimeParts
         // Execute function
         $timeValue = fmod($timeValue, 1);
         $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
+        SharedDateHelper::roundMicroseconds($timeValue);
 
         return (int) $timeValue->format('s');
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
index 4abdd75..d8c53b4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
@@ -25,40 +25,42 @@ class TimeValue
      * Excel Function:
      *        TIMEVALUE(timeValue)
      *
-     * @param array|string $timeValue A text string that represents a time in any one of the Microsoft
+     * @param null|array|bool|float|int|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,
+     * @return array|Datetime|float|int|string 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)
+    public static function fromString(null|array|string|int|bool|float $timeValue): array|string|Datetime|int|float
     {
         if (is_array($timeValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
         }
 
-        $timeValue = trim($timeValue ?? '', '"');
+        // try to parse as time iff there is at least one digit
+        if (is_string($timeValue) && preg_match('/\\d/', $timeValue) !== 1) {
+            return ExcelError::VALUE();
+        }
+
+        $timeValue = trim((string) $timeValue, '"');
         $timeValue = str_replace(['/', '.'], '-', $timeValue);
 
         $arraySplit = preg_split('/[\/:\-\s]/', $timeValue) ?: [];
         if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
-            $arraySplit[0] = ($arraySplit[0] % 24);
+            $arraySplit[0] = ((int) $arraySplit[0] % 24);
             $timeValue = implode(':', $arraySplit);
         }
 
         $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, $hour, $minute, $second) - 1;
@@ -69,7 +71,7 @@ class TimeValue
             } elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
                 $retValue = (int) SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
             } else {
-                $retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
+                $retValue = new Datetime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
             }
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
index 2f69007..e620b4c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
@@ -45,7 +45,7 @@ class Week
      *         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)
+    public static function number(mixed $dateValue, array|int|string|null $method = Constants::STARTWEEK_SUNDAY): array|int|string
     {
         if (is_array($dateValue) || is_array($method)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $method);
@@ -105,7 +105,7 @@ class Week
      *         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)
+    public static function isoWeekNumber(mixed $dateValue): array|int|string
     {
         if (is_array($dateValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
@@ -137,7 +137,7 @@ class Week
      * Excel Function:
      *        WEEKDAY(dateValue[,style])
      *
-     * @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
+     * @param null|array|bool|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
@@ -150,7 +150,7 @@ class Week
      *         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)
+    public static function day(null|array|float|int|string|bool $dateValue, mixed $style = 1): array|string|int
     {
         if (is_array($dateValue) || is_array($style)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
@@ -189,7 +189,7 @@ class Week
     /**
      * @param mixed $style expect int
      */
-    private static function validateStyle($style): int
+    private static function validateStyle(mixed $style): int
     {
         if (!is_numeric($style)) {
             throw new Exception(ExcelError::VALUE());
@@ -211,7 +211,7 @@ class Week
      * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
      *                                    PHP DateTime object, or a standard date string
      */
-    private static function apparentBug($dateValue): bool
+    private static function apparentBug(mixed $dateValue): bool
     {
         if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
             if (is_bool($dateValue)) {
@@ -227,10 +227,8 @@ class Week
 
     /**
      * Validate dateValue parameter.
-     *
-     * @param mixed $dateValue
      */
-    private static function validateDateValue($dateValue): float
+    private static function validateDateValue(mixed $dateValue): float
     {
         if (is_bool($dateValue)) {
             throw new Exception(ExcelError::VALUE());
@@ -241,10 +239,8 @@ class Week
 
     /**
      * Validate method parameter.
-     *
-     * @param mixed $method
      */
-    private static function validateMethod($method): int
+    private static function validateMethod(mixed $method): int
     {
         if ($method === null) {
             $method = Constants::STARTWEEK_SUNDAY;
@@ -272,7 +268,7 @@ class Week
     {
         // This appears to be another Excel bug.
 
-        return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
-            !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
+        return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904
+            && !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
index 1f5735e..4e4ed3c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 
+use DateTime;
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
@@ -30,12 +31,12 @@ class WorkDay
      *                         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 array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+     * @return array|DateTime|float|int|string 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)
+    public static function date(mixed $startDate, array|int|string $endDays, mixed ...$dateArgs): array|float|int|DateTime|string
     {
         if (is_array($startDate) || is_array($endDays)) {
             return self::evaluateArrayArgumentsSubset(
@@ -71,10 +72,8 @@ class WorkDay
 
     /**
      * Use incrementing logic to determine Workday.
-     *
-     * @return mixed
      */
-    private static function incrementing(float $startDate, int $endDays, array $holidayArray)
+    private static function incrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
     {
         //    Adjust the start date if it falls over a weekend
         $startDoW = self::getWeekDay($startDate, 3);
@@ -132,10 +131,8 @@ class WorkDay
 
     /**
      * Use decrementing logic to determine Workday.
-     *
-     * @return mixed
      */
-    private static function decrementing(float $startDate, int $endDays, array $holidayArray)
+    private static function decrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime
     {
         //    Adjust the start date if it falls over a weekend
         $startDoW = self::getWeekDay($startDate, 3);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
index 394c6b7..2713754 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
@@ -39,11 +39,11 @@ class YearFrac
      *                                        4                European 30/360
      *                         Or can be an array of methods
      *
-     * @return array|float|string fraction of the year, or a string containing an error
+     * @return array|float|int|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)
+    public static function fraction(mixed $startDate, mixed $endDate, array|int|string|null $method = 0): array|string|int|float
     {
         if (is_array($startDate) || is_array($endDate) || is_array($method)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $method);
@@ -61,29 +61,20 @@ class YearFrac
             return $e->getMessage();
         }
 
-        switch ($method) {
-            case 0:
-                return Functions::scalar(Days360::between($startDate, $endDate)) / 360;
-            case 1:
-                return self::method1($startDate, $endDate);
-            case 2:
-                return Functions::scalar(Difference::interval($startDate, $endDate)) / 360;
-            case 3:
-                return Functions::scalar(Difference::interval($startDate, $endDate)) / 365;
-            case 4:
-                return Functions::scalar(Days360::between($startDate, $endDate, true)) / 360;
-        }
-
-        return ExcelError::NAN();
+        return match ($method) {
+            0 => Functions::scalar(Days360::between($startDate, $endDate)) / 360,
+            1 => self::method1($startDate, $endDate),
+            2 => Functions::scalar(Difference::interval($startDate, $endDate)) / 360,
+            3 => Functions::scalar(Difference::interval($startDate, $endDate)) / 365,
+            4 => Functions::scalar(Days360::between($startDate, $endDate, true)) / 360,
+            default => ExcelError::NAN(),
+        };
     }
 
     /**
      * Excel 1900 calendar treats date argument of null as 1900-01-00. Really.
-     *
-     * @param mixed $startDate
-     * @param mixed $endDate
      */
-    private static function excelBug(float $sDate, $startDate, $endDate, int $method): float
+    private static function excelBug(float $sDate, mixed $startDate, mixed $endDate, int $method): float
     {
         if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
             if ($endDate === null && $startDate !== null) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
index fae9d90..0107f40 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php
@@ -6,30 +6,15 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 
 class ArrayArgumentHelper
 {
-    /**
-     * @var int
-     */
-    protected $indexStart = 0;
+    protected int $indexStart = 0;
 
-    /**
-     * @var array
-     */
-    protected $arguments;
+    protected array $arguments;
 
-    /**
-     * @var int
-     */
-    protected $argumentCount;
+    protected int $argumentCount;
 
-    /**
-     * @var array
-     */
-    protected $rows;
+    protected array $rows;
 
-    /**
-     * @var array
-     */
-    protected $columns;
+    protected array $columns;
 
     public function initialise(array $arguments): void
     {
@@ -152,9 +137,7 @@ class ArrayArgumentHelper
     private function rows(array $arguments): array
     {
         return array_map(
-            function ($argument) {
-                return is_countable($argument) ? count($argument) : 1;
-            },
+            fn ($argument): int => is_countable($argument) ? count($argument) : 1,
             $arguments
         );
     }
@@ -162,7 +145,7 @@ class ArrayArgumentHelper
     private function columns(array $arguments): array
     {
         return array_map(
-            function ($argument) {
+            function (mixed $argument): int {
                 return is_array($argument) && is_array($argument[array_keys($argument)[0]])
                     ? count($argument[array_keys($argument)[0]])
                     : 1;
@@ -201,9 +184,7 @@ class ArrayArgumentHelper
     {
         return array_filter(
             $array,
-            function ($value) {
-                return $value > 1;
-            }
+            fn ($value): bool => $value > 1
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
index 3e69d77..fb2c853 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php
@@ -6,18 +6,12 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 
 class ArrayArgumentProcessor
 {
-    /**
-     * @var ArrayArgumentHelper
-     */
-    private static $arrayArgumentHelper;
+    private static ArrayArgumentHelper $arrayArgumentHelper;
 
-    /**
-     * @param mixed ...$arguments
-     */
     public static function processArguments(
         ArrayArgumentHelper $arrayArgumentHelper,
         callable $method,
-        ...$arguments
+        mixed ...$arguments
     ): array {
         self::$arrayArgumentHelper = $arrayArgumentHelper;
 
@@ -42,14 +36,15 @@ class ArrayArgumentProcessor
         $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)
+                (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);
         }
@@ -59,10 +54,7 @@ class ArrayArgumentProcessor
         return ['#VALUE!'];
     }
 
-    /**
-     * @param mixed ...$arguments
-     */
-    private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
+    private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
     {
         $matrix2 = array_pop($matrixIndexes);
         /** @var array $matrixValues2 */
@@ -100,10 +92,7 @@ class ArrayArgumentProcessor
         return $result;
     }
 
-    /**
-     * @param mixed ...$arguments
-     */
-    private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
+    private static function evaluateMatrixPair(callable $method, array $matrixIndexes, mixed ...$arguments): array
     {
         $matrix2 = array_pop($matrixIndexes);
         /** @var array $matrixValues2 */
@@ -130,10 +119,7 @@ class ArrayArgumentProcessor
         return $result;
     }
 
-    /**
-     * @param mixed ...$arguments
-     */
-    private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array
+    private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, mixed ...$arguments): array
     {
         $rowVector = Functions::flattenArray($arguments[$rowIndex]);
         $columnVector = Functions::flattenArray($arguments[$columnIndex]);
@@ -155,10 +141,8 @@ class ArrayArgumentProcessor
 
     /**
      * 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
+    private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, mixed ...$arguments): array
     {
         $values = array_slice($arguments, $nthArgument - 1, 1);
         /** @var array $values */
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
index 9cd767e..e6dbbcb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php
@@ -6,69 +6,50 @@ use PhpOffice\PhpSpreadsheet\Calculation\Exception;
 
 class BranchPruner
 {
-    /**
-     * @var bool
-     */
-    protected $branchPruningEnabled = true;
+    protected bool $branchPruningEnabled;
 
     /**
      * Used to generate unique store keys.
-     *
-     * @var int
      */
-    private $branchStoreKeyCounter = 0;
+    private int $branchStoreKeyCounter = 0;
 
     /**
      * currently pending storeKey (last item of the storeKeysStack.
-     *
-     * @var ?string
      */
-    protected $pendingStoreKey;
+    protected ?string $pendingStoreKey = null;
 
     /**
      * @var string[]
      */
-    protected $storeKeysStack = [];
+    protected array $storeKeysStack = [];
 
     /**
      * @var bool[]
      */
-    protected $conditionMap = [];
+    protected array $conditionMap = [];
 
     /**
      * @var bool[]
      */
-    protected $thenMap = [];
+    protected array $thenMap = [];
 
     /**
      * @var bool[]
      */
-    protected $elseMap = [];
+    protected array $elseMap = [];
 
     /**
      * @var int[]
      */
-    protected $braceDepthMap = [];
+    protected array $braceDepthMap = [];
 
-    /**
-     * @var null|string
-     */
-    protected $currentCondition;
+    protected ?string $currentCondition = null;
 
-    /**
-     * @var null|string
-     */
-    protected $currentOnlyIf;
+    protected ?string $currentOnlyIf = null;
 
-    /**
-     * @var null|string
-     */
-    protected $currentOnlyIfNot;
+    protected ?string $currentOnlyIfNot = null;
 
-    /**
-     * @var null|string
-     */
-    protected $previousStoreKey;
+    protected ?string $previousStoreKey = null;
 
     public function __construct(bool $branchPruningEnabled)
     {
@@ -175,10 +156,7 @@ class BranchPruner
         }
     }
 
-    /**
-     * @param mixed $value
-     */
-    public function closingBrace($value): void
+    public function closingBrace(mixed $value): void
     {
         if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {
             // we are closing an IF(
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
index b688e05..f4806b4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php
@@ -9,34 +9,28 @@ class CyclicReferenceStack
      *
      * @var mixed[]
      */
-    private $stack = [];
+    private array $stack = [];
 
     /**
      * Return the number of entries on the stack.
-     *
-     * @return int
      */
-    public function count()
+    public function count(): int
     {
         return count($this->stack);
     }
 
     /**
      * Push a new entry onto the stack.
-     *
-     * @param mixed $value
      */
-    public function push($value): void
+    public function push(mixed $value): void
     {
         $this->stack[$value] = $value;
     }
 
     /**
      * Pop the last entry from the stack.
-     *
-     * @return mixed
      */
-    public function pop()
+    public function pop(): mixed
     {
         return array_pop($this->stack);
     }
@@ -45,10 +39,8 @@ class CyclicReferenceStack
      * Test to see if a specified entry exists on the stack.
      *
      * @param mixed $value The value to test
-     *
-     * @return bool
      */
-    public function onStack($value)
+    public function onStack(mixed $value): bool
     {
         return isset($this->stack[$value]);
     }
@@ -66,7 +58,7 @@ class CyclicReferenceStack
      *
      * @return mixed[]
      */
-    public function showStack()
+    public function showStack(): array
     {
         return $this->stack;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
index 8fe7aa4..331fa44 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php
@@ -13,6 +13,9 @@ class FormattedNumber
 
     private const STRING_REGEXP_PERCENT = '~^(?:(?: *(?<PrefixedSign>[-+])? *\% *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *\% *))$~i';
 
+    // preg_quoted string for major currency symbols, with a %s for locale currency
+    private const CURRENCY_CONVERSION_LIST = '\$€£¥%s';
+
     private const STRING_CONVERSION_LIST = [
         [self::class, 'convertToNumberIfNumeric'],
         [self::class, 'convertToNumberIfFraction'],
@@ -45,7 +48,10 @@ class FormattedNumber
      */
     public static function convertToNumberIfNumeric(string &$operand): bool
     {
-        $value = preg_replace(['/(\d),(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
+        $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+        $value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
+        $decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
+        $value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
 
         if (is_numeric($value)) {
             $operand = (float) $value;
@@ -84,7 +90,10 @@ class FormattedNumber
      */
     public static function convertToNumberIfPercent(string &$operand): bool
     {
-        $value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
+        $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+        $value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim($operand));
+        $decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
+        $value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
 
         $match = [];
         if ($value !== null && preg_match(self::STRING_REGEXP_PERCENT, $value, $match, PREG_UNMATCHED_AS_NULL)) {
@@ -106,21 +115,33 @@ class FormattedNumber
      */
     public static function convertToNumberIfCurrency(string &$operand): bool
     {
-        $quotedCurrencyCode = preg_quote(StringHelper::getCurrencyCode());
-
-        $value = preg_replace('/(\d),(\d)/u', '$1$2', $operand);
-        $regExp = '~^(?:(?: *(?<PrefixedSign>[-+])? *' . $quotedCurrencyCode . ' *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *' . $quotedCurrencyCode . ' *))$~ui';
+        $currencyRegexp = self::currencyMatcherRegexp();
+        $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+        $value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $operand);
 
         $match = [];
-        if ($value !== null && preg_match($regExp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
+        if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
             //Determine the sign
             $sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
+            $decimalSeparator = StringHelper::getDecimalSeparator();
             //Cast to a float
-            $operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue']));
+            $intermediate = (string) ($match['PostfixedValue'] ?? $match['PrefixedValue']);
+            $intermediate = str_replace($decimalSeparator, '.', $intermediate);
+            if (is_numeric($intermediate)) {
+                $operand = (float) ($sign . str_replace($decimalSeparator, '.', $intermediate));
 
-            return true;
+                return true;
+            }
         }
 
         return false;
     }
+
+    public static function currencyMatcherRegexp(): string
+    {
+        $currencyCodes = sprintf(self::CURRENCY_CONVERSION_LIST, preg_quote(StringHelper::getCurrencyCode(), '/'));
+        $decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
+
+        return '~^(?:(?: *(?<PrefixedSign>[-+])? *(?<PrefixedCurrency>[' . $currencyCodes . ']) *(?<PrefixedSign2>[-+])? *(?<PrefixedValue>[0-9]+[' . $decimalSeparator . ']?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?<PostfixedSign>[-+])? *(?<PostfixedValue>[0-9]+' . $decimalSeparator . '?[0-9]*(?:E[-+]?[0-9]*)?) *(?<PostfixedCurrency>[' . $currencyCodes . ']) *))$~ui';
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
index 256c3ef..9adcd55 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php
@@ -8,34 +8,28 @@ class Logger
      * Flag to determine whether a debug log should be generated by the calculation engine
      *        If true, then a debug log will be generated
      *        If false, then a debug log will not be generated.
-     *
-     * @var bool
      */
-    private $writeDebugLog = false;
+    private bool $writeDebugLog = false;
 
     /**
      * Flag to determine whether a debug log should be echoed by the calculation engine
      *        If true, then a debug log will be echoed
      *        If false, then a debug log will not be echoed
      * A debug log can only be echoed if it is generated.
-     *
-     * @var bool
      */
-    private $echoDebugLog = false;
+    private bool $echoDebugLog = false;
 
     /**
      * The debug log generated by the calculation engine.
      *
      * @var string[]
      */
-    private $debugLog = [];
+    private array $debugLog = [];
 
     /**
      * The calculation engine cell reference stack.
-     *
-     * @var CyclicReferenceStack
      */
-    private $cellStack;
+    private CyclicReferenceStack $cellStack;
 
     /**
      * Instantiate a Calculation engine logger.
@@ -47,50 +41,40 @@ class Logger
 
     /**
      * Enable/Disable Calculation engine logging.
-     *
-     * @param bool $writeDebugLog
      */
-    public function setWriteDebugLog($writeDebugLog): void
+    public function setWriteDebugLog(bool $writeDebugLog): void
     {
         $this->writeDebugLog = $writeDebugLog;
     }
 
     /**
      * Return whether calculation engine logging is enabled or disabled.
-     *
-     * @return bool
      */
-    public function getWriteDebugLog()
+    public function getWriteDebugLog(): bool
     {
         return $this->writeDebugLog;
     }
 
     /**
      * Enable/Disable echoing of debug log information.
-     *
-     * @param bool $echoDebugLog
      */
-    public function setEchoDebugLog($echoDebugLog): void
+    public function setEchoDebugLog(bool $echoDebugLog): void
     {
         $this->echoDebugLog = $echoDebugLog;
     }
 
     /**
      * Return whether echoing of debug log information is enabled or disabled.
-     *
-     * @return bool
      */
-    public function getEchoDebugLog()
+    public function getEchoDebugLog(): bool
     {
         return $this->echoDebugLog;
     }
 
     /**
      * Write an entry to the calculation engine debug log.
-     *
-     * @param mixed $args
      */
-    public function writeDebugLog(string $message, ...$args): void
+    public function writeDebugLog(string $message, mixed ...$args): void
     {
         //    Only write the debug log if logging is enabled
         if ($this->writeDebugLog) {
@@ -102,9 +86,9 @@ class Logger
                 $message,
                 PHP_EOL;
             }
-            $this->debugLog[] = $cellReference .
-                ($this->cellStack->count() > 0 ? ' => ' : '') .
-                $message;
+            $this->debugLog[] = $cellReference
+                . ($this->cellStack->count() > 0 ? ' => ' : '')
+                . $message;
         }
     }
 
@@ -135,7 +119,7 @@ class Logger
      *
      * @return string[]
      */
-    public function getLog()
+    public function getLog(): array
     {
         return $this->debugLog;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/StructuredReference.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/StructuredReference.php
index 4a4d1b8..fc2b5ea 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/StructuredReference.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Operands/StructuredReference.php
@@ -2,17 +2,53 @@
 
 namespace PhpOffice\PhpSpreadsheet\Calculation\Engine\Operands;
 
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception;
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Table;
+use Stringable;
 
-final class StructuredReference implements Operand
+final class StructuredReference implements Operand, Stringable
 {
     public const NAME = 'Structured Reference';
 
     private const OPEN_BRACE = '[';
     private const CLOSE_BRACE = ']';
 
+    private const ITEM_SPECIFIER_ALL = '#All';
+    private const ITEM_SPECIFIER_HEADERS = '#Headers';
+    private const ITEM_SPECIFIER_DATA = '#Data';
+    private const ITEM_SPECIFIER_TOTALS = '#Totals';
+    private const ITEM_SPECIFIER_THIS_ROW = '#This Row';
+
+    private const ITEM_SPECIFIER_ROWS_SET = [
+        self::ITEM_SPECIFIER_ALL,
+        self::ITEM_SPECIFIER_HEADERS,
+        self::ITEM_SPECIFIER_DATA,
+        self::ITEM_SPECIFIER_TOTALS,
+    ];
+
+    private const TABLE_REFERENCE = '/([\p{L}_\\\\][\p{L}\p{N}\._]+)?(\[(?:[^\]\[]+|(?R))*+\])/miu';
+
     private string $value;
 
+    private string $tableName;
+
+    private Table $table;
+
+    private string $reference;
+
+    private ?int $headersRow;
+
+    private int $firstDataRow;
+
+    private int $lastDataRow;
+
+    private ?int $totalsRow;
+
+    private array $columns;
+
     public function __construct(string $structuredReference)
     {
         $this->value = $structuredReference;
@@ -33,7 +69,7 @@ final class StructuredReference implements Operand
             }
             $srStringRemainder = substr($srStringRemainder, 0, $closingPos + 1);
             --$srCount;
-            if (strpos($srStringRemainder, self::OPEN_BRACE) !== false) {
+            if (str_contains($srStringRemainder, self::OPEN_BRACE)) {
                 ++$srCount;
             }
             $val .= $srStringRemainder;
@@ -42,8 +78,278 @@ final class StructuredReference implements Operand
         return new self($val);
     }
 
+    /**
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     */
+    public function parse(Cell $cell): string
+    {
+        $this->getTableStructure($cell);
+        $cellRange = ($this->isRowReference()) ? $this->getRowReference($cell) : $this->getColumnReference();
+        $sheetName = '';
+        $worksheet = $this->table->getWorksheet();
+        if ($worksheet !== null && $worksheet !== $cell->getWorksheet()) {
+            $sheetName = "'" . $worksheet->getTitle() . "'!";
+        }
+
+        return $sheetName . $cellRange;
+    }
+
+    private function isRowReference(): bool
+    {
+        return str_contains($this->value, '[@')
+            || str_contains($this->value, '[' . self::ITEM_SPECIFIER_THIS_ROW . ']');
+    }
+
+    /**
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     */
+    private function getTableStructure(Cell $cell): void
+    {
+        preg_match(self::TABLE_REFERENCE, $this->value, $matches);
+
+        $this->tableName = $matches[1];
+        $this->table = ($this->tableName === '')
+            ? $this->getTableForCell($cell)
+            : $this->getTableByName($cell);
+        $this->reference = $matches[2];
+        $tableRange = Coordinate::getRangeBoundaries($this->table->getRange());
+
+        $this->headersRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] : null;
+        $this->firstDataRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] + 1 : $tableRange[0][1];
+        $this->totalsRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] : null;
+        $this->lastDataRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] - 1 : $tableRange[1][1];
+
+        $cellParam = $cell;
+        $worksheet = $this->table->getWorksheet();
+        if ($worksheet !== null && $worksheet !== $cell->getWorksheet()) {
+            $cellParam = $worksheet->getCell('A1');
+        }
+        $this->columns = $this->getColumns($cellParam, $tableRange);
+    }
+
+    /**
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     */
+    private function getTableForCell(Cell $cell): Table
+    {
+        $tables = $cell->getWorksheet()->getTableCollection();
+        foreach ($tables as $table) {
+            /** @var Table $table */
+            $range = $table->getRange();
+            if ($cell->isInRange($range) === true) {
+                $this->tableName = $table->getName();
+
+                return $table;
+            }
+        }
+
+        throw new Exception('Table for Structured Reference cannot be identified');
+    }
+
+    /**
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     */
+    private function getTableByName(Cell $cell): Table
+    {
+        $table = $cell->getWorksheet()->getTableByName($this->tableName);
+
+        if ($table === null) {
+            $spreadsheet = $cell->getWorksheet()->getParent();
+            if ($spreadsheet !== null) {
+                $table = $spreadsheet->getTableByName($this->tableName);
+            }
+        }
+
+        if ($table === null) {
+            throw new Exception("Table {$this->tableName} for Structured Reference cannot be located");
+        }
+
+        return $table;
+    }
+
+    private function getColumns(Cell $cell, array $tableRange): array
+    {
+        $worksheet = $cell->getWorksheet();
+        $cellReference = $cell->getCoordinate();
+
+        $columns = [];
+        $lastColumn = ++$tableRange[1][0];
+        for ($column = $tableRange[0][0]; $column !== $lastColumn; ++$column) {
+            $columns[$column] = $worksheet
+                ->getCell($column . ($this->headersRow ?? ($this->firstDataRow - 1)))
+                ->getCalculatedValue();
+        }
+
+        $worksheet->getCell($cellReference);
+
+        return $columns;
+    }
+
+    private function getRowReference(Cell $cell): string
+    {
+        $reference = str_replace("\u{a0}", ' ', $this->reference);
+        /** @var string $reference */
+        $reference = str_replace('[' . self::ITEM_SPECIFIER_THIS_ROW . '],', '', $reference);
+
+        foreach ($this->columns as $columnId => $columnName) {
+            $columnName = str_replace("\u{a0}", ' ', $columnName);
+            $reference = $this->adjustRowReference($columnName, $reference, $cell, $columnId);
+        }
+
+        return $this->validateParsedReference(trim($reference, '[]@, '));
+    }
+
+    private function adjustRowReference(string $columnName, string $reference, Cell $cell, string $columnId): string
+    {
+        if ($columnName !== '') {
+            $cellReference = $columnId . $cell->getRow();
+            $pattern1 = '/\[' . preg_quote($columnName, '/') . '\]/miu';
+            $pattern2 = '/@' . preg_quote($columnName, '/') . '/miu';
+            if (preg_match($pattern1, $reference) === 1) {
+                $reference = preg_replace($pattern1, $cellReference, $reference);
+            } elseif (preg_match($pattern2, $reference) === 1) {
+                $reference = preg_replace($pattern2, $cellReference, $reference);
+            }
+            /** @var string $reference */
+        }
+
+        return $reference;
+    }
+
+    /**
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     */
+    private function getColumnReference(): string
+    {
+        $reference = str_replace("\u{a0}", ' ', $this->reference);
+        $startRow = ($this->totalsRow === null) ? $this->lastDataRow : $this->totalsRow;
+        $endRow = ($this->headersRow === null) ? $this->firstDataRow : $this->headersRow;
+
+        [$startRow, $endRow] = $this->getRowsForColumnReference($reference, $startRow, $endRow);
+        $reference = $this->getColumnsForColumnReference($reference, $startRow, $endRow);
+
+        $reference = trim($reference, '[]@, ');
+        if (substr_count($reference, ':') > 1) {
+            $cells = explode(':', $reference);
+            $firstCell = array_shift($cells);
+            $lastCell = array_pop($cells);
+            $reference = "{$firstCell}:{$lastCell}";
+        }
+
+        return $this->validateParsedReference($reference);
+    }
+
+    /**
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     */
+    private function validateParsedReference(string $reference): string
+    {
+        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . ':' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
+            if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
+                throw new Exception(
+                    "Invalid Structured Reference {$this->reference} {$reference}",
+                    Exception::CALCULATION_ENGINE_PUSH_TO_STACK
+                );
+            }
+        }
+
+        return $reference;
+    }
+
+    private function fullData(int $startRow, int $endRow): string
+    {
+        $columns = array_keys($this->columns);
+        $firstColumn = array_shift($columns);
+        $lastColumn = (empty($columns)) ? $firstColumn : array_pop($columns);
+
+        return "{$firstColumn}{$startRow}:{$lastColumn}{$endRow}";
+    }
+
+    private function getMinimumRow(string $reference): int
+    {
+        return match ($reference) {
+            self::ITEM_SPECIFIER_ALL, self::ITEM_SPECIFIER_HEADERS => $this->headersRow ?? $this->firstDataRow,
+            self::ITEM_SPECIFIER_DATA => $this->firstDataRow,
+            self::ITEM_SPECIFIER_TOTALS => $this->totalsRow ?? $this->lastDataRow,
+            default => $this->headersRow ?? $this->firstDataRow,
+        };
+    }
+
+    private function getMaximumRow(string $reference): int
+    {
+        return match ($reference) {
+            self::ITEM_SPECIFIER_HEADERS => $this->headersRow ?? $this->firstDataRow,
+            self::ITEM_SPECIFIER_DATA => $this->lastDataRow,
+            self::ITEM_SPECIFIER_ALL, self::ITEM_SPECIFIER_TOTALS => $this->totalsRow ?? $this->lastDataRow,
+            default => $this->totalsRow ?? $this->lastDataRow,
+        };
+    }
+
     public function value(): string
     {
         return $this->value;
     }
+
+    /**
+     * @return array<int, int>
+     */
+    private function getRowsForColumnReference(string &$reference, int $startRow, int $endRow): array
+    {
+        $rowsSelected = false;
+        foreach (self::ITEM_SPECIFIER_ROWS_SET as $rowReference) {
+            $pattern = '/\[' . $rowReference . '\]/mui';
+            if (preg_match($pattern, $reference) === 1) {
+                if (($rowReference === self::ITEM_SPECIFIER_HEADERS) && ($this->table->getShowHeaderRow() === false)) {
+                    throw new Exception(
+                        'Table Headers are Hidden, and should not be Referenced',
+                        Exception::CALCULATION_ENGINE_PUSH_TO_STACK
+                    );
+                }
+                $rowsSelected = true;
+                $startRow = min($startRow, $this->getMinimumRow($rowReference));
+                $endRow = max($endRow, $this->getMaximumRow($rowReference));
+                $reference = preg_replace($pattern, '', $reference) ?? '';
+            }
+        }
+        if ($rowsSelected === false) {
+            // If there isn't any Special Item Identifier specified, then the selection defaults to data rows only.
+            $startRow = $this->firstDataRow;
+            $endRow = $this->lastDataRow;
+        }
+
+        return [$startRow, $endRow];
+    }
+
+    private function getColumnsForColumnReference(string $reference, int $startRow, int $endRow): string
+    {
+        $columnsSelected = false;
+        foreach ($this->columns as $columnId => $columnName) {
+            $columnName = str_replace("\u{a0}", ' ', $columnName ?? '');
+            $cellFrom = "{$columnId}{$startRow}";
+            $cellTo = "{$columnId}{$endRow}";
+            $cellReference = ($cellFrom === $cellTo) ? $cellFrom : "{$cellFrom}:{$cellTo}";
+            $pattern = '/\[' . preg_quote($columnName, '/') . '\]/mui';
+            if (preg_match($pattern, $reference) === 1) {
+                $columnsSelected = true;
+                $reference = preg_replace($pattern, $cellReference, $reference);
+            }
+            /** @var string $reference */
+        }
+        if ($columnsSelected === false) {
+            return $this->fullData($startRow, $endRow);
+        }
+
+        return $reference;
+    }
+
+    public function __toString(): string
+    {
+        return $this->value;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
deleted file mode 100644
index e3a2bd6..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering.php
+++ /dev/null
@@ -1,1447 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-use Complex\Complex;
-use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ComplexFunctions;
-use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ComplexOperations;
-
-/**
- * @deprecated 1.18.0
- */
-class Engineering
-{
-    /**
-     * EULER.
-     *
-     * @deprecated 1.18.0
-     *      Use Engineering\Constants::EULER instead
-     * @see Engineering\Constants::EULER
-     */
-    public const EULER = 2.71828182845904523536;
-
-    /**
-     * parseComplex.
-     *
-     * Parses a complex number into its real and imaginary parts, and an I or J suffix
-     *
-     * @deprecated 1.12.0 No longer used by internal code. Please use the \Complex\Complex class instead
-     *
-     * @param string $complexNumber The complex number
-     *
-     * @return mixed[] Indexed on "real", "imaginary" and "suffix"
-     */
-    public static function parseComplex($complexNumber)
-    {
-        $complex = new Complex($complexNumber);
-
-        return [
-            'real' => $complex->getReal(),
-            'imaginary' => $complex->getImaginary(),
-            'suffix' => $complex->getSuffix(),
-        ];
-    }
-
-    /**
-     * BESSELI.
-     *
-     *    Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
-     *        for purely imaginary arguments
-     *
-     *    Excel Function:
-     *        BESSELI(x,ord)
-     *
-     * @deprecated 1.17.0
-     *      Use the BESSELI() method in the Engineering\BesselI class instead
-     * @see Engineering\BesselI::BESSELI()
-     *
-     * @param float $x The value at which to evaluate the function.
-     *                                If x is nonnumeric, BESSELI returns the #VALUE! error value.
-     * @param int $ord The 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.
-     *
-     * @return array|float|string Result, or a string containing an error
-     */
-    public static function BESSELI($x, $ord)
-    {
-        return Engineering\BesselI::BESSELI($x, $ord);
-    }
-
-    /**
-     * BESSELJ.
-     *
-     *    Returns the Bessel function
-     *
-     *    Excel Function:
-     *        BESSELJ(x,ord)
-     *
-     * @deprecated 1.17.0
-     *      Use the BESSELJ() method in the Engineering\BesselJ class instead
-     * @see Engineering\BesselJ::BESSELJ()
-     *
-     * @param float $x The value at which to evaluate the function.
-     *                                If x is nonnumeric, BESSELJ returns the #VALUE! error value.
-     * @param int $ord The order of the Bessel function. If n 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.
-     *
-     * @return array|float|string Result, or a string containing an error
-     */
-    public static function BESSELJ($x, $ord)
-    {
-        return Engineering\BesselJ::BESSELJ($x, $ord);
-    }
-
-    /**
-     * BESSELK.
-     *
-     *    Returns the modified Bessel function Kn(x), which is equivalent to the Bessel functions evaluated
-     *        for purely imaginary arguments.
-     *
-     *    Excel Function:
-     *        BESSELK(x,ord)
-     *
-     * @deprecated 1.17.0
-     *      Use the BESSELK() method in the Engineering\BesselK class instead
-     * @see Engineering\BesselK::BESSELK()
-     *
-     * @param float $x The value at which to evaluate the function.
-     *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
-     * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
-     *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
-     *                                If $ord < 0, BESSELK returns the #NUM! error value.
-     *
-     * @return array|float|string Result, or a string containing an error
-     */
-    public static function BESSELK($x, $ord)
-    {
-        return Engineering\BesselK::BESSELK($x, $ord);
-    }
-
-    /**
-     * BESSELY.
-     *
-     * Returns the Bessel function, which is also called the Weber function or the Neumann function.
-     *
-     *    Excel Function:
-     *        BESSELY(x,ord)
-     *
-     * @deprecated 1.17.0
-     *      Use the BESSELY() method in the Engineering\BesselY class instead
-     * @see Engineering\BesselY::BESSELY()
-     *
-     * @param float $x The value at which to evaluate the function.
-     *                                If x is nonnumeric, BESSELY returns the #VALUE! error value.
-     * @param int $ord The order of the Bessel function. If n 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.
-     *
-     * @return array|float|string Result, or a string containing an error
-     */
-    public static function BESSELY($x, $ord)
-    {
-        return Engineering\BesselY::BESSELY($x, $ord);
-    }
-
-    /**
-     * BINTODEC.
-     *
-     * Return a binary value as decimal.
-     *
-     * Excel Function:
-     *        BIN2DEC(x)
-     *
-     * @deprecated 1.17.0
-     *      Use the toDecimal() method in the Engineering\ConvertBinary class instead
-     * @see Engineering\ConvertBinary::toDecimal()
-     *
-     * @param mixed $x 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.
-     *
-     * @return array|string
-     */
-    public static function BINTODEC($x)
-    {
-        return Engineering\ConvertBinary::toDecimal($x);
-    }
-
-    /**
-     * BINTOHEX.
-     *
-     * Return a binary value as hex.
-     *
-     * Excel Function:
-     *        BIN2HEX(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toHex() method in the Engineering\ConvertBinary class instead
-     * @see Engineering\ConvertBinary::toHex()
-     *
-     * @param mixed $x 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 mixed $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.
-     *
-     * @return array|string
-     */
-    public static function BINTOHEX($x, $places = null)
-    {
-        return Engineering\ConvertBinary::toHex($x, $places);
-    }
-
-    /**
-     * BINTOOCT.
-     *
-     * Return a binary value as octal.
-     *
-     * Excel Function:
-     *        BIN2OCT(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toOctal() method in the Engineering\ConvertBinary class instead
-     * @see Engineering\ConvertBinary::toOctal()
-     *
-     * @param mixed $x 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 mixed $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.
-     *
-     * @return array|string
-     */
-    public static function BINTOOCT($x, $places = null)
-    {
-        return Engineering\ConvertBinary::toOctal($x, $places);
-    }
-
-    /**
-     * DECTOBIN.
-     *
-     * Return a decimal value as binary.
-     *
-     * Excel Function:
-     *        DEC2BIN(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toBinary() method in the Engineering\ConvertDecimal class instead
-     * @see Engineering\ConvertDecimal::toBinary()
-     *
-     * @param mixed $x 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
-     *                                represented using two's-complement notation.
-     *                                If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
-     *                                value.
-     *                                If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
-     *                                If DEC2BIN requires more than places characters, it returns the #NUM!
-     *                                error value.
-     * @param mixed $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.
-     *
-     * @return array|string
-     */
-    public static function DECTOBIN($x, $places = null)
-    {
-        return Engineering\ConvertDecimal::toBinary($x, $places);
-    }
-
-    /**
-     * DECTOHEX.
-     *
-     * Return a decimal value as hex.
-     *
-     * Excel Function:
-     *        DEC2HEX(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toHex() method in the Engineering\ConvertDecimal class instead
-     * @see Engineering\ConvertDecimal::toHex()
-     *
-     * @param mixed $x 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
-     *                                are represented using two's-complement notation.
-     *                                If number < -549,755,813,888 or if number > 549,755,813,887,
-     *                                DEC2HEX returns the #NUM! error value.
-     *                                If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
-     *                                If DEC2HEX requires more than places characters, it returns the
-     *                                #NUM! error value.
-     * @param mixed $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.
-     *
-     * @return array|string
-     */
-    public static function DECTOHEX($x, $places = null)
-    {
-        return Engineering\ConvertDecimal::toHex($x, $places);
-    }
-
-    /**
-     * DECTOOCT.
-     *
-     * Return an decimal value as octal.
-     *
-     * Excel Function:
-     *        DEC2OCT(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toOctal() method in the Engineering\ConvertDecimal class instead
-     * @see Engineering\ConvertDecimal::toOctal()
-     *
-     * @param mixed $x 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
-     *                                represented using two's-complement notation.
-     *                                If number < -536,870,912 or if number > 536,870,911, DEC2OCT
-     *                                returns the #NUM! error value.
-     *                                If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
-     *                                If DEC2OCT requires more than places characters, it returns the
-     *                                #NUM! error value.
-     * @param mixed $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.
-     *
-     * @return array|string
-     */
-    public static function DECTOOCT($x, $places = null)
-    {
-        return Engineering\ConvertDecimal::toOctal($x, $places);
-    }
-
-    /**
-     * HEXTOBIN.
-     *
-     * Return a hex value as binary.
-     *
-     * Excel Function:
-     *        HEX2BIN(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toBinary() method in the Engineering\ConvertHex class instead
-     * @see Engineering\ConvertHex::toBinary()
-     *
-     * @param mixed $x the hexadecimal number (as a string) that 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.
-     *                  Negative numbers are represented using two's-complement notation.
-     *                  If number is negative, HEX2BIN ignores places and returns a 10-character binary number.
-     *                  If number is negative, it cannot be less than FFFFFFFE00,
-     *                      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 mixed $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.
-     *
-     * @return array|string
-     */
-    public static function HEXTOBIN($x, $places = null)
-    {
-        return Engineering\ConvertHex::toBinary($x, $places);
-    }
-
-    /**
-     * HEXTODEC.
-     *
-     * Return a hex value as decimal.
-     *
-     * Excel Function:
-     *        HEX2DEC(x)
-     *
-     * @deprecated 1.17.0
-     *      Use the toDecimal() method in the Engineering\ConvertHex class instead
-     * @see Engineering\ConvertHex::toDecimal()
-     *
-     * @param mixed $x The hexadecimal number (as a string) that 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.
-     *
-     * @return array|string
-     */
-    public static function HEXTODEC($x)
-    {
-        return Engineering\ConvertHex::toDecimal($x);
-    }
-
-    /**
-     * HEXTOOCT.
-     *
-     * Return a hex value as octal.
-     *
-     * Excel Function:
-     *        HEX2OCT(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toOctal() method in the Engineering\ConvertHex class instead
-     * @see Engineering\ConvertHex::toOctal()
-     *
-     * @param mixed $x The hexadecimal number (as a string) that 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
-     *                                    notation.
-     *                                    If number is negative, HEX2OCT ignores places and returns a
-     *                                    10-character octal number.
-     *                                    If number is negative, it cannot be less than FFE0000000, and
-     *                                    if number is positive, it cannot be greater than 1FFFFFFF.
-     *                                    If number is not a valid hexadecimal number, HEX2OCT returns
-     *                                    the #NUM! error value.
-     *                                    If HEX2OCT requires more than places characters, it returns
-     *                                    the #NUM! error value.
-     * @param mixed $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.
-     *
-     * @return array|string
-     */
-    public static function HEXTOOCT($x, $places = null)
-    {
-        return Engineering\ConvertHex::toOctal($x, $places);
-    }
-
-    /**
-     * OCTTOBIN.
-     *
-     * Return an octal value as binary.
-     *
-     * Excel Function:
-     *        OCT2BIN(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toBinary() method in the Engineering\ConvertOctal class instead
-     * @see Engineering\ConvertOctal::toBinary()
-     *
-     * @param mixed $x 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
-     *                                    using two's-complement notation.
-     *                                    If number is negative, OCT2BIN ignores places and returns
-     *                                    a 10-character binary number.
-     *                                    If number is negative, it cannot be less than 7777777000,
-     *                                    and if number is positive, it cannot be greater than 777.
-     *                                    If number is not a valid octal number, OCT2BIN returns
-     *                                    the #NUM! error value.
-     *                                    If OCT2BIN requires more than places characters, it
-     *                                    returns the #NUM! error value.
-     * @param mixed $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).
-     *                                    If places is not an integer, it is truncated.
-     *                                    If places is nonnumeric, OCT2BIN returns the #VALUE!
-     *                                    error value.
-     *                                    If places is negative, OCT2BIN returns the #NUM! error
-     *                                    value.
-     *
-     * @return array|string
-     */
-    public static function OCTTOBIN($x, $places = null)
-    {
-        return Engineering\ConvertOctal::toBinary($x, $places);
-    }
-
-    /**
-     * OCTTODEC.
-     *
-     * Return an octal value as decimal.
-     *
-     * Excel Function:
-     *        OCT2DEC(x)
-     *
-     * @deprecated 1.17.0
-     *      Use the toDecimal() method in the Engineering\ConvertOctal class instead
-     * @see Engineering\ConvertOctal::toDecimal()
-     *
-     * @param mixed $x 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.
-     *
-     * @return array|string
-     */
-    public static function OCTTODEC($x)
-    {
-        return Engineering\ConvertOctal::toDecimal($x);
-    }
-
-    /**
-     * OCTTOHEX.
-     *
-     * Return an octal value as hex.
-     *
-     * Excel Function:
-     *        OCT2HEX(x[,places])
-     *
-     * @deprecated 1.17.0
-     *      Use the toHex() method in the Engineering\ConvertOctal class instead
-     * @see Engineering\ConvertOctal::toHex()
-     *
-     * @param mixed $x 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 negative, OCT2HEX ignores places and returns a
-     *                                    10-character hexadecimal number.
-     *                                    If number is not a valid octal number, OCT2HEX returns the
-     *                                    #NUM! error value.
-     *                                    If OCT2HEX requires more than places characters, it returns
-     *                                    the #NUM! error value.
-     * @param mixed $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.
-     *
-     * @return array|string
-     */
-    public static function OCTTOHEX($x, $places = null)
-    {
-        return Engineering\ConvertOctal::toHex($x, $places);
-    }
-
-    /**
-     * COMPLEX.
-     *
-     * Converts real and imaginary coefficients into a complex number of the form x +/- yi or x +/- yj.
-     *
-     * Excel Function:
-     *        COMPLEX(realNumber,imaginary[,suffix])
-     *
-     * @deprecated 1.18.0
-     *      Use the COMPLEX() method in the Engineering\Complex class instead
-     * @see Engineering\Complex::COMPLEX()
-     *
-     * @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 array|string
-     */
-    public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
-    {
-        return Engineering\Complex::COMPLEX($realNumber, $imaginary, $suffix);
-    }
-
-    /**
-     * IMAGINARY.
-     *
-     * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMAGINARY(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMAGINARY() method in the Engineering\Complex class instead
-     * @see Engineering\Complex::IMAGINARY()
-     *
-     * @param string $complexNumber the complex number for which you want the imaginary
-     *                                         coefficient
-     *
-     * @return array|float|string
-     */
-    public static function IMAGINARY($complexNumber)
-    {
-        return Engineering\Complex::IMAGINARY($complexNumber);
-    }
-
-    /**
-     * IMREAL.
-     *
-     * Returns the real coefficient of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMREAL(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMREAL() method in the Engineering\Complex class instead
-     * @see Engineering\Complex::IMREAL()
-     *
-     * @param string $complexNumber the complex number for which you want the real coefficient
-     *
-     * @return array|float|string
-     */
-    public static function IMREAL($complexNumber)
-    {
-        return Engineering\Complex::IMREAL($complexNumber);
-    }
-
-    /**
-     * IMABS.
-     *
-     * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMABS(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMABS() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMABS()
-     *
-     * @param string $complexNumber the complex number for which you want the absolute value
-     *
-     * @return array|float|string
-     */
-    public static function IMABS($complexNumber)
-    {
-        return ComplexFunctions::IMABS($complexNumber);
-    }
-
-    /**
-     * IMARGUMENT.
-     *
-     * Returns the argument theta of a complex number, i.e. the angle in radians from the real
-     * axis to the representation of the number in polar coordinates.
-     *
-     * Excel Function:
-     *        IMARGUMENT(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMARGUMENT() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMARGUMENT()
-     *
-     * @param array|string $complexNumber the complex number for which you want the argument theta
-     *
-     * @return array|float|string
-     */
-    public static function IMARGUMENT($complexNumber)
-    {
-        return ComplexFunctions::IMARGUMENT($complexNumber);
-    }
-
-    /**
-     * IMCONJUGATE.
-     *
-     * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMCONJUGATE(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMCONJUGATE() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMCONJUGATE()
-     *
-     * @param array|string $complexNumber the complex number for which you want the conjugate
-     *
-     * @return array|string
-     */
-    public static function IMCONJUGATE($complexNumber)
-    {
-        return ComplexFunctions::IMCONJUGATE($complexNumber);
-    }
-
-    /**
-     * IMCOS.
-     *
-     * Returns the cosine of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMCOS(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMCOS() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMCOS()
-     *
-     * @param array|string $complexNumber the complex number for which you want the cosine
-     *
-     * @return array|float|string
-     */
-    public static function IMCOS($complexNumber)
-    {
-        return ComplexFunctions::IMCOS($complexNumber);
-    }
-
-    /**
-     * IMCOSH.
-     *
-     * Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMCOSH(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMCOSH() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMCOSH()
-     *
-     * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
-     *
-     * @return array|float|string
-     */
-    public static function IMCOSH($complexNumber)
-    {
-        return ComplexFunctions::IMCOSH($complexNumber);
-    }
-
-    /**
-     * IMCOT.
-     *
-     * Returns the cotangent of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMCOT(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMCOT() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMCOT()
-     *
-     * @param array|string $complexNumber the complex number for which you want the cotangent
-     *
-     * @return array|float|string
-     */
-    public static function IMCOT($complexNumber)
-    {
-        return ComplexFunctions::IMCOT($complexNumber);
-    }
-
-    /**
-     * IMCSC.
-     *
-     * Returns the cosecant of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMCSC(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMCSC() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMCSC()
-     *
-     * @param array|string $complexNumber the complex number for which you want the cosecant
-     *
-     * @return array|float|string
-     */
-    public static function IMCSC($complexNumber)
-    {
-        return ComplexFunctions::IMCSC($complexNumber);
-    }
-
-    /**
-     * IMCSCH.
-     *
-     * Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMCSCH(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMCSCH() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMCSCH()
-     *
-     * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
-     *
-     * @return array|float|string
-     */
-    public static function IMCSCH($complexNumber)
-    {
-        return ComplexFunctions::IMCSCH($complexNumber);
-    }
-
-    /**
-     * IMSIN.
-     *
-     * Returns the sine of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMSIN(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMSIN() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMSIN()
-     *
-     * @param string $complexNumber the complex number for which you want the sine
-     *
-     * @return array|float|string
-     */
-    public static function IMSIN($complexNumber)
-    {
-        return ComplexFunctions::IMSIN($complexNumber);
-    }
-
-    /**
-     * IMSINH.
-     *
-     * Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMSINH(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMSINH() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMSINH()
-     *
-     * @param string $complexNumber the complex number for which you want the hyperbolic sine
-     *
-     * @return array|float|string
-     */
-    public static function IMSINH($complexNumber)
-    {
-        return ComplexFunctions::IMSINH($complexNumber);
-    }
-
-    /**
-     * IMSEC.
-     *
-     * Returns the secant of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMSEC(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMSEC() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMSEC()
-     *
-     * @param string $complexNumber the complex number for which you want the secant
-     *
-     * @return array|float|string
-     */
-    public static function IMSEC($complexNumber)
-    {
-        return ComplexFunctions::IMSEC($complexNumber);
-    }
-
-    /**
-     * IMSECH.
-     *
-     * Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMSECH(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMSECH() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMSECH()
-     *
-     * @param string $complexNumber the complex number for which you want the hyperbolic secant
-     *
-     * @return array|float|string
-     */
-    public static function IMSECH($complexNumber)
-    {
-        return ComplexFunctions::IMSECH($complexNumber);
-    }
-
-    /**
-     * IMTAN.
-     *
-     * Returns the tangent of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMTAN(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMTAN() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMTAN()
-     *
-     * @param string $complexNumber the complex number for which you want the tangent
-     *
-     * @return array|float|string
-     */
-    public static function IMTAN($complexNumber)
-    {
-        return ComplexFunctions::IMTAN($complexNumber);
-    }
-
-    /**
-     * IMSQRT.
-     *
-     * Returns the square root of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMSQRT(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMSQRT() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMSQRT()
-     *
-     * @param string $complexNumber the complex number for which you want the square root
-     *
-     * @return array|string
-     */
-    public static function IMSQRT($complexNumber)
-    {
-        return ComplexFunctions::IMSQRT($complexNumber);
-    }
-
-    /**
-     * IMLN.
-     *
-     * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMLN(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMLN() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMLN()
-     *
-     * @param string $complexNumber the complex number for which you want the natural logarithm
-     *
-     * @return array|string
-     */
-    public static function IMLN($complexNumber)
-    {
-        return ComplexFunctions::IMLN($complexNumber);
-    }
-
-    /**
-     * IMLOG10.
-     *
-     * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMLOG10(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMLOG10() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMLOG10()
-     *
-     * @param string $complexNumber the complex number for which you want the common logarithm
-     *
-     * @return array|string
-     */
-    public static function IMLOG10($complexNumber)
-    {
-        return ComplexFunctions::IMLOG10($complexNumber);
-    }
-
-    /**
-     * IMLOG2.
-     *
-     * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMLOG2(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMLOG2() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMLOG2()
-     *
-     * @param string $complexNumber the complex number for which you want the base-2 logarithm
-     *
-     * @return array|string
-     */
-    public static function IMLOG2($complexNumber)
-    {
-        return ComplexFunctions::IMLOG2($complexNumber);
-    }
-
-    /**
-     * IMEXP.
-     *
-     * Returns the exponential of a complex number in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMEXP(complexNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMEXP() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMEXP()
-     *
-     * @param string $complexNumber the complex number for which you want the exponential
-     *
-     * @return array|string
-     */
-    public static function IMEXP($complexNumber)
-    {
-        return ComplexFunctions::IMEXP($complexNumber);
-    }
-
-    /**
-     * IMPOWER.
-     *
-     * Returns a complex number in x + yi or x + yj text format raised to a power.
-     *
-     * Excel Function:
-     *        IMPOWER(complexNumber,realNumber)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMPOWER() method in the Engineering\ComplexFunctions class instead
-     * @see ComplexFunctions::IMPOWER()
-     *
-     * @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 array|string
-     */
-    public static function IMPOWER($complexNumber, $realNumber)
-    {
-        return ComplexFunctions::IMPOWER($complexNumber, $realNumber);
-    }
-
-    /**
-     * IMDIV.
-     *
-     * Returns the quotient of two complex numbers in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMDIV(complexDividend,complexDivisor)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMDIV() method in the Engineering\ComplexOperations class instead
-     * @see ComplexOperations::IMDIV()
-     *
-     * @param string $complexDividend the complex numerator or dividend
-     * @param string $complexDivisor the complex denominator or divisor
-     *
-     * @return array|string
-     */
-    public static function IMDIV($complexDividend, $complexDivisor)
-    {
-        return ComplexOperations::IMDIV($complexDividend, $complexDivisor);
-    }
-
-    /**
-     * IMSUB.
-     *
-     * Returns the difference of two complex numbers in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMSUB(complexNumber1,complexNumber2)
-     *
-     * @deprecated 1.18.0
-     *      Use the IMSUB() method in the Engineering\ComplexOperations class instead
-     * @see ComplexOperations::IMSUB()
-     *
-     * @param string $complexNumber1 the complex number from which to subtract complexNumber2
-     * @param string $complexNumber2 the complex number to subtract from complexNumber1
-     *
-     * @return array|string
-     */
-    public static function IMSUB($complexNumber1, $complexNumber2)
-    {
-        return ComplexOperations::IMSUB($complexNumber1, $complexNumber2);
-    }
-
-    /**
-     * IMSUM.
-     *
-     * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMSUM(complexNumber[,complexNumber[,...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the IMSUM() method in the Engineering\ComplexOperations class instead
-     * @see ComplexOperations::IMSUM()
-     *
-     * @param string ...$complexNumbers Series of complex numbers to add
-     *
-     * @return string
-     */
-    public static function IMSUM(...$complexNumbers)
-    {
-        return ComplexOperations::IMSUM(...$complexNumbers);
-    }
-
-    /**
-     * IMPRODUCT.
-     *
-     * Returns the product of two or more complex numbers in x + yi or x + yj text format.
-     *
-     * Excel Function:
-     *        IMPRODUCT(complexNumber[,complexNumber[,...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the IMPRODUCT() method in the Engineering\ComplexOperations class instead
-     * @see ComplexOperations::IMPRODUCT()
-     *
-     * @param string ...$complexNumbers Series of complex numbers to multiply
-     *
-     * @return string
-     */
-    public static function IMPRODUCT(...$complexNumbers)
-    {
-        return ComplexOperations::IMPRODUCT(...$complexNumbers);
-    }
-
-    /**
-     * DELTA.
-     *
-     * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
-     * Use this function to filter a set of values. For example, by summing several DELTA
-     *     functions you calculate the count of equal pairs. This function is also known as the
-     *     Kronecker Delta function.
-     *
-     *    Excel Function:
-     *        DELTA(a[,b])
-     *
-     * @deprecated 1.17.0
-     *      Use the DELTA() method in the Engineering\Compare class instead
-     * @see Engineering\Compare::DELTA()
-     *
-     * @param float $a the first number
-     * @param float $b The second number. If omitted, b is assumed to be zero.
-     *
-     * @return array|int|string (string in the event of an error)
-     */
-    public static function DELTA($a, $b = 0)
-    {
-        return Engineering\Compare::DELTA($a, $b);
-    }
-
-    /**
-     * GESTEP.
-     *
-     *    Excel Function:
-     *        GESTEP(number[,step])
-     *
-     *    Returns 1 if number >= step; returns 0 (zero) otherwise
-     *    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.
-     *
-     * @deprecated 1.17.0
-     *      Use the GESTEP() method in the Engineering\Compare class instead
-     * @see Engineering\Compare::GESTEP()
-     *
-     * @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 array|int|string (string in the event of an error)
-     */
-    public static function GESTEP($number, $step = 0)
-    {
-        return Engineering\Compare::GESTEP($number, $step);
-    }
-
-    /**
-     * BITAND.
-     *
-     * Returns the bitwise AND of two integer values.
-     *
-     * Excel Function:
-     *        BITAND(number1, number2)
-     *
-     * @deprecated 1.17.0
-     *      Use the BITAND() method in the Engineering\BitWise class instead
-     * @see Engineering\BitWise::BITAND()
-     *
-     * @param int $number1
-     * @param int $number2
-     *
-     * @return array|int|string
-     */
-    public static function BITAND($number1, $number2)
-    {
-        return Engineering\BitWise::BITAND($number1, $number2);
-    }
-
-    /**
-     * BITOR.
-     *
-     * Returns the bitwise OR of two integer values.
-     *
-     * Excel Function:
-     *        BITOR(number1, number2)
-     *
-     * @deprecated 1.17.0
-     *      Use the BITOR() method in the Engineering\BitWise class instead
-     * @see Engineering\BitWise::BITOR()
-     *
-     * @param int $number1
-     * @param int $number2
-     *
-     * @return array|int|string
-     */
-    public static function BITOR($number1, $number2)
-    {
-        return Engineering\BitWise::BITOR($number1, $number2);
-    }
-
-    /**
-     * BITXOR.
-     *
-     * Returns the bitwise XOR of two integer values.
-     *
-     * Excel Function:
-     *        BITXOR(number1, number2)
-     *
-     * @deprecated 1.17.0
-     *      Use the BITXOR() method in the Engineering\BitWise class instead
-     * @see Engineering\BitWise::BITXOR()
-     *
-     * @param int $number1
-     * @param int $number2
-     *
-     * @return array|int|string
-     */
-    public static function BITXOR($number1, $number2)
-    {
-        return Engineering\BitWise::BITXOR($number1, $number2);
-    }
-
-    /**
-     * BITLSHIFT.
-     *
-     * Returns the number value shifted left by shift_amount bits.
-     *
-     * Excel Function:
-     *        BITLSHIFT(number, shift_amount)
-     *
-     * @deprecated 1.17.0
-     *      Use the BITLSHIFT() method in the Engineering\BitWise class instead
-     * @see Engineering\BitWise::BITLSHIFT()
-     *
-     * @param int $number
-     * @param int $shiftAmount
-     *
-     * @return array|float|int|string
-     */
-    public static function BITLSHIFT($number, $shiftAmount)
-    {
-        return Engineering\BitWise::BITLSHIFT($number, $shiftAmount);
-    }
-
-    /**
-     * BITRSHIFT.
-     *
-     * Returns the number value shifted right by shift_amount bits.
-     *
-     * Excel Function:
-     *        BITRSHIFT(number, shift_amount)
-     *
-     * @deprecated 1.17.0
-     *      Use the BITRSHIFT() method in the Engineering\BitWise class instead
-     * @see Engineering\BitWise::BITRSHIFT()
-     *
-     * @param int $number
-     * @param int $shiftAmount
-     *
-     * @return array|float|int|string
-     */
-    public static function BITRSHIFT($number, $shiftAmount)
-    {
-        return Engineering\BitWise::BITRSHIFT($number, $shiftAmount);
-    }
-
-    /**
-     * ERF.
-     *
-     * Returns the error function integrated between the lower and upper bound arguments.
-     *
-     *    Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
-     *            the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
-     *            improved, so that it can now calculate the function for both positive and negative ranges.
-     *            PhpSpreadsheet follows Excel 2010 behaviour, and accepts negative arguments.
-     *
-     *    Excel Function:
-     *        ERF(lower[,upper])
-     *
-     * @deprecated 1.17.0
-     *      Use the ERF() method in the Engineering\Erf class instead
-     * @see Engineering\Erf::ERF()
-     *
-     * @param float $lower lower bound for integrating ERF
-     * @param float $upper upper bound for integrating ERF.
-     *                                If omitted, ERF integrates between zero and lower_limit
-     *
-     * @return array|float|string
-     */
-    public static function ERF($lower, $upper = null)
-    {
-        return Engineering\Erf::ERF($lower, $upper);
-    }
-
-    /**
-     * ERFPRECISE.
-     *
-     * Returns the error function integrated between the lower and upper bound arguments.
-     *
-     *    Excel Function:
-     *        ERF.PRECISE(limit)
-     *
-     * @deprecated 1.17.0
-     *      Use the ERFPRECISE() method in the Engineering\Erf class instead
-     * @see Engineering\Erf::ERFPRECISE()
-     *
-     * @param float $limit bound for integrating ERF
-     *
-     * @return array|float|string
-     */
-    public static function ERFPRECISE($limit)
-    {
-        return Engineering\Erf::ERFPRECISE($limit);
-    }
-
-    /**
-     * ERFC.
-     *
-     *    Returns the complementary ERF function integrated between x and infinity
-     *
-     *    Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
-     *        the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
-     *        improved, so that it can now calculate the function for both positive and negative x values.
-     *            PhpSpreadsheet follows Excel 2010 behaviour, and accepts nagative arguments.
-     *
-     *    Excel Function:
-     *        ERFC(x)
-     *
-     * @deprecated 1.17.0
-     *      Use the ERFC() method in the Engineering\ErfC class instead
-     * @see Engineering\ErfC::ERFC()
-     *
-     * @param float $x The lower bound for integrating ERFC
-     *
-     * @return array|float|string
-     */
-    public static function ERFC($x)
-    {
-        return Engineering\ErfC::ERFC($x);
-    }
-
-    /**
-     *    getConversionGroups
-     * Returns a list of the different conversion groups for UOM conversions.
-     *
-     * @deprecated 1.16.0
-     *      Use the getConversionCategories() method in the Engineering\ConvertUOM class instead
-     * @see Engineering\ConvertUOM::getConversionCategories()
-     *
-     * @return array
-     */
-    public static function getConversionGroups()
-    {
-        return Engineering\ConvertUOM::getConversionCategories();
-    }
-
-    /**
-     *    getConversionGroupUnits
-     * Returns an array of units of measure, for a specified conversion group, or for all groups.
-     *
-     * @deprecated 1.16.0
-     *      Use the getConversionCategoryUnits() method in the ConvertUOM class instead
-     * @see Engineering\ConvertUOM::getConversionCategoryUnits()
-     *
-     * @param null|mixed $category
-     *
-     * @return array
-     */
-    public static function getConversionGroupUnits($category = null)
-    {
-        return Engineering\ConvertUOM::getConversionCategoryUnits($category);
-    }
-
-    /**
-     * getConversionGroupUnitDetails.
-     *
-     * @deprecated 1.16.0
-     *      Use the getConversionCategoryUnitDetails() method in the ConvertUOM class instead
-     * @see Engineering\ConvertUOM::getConversionCategoryUnitDetails()
-     *
-     * @param null|mixed $category
-     *
-     * @return array
-     */
-    public static function getConversionGroupUnitDetails($category = null)
-    {
-        return Engineering\ConvertUOM::getConversionCategoryUnitDetails($category);
-    }
-
-    /**
-     *    getConversionMultipliers
-     * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
-     *
-     * @deprecated 1.16.0
-     *      Use the getConversionMultipliers() method in the ConvertUOM class instead
-     * @see Engineering\ConvertUOM::getConversionMultipliers()
-     *
-     * @return mixed[]
-     */
-    public static function getConversionMultipliers()
-    {
-        return Engineering\ConvertUOM::getConversionMultipliers();
-    }
-
-    /**
-     *    getBinaryConversionMultipliers.
-     *
-     * Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure
-     *     in CONVERTUOM().
-     *
-     * @deprecated 1.16.0
-     *      Use the getBinaryConversionMultipliers() method in the ConvertUOM class instead
-     * @see Engineering\ConvertUOM::getBinaryConversionMultipliers()
-     *
-     * @return mixed[]
-     */
-    public static function getBinaryConversionMultipliers()
-    {
-        return Engineering\ConvertUOM::getBinaryConversionMultipliers();
-    }
-
-    /**
-     * CONVERTUOM.
-     *
-     * Converts a number from one measurement system to another.
-     *    For example, CONVERT can translate a table of distances in miles to a table of distances
-     * in kilometers.
-     *
-     *    Excel Function:
-     *        CONVERT(value,fromUOM,toUOM)
-     *
-     * @deprecated 1.16.0
-     *      Use the CONVERT() method in the ConvertUOM class instead
-     * @see Engineering\ConvertUOM::CONVERT()
-     *
-     * @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
-     *
-     * @return array|float|string
-     */
-    public static function CONVERTUOM($value, $fromUOM, $toUOM)
-    {
-        return Engineering\ConvertUOM::CONVERT($value, $fromUOM, $toUOM);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
index 1134574..5d564a0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php
@@ -35,7 +35,7 @@ class BesselI
      *         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)
+    public static function BESSELI(mixed $x, mixed $ord): array|string|float
     {
         if (is_array($x) || is_array($ord)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -59,15 +59,11 @@ class BesselI
 
     private static function calculate(float $x, int $ord): float
     {
-        // special cases
-        switch ($ord) {
-            case 0:
-                return self::besselI0($x);
-            case 1:
-                return self::besselI1($x);
-        }
-
-        return self::besselI2($x, $ord);
+        return match ($ord) {
+            0 => self::besselI0($x),
+            1 => self::besselI1($x),
+            default => self::besselI2($x, $ord),
+        };
     }
 
     private static function besselI0(float $x): float
@@ -85,8 +81,8 @@ class BesselI
         $y = 3.75 / $ax;
 
         return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
-                            + $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1 +
-                                        $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
+                            + $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1
+                                        + $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
     }
 
     private static function besselI1(float $x): float
@@ -96,31 +92,24 @@ class BesselI
         if ($ax < 3.75) {
             $y = $x / 3.75;
             $y = $y * $y;
-            $ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1 +
-                                    $y * (0.301532e-2 + $y * 0.32411e-3))))));
+            $ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1
+                                    + $y * (0.301532e-2 + $y * 0.32411e-3))))));
 
             return ($x < 0.0) ? -$ans : $ans;
         }
 
         $y = 3.75 / $ax;
         $ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
-        $ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2 +
-                        $y * (-0.1031555e-1 + $y * $ans))));
+        $ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2
+                        + $y * (-0.1031555e-1 + $y * $ans))));
         $ans *= exp($ax) / sqrt($ax);
 
         return ($x < 0.0) ? -$ans : $ans;
     }
 
-    /**
-     * Sop to Scrutinizer.
-     *
-     * @var float
-     */
-    private static $zeroPointZero = 0.0;
-
     private static function besselI2(float $x, int $ord): float
     {
-        if ($x === self::$zeroPointZero) {
+        if ($x === 0.0) {
             return 0.0;
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
index 800a8a1..4a9d9ff 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
@@ -34,7 +34,7 @@ class BesselJ
      *         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)
+    public static function BESSELJ(mixed $x, mixed $ord): array|string|float
     {
         if (is_array($x) || is_array($ord)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -58,15 +58,11 @@ class BesselJ
 
     private static function calculate(float $x, int $ord): float
     {
-        // special cases
-        switch ($ord) {
-            case 0:
-                return self::besselJ0($x);
-            case 1:
-                return self::besselJ1($x);
-        }
-
-        return self::besselJ2($x, $ord);
+        return match ($ord) {
+            0 => self::besselJ0($x),
+            1 => self::besselJ1($x),
+            default => self::besselJ2($x, $ord),
+        };
     }
 
     private static function besselJ0(float $x): float
@@ -75,10 +71,10 @@ class BesselJ
 
         if ($ax < 8.0) {
             $y = $x * $x;
-            $ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y *
-                            (77392.33017 + $y * (-184.9052456)))));
-            $ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y *
-                            (267.8532712 + $y * 1.0))));
+            $ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y
+                            * (77392.33017 + $y * (-184.9052456)))));
+            $ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y
+                            * (267.8532712 + $y * 1.0))));
 
             return $ans1 / $ans2;
         }
@@ -87,8 +83,8 @@ class BesselJ
         $y = $z * $z;
         $xx = $ax - 0.785398164;
         $ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
-        $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y *
-                    (0.7621095161e-6 - $y * 0.934935152e-7)));
+        $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y
+                    * (0.7621095161e-6 - $y * 0.934935152e-7)));
 
         return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
     }
@@ -99,10 +95,10 @@ class BesselJ
 
         if ($ax < 8.0) {
             $y = $x * $x;
-            $ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y *
-                            (-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
-            $ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y *
-                            (376.9991397 + $y * 1.0))));
+            $ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y
+                            * (-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
+            $ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y
+                            * (376.9991397 + $y * 1.0))));
 
             return $ans1 / $ans2;
         }
@@ -112,8 +108,8 @@ class BesselJ
         $xx = $ax - 2.356194491;
 
         $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
-        $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
-                    (-0.88228987e-6 + $y * 0.105787412e-6)));
+        $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
+                    * (-0.88228987e-6 + $y * 0.105787412e-6)));
         $ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
 
         return ($x < 0.0) ? -$ans : $ans;
@@ -133,7 +129,7 @@ class BesselJ
         return self::besselj2b($ax, $ord, $x);
     }
 
-    private static function besselj2a(float $ax, int $ord, float $x)
+    private static function besselj2a(float $ax, int $ord, float $x): float
     {
         $tox = 2.0 / $ax;
         $bjm = self::besselJ0($ax);
@@ -148,7 +144,7 @@ class BesselJ
         return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
     }
 
-    private static function besselj2b(float $ax, int $ord, float $x)
+    private static function besselj2b(float $ax, int $ord, float $x): float
     {
         $tox = 2.0 / $ax;
         $jsum = false;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
index 2d21e75..5a9bd54 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
@@ -4,7 +4,6 @@ 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
@@ -33,7 +32,7 @@ class BesselK
      *         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)
+    public static function BESSELK(mixed $x, mixed $ord): array|string|float
     {
         if (is_array($x) || is_array($ord)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -57,15 +56,11 @@ class BesselK
 
     private static function calculate(float $x, int $ord): float
     {
-        // special cases
-        switch ($ord) {
-            case 0:
-                return self::besselK0($x);
-            case 1:
-                return self::besselK1($x);
-        }
-
-        return self::besselK2($x, $ord);
+        return match ($ord) {
+            0 => self::besselK0($x),
+            1 => self::besselK1($x),
+            default => self::besselK2($x, $ord),
+        };
     }
 
     /**
@@ -89,16 +84,16 @@ class BesselK
             $fNum2 = $x * 0.5;
             $y = ($fNum2 * $fNum2);
 
-            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))))));
+            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))))));
         }
 
         $y = 2 / $x;
 
-        return exp(-$x) / sqrt($x) *
-            (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
-                            (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
+        return exp(-$x) / sqrt($x)
+            * (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y
+                            * (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
     }
 
     private static function besselK1(float $x): float
@@ -107,16 +102,16 @@ class BesselK
             $fNum2 = $x * 0.5;
             $y = ($fNum2 * $fNum2);
 
-            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;
+            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;
         }
 
         $y = 2 / $x;
 
-        return exp(-$x) / sqrt($x) *
-            (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
-                                (0.325614e-2 + $y * (-0.68245e-3)))))));
+        return exp(-$x) / sqrt($x)
+            * (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y
+                                * (0.325614e-2 + $y * (-0.68245e-3)))))));
     }
 
     private static function besselK2(float $x, int $ord): float
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
index 31d9694..5d99638 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselY.php
@@ -31,7 +31,7 @@ class BesselY
      *         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)
+    public static function BESSELY(mixed $x, mixed $ord): array|string|float
     {
         if (is_array($x) || is_array($ord)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
@@ -55,15 +55,11 @@ class BesselY
 
     private static function calculate(float $x, int $ord): float
     {
-        // special cases
-        switch ($ord) {
-            case 0:
-                return self::besselY0($x);
-            case 1:
-                return self::besselY1($x);
-        }
-
-        return self::besselY2($x, $ord);
+        return match ($ord) {
+            0 => self::besselY0($x),
+            1 => self::besselY1($x),
+            default => self::besselY2($x, $ord),
+        };
     }
 
     /**
@@ -85,10 +81,10 @@ class BesselY
     {
         if ($x < 8.0) {
             $y = ($x * $x);
-            $ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y *
-                            (-86327.92757 + $y * 228.4622733))));
-            $ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
-                        (47447.26470 + $y * (226.1030244 + $y))));
+            $ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y
+                            * (-86327.92757 + $y * 228.4622733))));
+            $ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y
+                        * (47447.26470 + $y * (226.1030244 + $y))));
 
             return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
         }
@@ -97,8 +93,8 @@ class BesselY
         $y = ($z * $z);
         $xx = $x - 0.785398164;
         $ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
-        $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y *
-                        (-0.934945152e-7))));
+        $ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y
+                        * (-0.934945152e-7))));
 
         return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
     }
@@ -107,10 +103,10 @@ class BesselY
     {
         if ($x < 8.0) {
             $y = ($x * $x);
-            $ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y *
-                            (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
-            $ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
-                            (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
+            $ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y
+                            * (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
+            $ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y
+                            * (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
 
             return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
         }
@@ -119,8 +115,8 @@ class BesselY
         $y = $z * $z;
         $xx = $x - 2.356194491;
         $ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
-        $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
-                    (-0.88228987e-6 + $y * 0.105787412e-6)));
+        $ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
+                    * (-0.88228987e-6 + $y * 0.105787412e-6)));
 
         return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
index 0362649..c861c21 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
@@ -4,7 +4,6 @@ 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
@@ -16,11 +15,9 @@ class BitWise
     /**
      * Split a number into upper and lower portions for full 32-bit support.
      *
-     * @param float|int $number
-     *
      * @return int[]
      */
-    private static function splitNumber($number): array
+    private static function splitNumber(float|int $number): array
     {
         return [(int) floor($number / self::SPLIT_DIVISOR), (int) fmod($number, self::SPLIT_DIVISOR)];
     }
@@ -33,16 +30,13 @@ class BitWise
      * Excel Function:
      *        BITAND(number1, number2)
      *
-     * @param array|int $number1
-     *                      Or can be an array of values
-     * @param array|int $number2
-     *                      Or can be an array of values
+     * @param null|array|bool|float|int|string $number1 Or can be an array of values
+     * @param null|array|bool|float|int|string $number2 Or can be an array of values
      *
-     * @return array|int|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
     {
         if (is_array($number1) || is_array($number2)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
@@ -57,7 +51,7 @@ class BitWise
         $split1 = self::splitNumber($number1);
         $split2 = self::splitNumber($number2);
 
-        return  self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
+        return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
     }
 
     /**
@@ -68,16 +62,13 @@ class BitWise
      * Excel Function:
      *        BITOR(number1, number2)
      *
-     * @param array|int $number1
-     *                      Or can be an array of values
-     * @param array|int $number2
-     *                      Or can be an array of values
+     * @param null|array|bool|float|int|string $number1 Or can be an array of values
+     * @param null|array|bool|float|int|string $number2 Or can be an array of values
      *
-     * @return array|int|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
     {
         if (is_array($number1) || is_array($number2)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
@@ -93,7 +84,7 @@ class BitWise
         $split1 = self::splitNumber($number1);
         $split2 = self::splitNumber($number2);
 
-        return  self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
+        return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
     }
 
     /**
@@ -104,16 +95,13 @@ class BitWise
      * Excel Function:
      *        BITXOR(number1, number2)
      *
-     * @param array|int $number1
-     *                      Or can be an array of values
-     * @param array|int $number2
-     *                      Or can be an array of values
+     * @param null|array|bool|float|int|string $number1 Or can be an array of values
+     * @param null|array|bool|float|int|string $number2 Or can be an array of values
      *
-     * @return array|int|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
     {
         if (is_array($number1) || is_array($number2)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
@@ -129,7 +117,7 @@ class BitWise
         $split1 = self::splitNumber($number1);
         $split2 = self::splitNumber($number2);
 
-        return  self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
+        return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
     }
 
     /**
@@ -140,16 +128,13 @@ class BitWise
      * Excel Function:
      *        BITLSHIFT(number, shift_amount)
      *
-     * @param array|int $number
-     *                      Or can be an array of values
-     * @param array|int $shiftAmount
-     *                      Or can be an array of values
+     * @param null|array|bool|float|int|string $number Or can be an array of values
+     * @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
      *
-     * @return array|float|int|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 BITLSHIFT($number, $shiftAmount)
+    public static function BITLSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
     {
         if (is_array($number) || is_array($shiftAmount)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
@@ -178,16 +163,13 @@ class BitWise
      * Excel Function:
      *        BITRSHIFT(number, shift_amount)
      *
-     * @param array|int $number
-     *                      Or can be an array of values
-     * @param array|int $shiftAmount
-     *                      Or can be an array of values
+     * @param null|array|bool|float|int|string $number Or can be an array of values
+     * @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
      *
-     * @return array|float|int|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 BITRSHIFT($number, $shiftAmount)
+    public static function BITRSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
     {
         if (is_array($number) || is_array($shiftAmount)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
@@ -210,12 +192,8 @@ class BitWise
 
     /**
      * Validate arguments passed to the bitwise functions.
-     *
-     * @param mixed $value
-     *
-     * @return float
      */
-    private static function validateBitwiseArgument($value)
+    private static function validateBitwiseArgument(mixed $value): float
     {
         $value = self::nullFalseTrueToNumber($value);
 
@@ -237,12 +215,8 @@ class BitWise
 
     /**
      * Validate arguments passed to the bitwise functions.
-     *
-     * @param mixed $value
-     *
-     * @return int
      */
-    private static function validateShiftAmount($value)
+    private static function validateShiftAmount(mixed $value): int
     {
         $value = self::nullFalseTrueToNumber($value);
 
@@ -259,12 +233,8 @@ class BitWise
 
     /**
      * Many functions accept null/false/true argument treated as 0/0/1.
-     *
-     * @param mixed $number
-     *
-     * @return mixed
      */
-    private static function nullFalseTrueToNumber(&$number)
+    private static function nullFalseTrueToNumber(mixed &$number): mixed
     {
         if ($number === null) {
             $number = 0;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
index 6aaf1fa..9e3275f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php
@@ -20,16 +20,16 @@ class Compare
      *        functions you calculate the count of equal pairs. This function is also known as the
      *        Kronecker Delta function.
      *
-     * @param array|float $a the first number
+     * @param array|bool|float|int|string $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.
+     * @param array|bool|float|int|string $b The second number. If omitted, b is assumed to be zero.
      *                      Or can be an array of values
      *
      * @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.0)
+    public static function DELTA(array|float|bool|string|int $a, array|float|bool|string|int $b = 0.0): array|string|int
     {
         if (is_array($a) || is_array($b)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $a, $b);
@@ -55,16 +55,16 @@ 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 array|float $number the value to test against step
+     * @param array|bool|float|int|string $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.
+     * @param null|array|bool|float|int|string $step The threshold value. If you omit a value for step, GESTEP uses zero.
      *                      Or can be an array of values
      *
      * @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.0)
+    public static function GESTEP(array|float|bool|string|int $number, $step = 0.0): array|string|int
     {
         if (is_array($number) || is_array($step)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $step);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
index 691de8b..3e41371 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php
@@ -28,11 +28,10 @@ class Complex
      *                          If omitted, the suffix is assumed to be "i".
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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')
+    public static function COMPLEX(mixed $realNumber = 0.0, mixed $imaginary = 0.0, mixed $suffix = 'i'): array|string
     {
         if (is_array($realNumber) || is_array($imaginary) || is_array($suffix)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $realNumber, $imaginary, $suffix);
@@ -49,7 +48,7 @@ class Complex
             return $e->getMessage();
         }
 
-        if (($suffix == 'i') || ($suffix == 'j') || ($suffix == '')) {
+        if (($suffix === 'i') || ($suffix === 'j') || ($suffix === '')) {
             $complex = new ComplexObject($realNumber, $imaginary, $suffix);
 
             return (string) $complex;
@@ -74,7 +73,7 @@ class Complex
      *         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)
+    public static function IMAGINARY($complexNumber): array|string|float
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -82,7 +81,7 @@ class Complex
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -104,7 +103,7 @@ class Complex
      *         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)
+    public static function IMREAL($complexNumber): array|string|float
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -112,7 +111,7 @@ class Complex
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
index 28a27a0..d1b7764 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
@@ -22,11 +22,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the absolute value
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMABS(array|string $complexNumber): array|float|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -34,7 +33,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -53,11 +52,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the argument theta
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMARGUMENT(array|string $complexNumber): array|float|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -65,7 +63,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -87,11 +85,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the conjugate
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMCONJUGATE(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -99,7 +96,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -117,11 +114,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the cosine
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMCOS($complexNumber)
+    public static function IMCOS(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -129,7 +125,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -147,11 +143,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMCOSH($complexNumber)
+    public static function IMCOSH(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -159,7 +154,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -177,11 +172,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the cotangent
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMCOT($complexNumber)
+    public static function IMCOT(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -189,7 +183,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -207,11 +201,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the cosecant
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMCSC($complexNumber)
+    public static function IMCSC(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -219,7 +212,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -237,11 +230,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMCSCH($complexNumber)
+    public static function IMCSCH(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -249,7 +241,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -267,11 +259,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the sine
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMSIN($complexNumber)
+    public static function IMSIN(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -279,7 +270,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -297,11 +288,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the hyperbolic sine
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMSINH($complexNumber)
+    public static function IMSINH(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -309,7 +299,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -327,11 +317,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the secant
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMSEC($complexNumber)
+    public static function IMSEC(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -339,7 +328,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -357,11 +346,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the hyperbolic secant
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMSECH($complexNumber)
+    public static function IMSECH(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -369,7 +357,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -387,11 +375,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the tangent
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 IMTAN($complexNumber)
+    public static function IMTAN(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -399,7 +386,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -417,11 +404,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the square root
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMSQRT(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -429,7 +415,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -452,11 +438,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the natural logarithm
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMLN(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -464,7 +449,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -486,11 +471,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the common logarithm
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMLOG10(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -498,7 +482,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -520,11 +504,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the base-2 logarithm
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMLOG2(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -532,7 +515,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -554,11 +537,10 @@ class ComplexFunctions
      * @param array|string $complexNumber the complex number for which you want the exponential
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMEXP(array|string $complexNumber): array|string
     {
         if (is_array($complexNumber)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
@@ -566,7 +548,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -586,11 +568,10 @@ class ComplexFunctions
      * @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 array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMPOWER(array|string $complexNumber, array|float|int|string $realNumber): array|string
     {
         if (is_array($complexNumber) || is_array($realNumber)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber, $realNumber);
@@ -598,7 +579,7 @@ class ComplexFunctions
 
         try {
             $complex = new ComplexObject($complexNumber);
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
index e525b4b..61efa84 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
@@ -25,11 +25,10 @@ class ComplexOperations
      * @param array|string $complexDivisor the complex denominator or divisor
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMDIV(array|string $complexDividend, array|string $complexDivisor): array|string
     {
         if (is_array($complexDividend) || is_array($complexDivisor)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexDividend, $complexDivisor);
@@ -37,7 +36,7 @@ class ComplexOperations
 
         try {
             return (string) (new ComplexObject($complexDividend))->divideby(new ComplexObject($complexDivisor));
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
     }
@@ -55,11 +54,10 @@ class ComplexOperations
      * @param array|string $complexNumber2 the complex number to subtract from complexNumber1
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function IMSUB(array|string $complexNumber1, array|string $complexNumber2): array|string
     {
         if (is_array($complexNumber1) || is_array($complexNumber2)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber1, $complexNumber2);
@@ -67,7 +65,7 @@ class ComplexOperations
 
         try {
             return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
     }
@@ -81,10 +79,8 @@ class ComplexOperations
      *        IMSUM(complexNumber[,complexNumber[,...]])
      *
      * @param string ...$complexNumbers Series of complex numbers to add
-     *
-     * @return string
      */
-    public static function IMSUM(...$complexNumbers)
+    public static function IMSUM(...$complexNumbers): string
     {
         // Return value
         $returnValue = new ComplexObject(0.0);
@@ -95,7 +91,7 @@ class ComplexOperations
             foreach ($aArgs as $complex) {
                 $returnValue = $returnValue->add(new ComplexObject($complex));
             }
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
@@ -111,10 +107,8 @@ class ComplexOperations
      *        IMPRODUCT(complexNumber[,complexNumber[,...]])
      *
      * @param string ...$complexNumbers Series of complex numbers to multiply
-     *
-     * @return string
      */
-    public static function IMPRODUCT(...$complexNumbers)
+    public static function IMPRODUCT(...$complexNumbers): string
     {
         // Return value
         $returnValue = new ComplexObject(1.0);
@@ -125,7 +119,7 @@ class ComplexOperations
             foreach ($aArgs as $complex) {
                 $returnValue = $returnValue->multiply(new ComplexObject($complex));
             }
-        } catch (ComplexException $e) {
+        } catch (ComplexException) {
             return ExcelError::NAN();
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
index e80490b..1222831 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
@@ -11,7 +11,7 @@ abstract class ConvertBase
 {
     use ArrayEnabled;
 
-    protected static function validateValue($value): string
+    protected static function validateValue(mixed $value): string
     {
         if (is_bool($value)) {
             if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
@@ -29,7 +29,7 @@ abstract class ConvertBase
         return strtoupper((string) $value);
     }
 
-    protected static function validatePlaces($places = null): ?int
+    protected static function validatePlaces(mixed $places = null): ?int
     {
         if ($places === null) {
             return $places;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
index 4741f30..9c00dcb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
@@ -15,7 +15,7 @@ class ConvertBinary extends ConvertBase
      * Excel Function:
      *        BIN2DEC(x)
      *
-     * @param array|string $value The binary number (as a string) that you want to convert. The number
+     * @param array|bool|float|int|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.
@@ -40,7 +40,7 @@ class ConvertBinary extends ConvertBase
             return $e->getMessage();
         }
 
-        if (strlen($value) == 10) {
+        if (strlen($value) == 10 && $value[0] === '1') {
             //    Two's Complement
             $value = substr($value, -9);
 
@@ -58,14 +58,14 @@ class ConvertBinary extends ConvertBase
      * Excel Function:
      *        BIN2HEX(x[,places])
      *
-     * @param array|string $value The binary number (as a string) that you want to convert. The number
+     * @param array|bool|float|int|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.
      *                      Or can be an array of values
-     * @param array|int $places The number of characters to use. If places is omitted, BIN2HEX uses the
+     * @param null|array|float|int|string $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.
@@ -77,7 +77,7 @@ class ConvertBinary extends ConvertBase
      *         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)
+    public static function toHex($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -91,7 +91,7 @@ class ConvertBinary extends ConvertBase
             return $e->getMessage();
         }
 
-        if (strlen($value) == 10) {
+        if (strlen($value) == 10 && $value[0] === '1') {
             $high2 = substr($value, 0, 2);
             $low8 = substr($value, 2);
             $xarr = ['00' => '00000000', '01' => '00000001', '10' => 'FFFFFFFE', '11' => 'FFFFFFFF'];
@@ -111,14 +111,14 @@ class ConvertBinary extends ConvertBase
      * Excel Function:
      *        BIN2OCT(x[,places])
      *
-     * @param array|string $value The binary number (as a string) that you want to convert. The number
+     * @param array|bool|float|int|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.
      *                      Or can be an array of values
-     * @param array|int $places The number of characters to use. If places is omitted, BIN2OCT uses the
+     * @param null|array|float|int|string $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.
@@ -130,7 +130,7 @@ class ConvertBinary extends ConvertBase
      *         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)
+    public static function toOctal($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -144,7 +144,7 @@ class ConvertBinary extends ConvertBase
             return $e->getMessage();
         }
 
-        if (strlen($value) == 10 && substr($value, 0, 1) === '1') { //    Two's Complement
+        if (strlen($value) == 10 && $value[0] === '1') { //    Two's Complement
             return str_repeat('7', 6) . strtoupper(decoct((int) bindec("11$value")));
         }
         $octVal = (string) decoct((int) bindec($value));
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
index 9b59d39..923caa9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
@@ -22,7 +22,7 @@ class ConvertDecimal extends ConvertBase
      * Excel Function:
      *        DEC2BIN(x[,places])
      *
-     * @param array|string $value The decimal integer you want to convert. If number is negative,
+     * @param array|bool|float|int|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
@@ -33,7 +33,7 @@ class ConvertDecimal extends ConvertBase
      *                      If DEC2BIN requires more than places characters, it returns the #NUM!
      *                          error value.
      *                      Or can be an array of values
-     * @param array|int $places The number of characters to use. If places is omitted, DEC2BIN uses
+     * @param null|array|float|int|string $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.
@@ -45,7 +45,7 @@ class ConvertDecimal extends ConvertBase
      *         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)
+    public static function toBinary($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -79,7 +79,7 @@ class ConvertDecimal extends ConvertBase
      * Excel Function:
      *        DEC2HEX(x[,places])
      *
-     * @param array|string $value The decimal integer you want to convert. If number is negative,
+     * @param array|bool|float|int|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
@@ -90,7 +90,7 @@ class ConvertDecimal extends ConvertBase
      *                      If DEC2HEX requires more than places characters, it returns the
      *                          #NUM! error value.
      *                      Or can be an array of values
-     * @param array|int $places The number of characters to use. If places is omitted, DEC2HEX uses
+     * @param null|array|float|int|string $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.
@@ -102,7 +102,7 @@ class ConvertDecimal extends ConvertBase
      *         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)
+    public static function toHex($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -155,7 +155,7 @@ class ConvertDecimal extends ConvertBase
      * Excel Function:
      *        DEC2OCT(x[,places])
      *
-     * @param array|string $value The decimal integer you want to convert. If number is negative,
+     * @param array|bool|float|int|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
@@ -178,7 +178,7 @@ class ConvertDecimal extends ConvertBase
      *         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)
+    public static function toOctal($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
index 55ce209..0003a9f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
@@ -15,7 +15,7 @@ class ConvertHex extends ConvertBase
      * Excel Function:
      *        HEX2BIN(x[,places])
      *
-     * @param array|string $value The hexadecimal number you want to convert.
+     * @param array|bool|float|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.
@@ -38,7 +38,7 @@ class ConvertHex extends ConvertBase
      *         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)
+    public static function toBinary($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -65,7 +65,7 @@ class ConvertHex extends ConvertBase
      * Excel Function:
      *        HEX2DEC(x)
      *
-     * @param array|string $value The hexadecimal number you want to convert. This number cannot
+     * @param array|bool|float|int|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
@@ -118,7 +118,7 @@ class ConvertHex extends ConvertBase
      * Excel Function:
      *        HEX2OCT(x[,places])
      *
-     * @param array|string $value The hexadecimal number you want to convert. Number cannot
+     * @param array|bool|float|int|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
@@ -145,7 +145,7 @@ class ConvertHex extends ConvertBase
      *         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)
+    public static function toOctal($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
index add7aba..5e3c124 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
@@ -15,7 +15,7 @@ class ConvertOctal extends ConvertBase
      * Excel Function:
      *        OCT2BIN(x[,places])
      *
-     * @param array|string $value The octal number you want to convert. Number may not
+     * @param array|bool|float|int|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
@@ -44,7 +44,7 @@ class ConvertOctal extends ConvertBase
      *         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)
+    public static function toBinary($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
@@ -69,7 +69,7 @@ class ConvertOctal extends ConvertBase
      * Excel Function:
      *        OCT2DEC(x)
      *
-     * @param array|string $value The octal number you want to convert. Number may not contain
+     * @param array|bool|float|int|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
@@ -118,7 +118,7 @@ class ConvertOctal extends ConvertBase
      * Excel Function:
      *        OCT2HEX(x[,places])
      *
-     * @param array|string $value The octal number you want to convert. Number may not contain
+     * @param array|bool|float|int|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
@@ -142,7 +142,7 @@ class ConvertOctal extends ConvertBase
      *         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)
+    public static function toHex($value, $places = null): array|string
     {
         if (is_array($value) || is_array($places)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
index 8541a6c..969c270 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
@@ -27,183 +27,183 @@ class ConvertUOM
     /**
      * Details of the Units of measure that can be used in CONVERTUOM().
      *
-     * @var mixed[]
+     * @var array<string, array{Group: string, UnitName: string, AllowPrefix: bool}>
      */
-    private static $conversionUnits = [
+    private static array $conversionUnits = [
         // Weight and Mass
-        'g' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Gram', 'AllowPrefix' => true],
-        'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Slug', 'AllowPrefix' => false],
-        'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
-        'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
-        'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
-        'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Grain', 'AllowPrefix' => false],
-        'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
-        'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
-        'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
-        'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
-        'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
-        'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Stone', 'AllowPrefix' => false],
-        'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ton', 'AllowPrefix' => false],
-        'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
-        'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
-        'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
+        'g' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Gram', 'AllowPrefix' => true],
+        'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Slug', 'AllowPrefix' => false],
+        'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
+        'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'U (atomic mass unit)', 'AllowPrefix' => true],
+        'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
+        'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Grain', 'AllowPrefix' => false],
+        'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
+        'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
+        'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial hundredweight', 'AllowPrefix' => false],
+        'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial hundredweight', 'AllowPrefix' => false],
+        'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial hundredweight', 'AllowPrefix' => false],
+        'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Stone', 'AllowPrefix' => false],
+        'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Ton', 'AllowPrefix' => false],
+        'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial ton', 'AllowPrefix' => false],
+        'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial ton', 'AllowPrefix' => false],
+        'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'UnitName' => 'Imperial ton', 'AllowPrefix' => false],
         // Distance
-        'm' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Meter', 'AllowPrefix' => true],
-        'mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
-        'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
-        'in' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Inch', 'AllowPrefix' => false],
-        'ft' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Foot', 'AllowPrefix' => false],
-        'yd' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Yard', 'AllowPrefix' => false],
-        'ang' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
-        'ell' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Ell', 'AllowPrefix' => false],
-        'ly' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Light Year', 'AllowPrefix' => false],
-        'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
-        'pc' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
-        'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
-        'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
-        'pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/6 in)', 'AllowPrefix' => false],
-        'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
+        'm' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Meter', 'AllowPrefix' => true],
+        'mi' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Statute mile', 'AllowPrefix' => false],
+        'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Nautical mile', 'AllowPrefix' => false],
+        'in' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Inch', 'AllowPrefix' => false],
+        'ft' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Foot', 'AllowPrefix' => false],
+        'yd' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Yard', 'AllowPrefix' => false],
+        'ang' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Angstrom', 'AllowPrefix' => true],
+        'ell' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Ell', 'AllowPrefix' => false],
+        'ly' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Light Year', 'AllowPrefix' => false],
+        'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Parsec', 'AllowPrefix' => false],
+        'pc' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Parsec', 'AllowPrefix' => false],
+        'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Pica (1/72 in)', 'AllowPrefix' => false],
+        'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Pica (1/72 in)', 'AllowPrefix' => false],
+        'pica' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'Pica (1/6 in)', 'AllowPrefix' => false],
+        'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'UnitName' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
         // Time
-        'yr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Year', 'AllowPrefix' => false],
-        'day' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
-        'd' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
-        'hr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Hour', 'AllowPrefix' => false],
-        'mn' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
-        'min' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
-        'sec' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
-        's' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
+        'yr' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Year', 'AllowPrefix' => false],
+        'day' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Day', 'AllowPrefix' => false],
+        'd' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Day', 'AllowPrefix' => false],
+        'hr' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Hour', 'AllowPrefix' => false],
+        'mn' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Minute', 'AllowPrefix' => false],
+        'min' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Minute', 'AllowPrefix' => false],
+        'sec' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Second', 'AllowPrefix' => true],
+        's' => ['Group' => self::CATEGORY_TIME, 'UnitName' => 'Second', 'AllowPrefix' => true],
         // Pressure
-        'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
-        'p' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
-        'atm' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
-        'at' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
-        'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
-        'psi' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'PSI', 'AllowPrefix' => true],
-        'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Torr', 'AllowPrefix' => true],
+        'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Pascal', 'AllowPrefix' => true],
+        'p' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Pascal', 'AllowPrefix' => true],
+        'atm' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Atmosphere', 'AllowPrefix' => true],
+        'at' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Atmosphere', 'AllowPrefix' => true],
+        'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'mm of Mercury', 'AllowPrefix' => true],
+        'psi' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'PSI', 'AllowPrefix' => true],
+        'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'UnitName' => 'Torr', 'AllowPrefix' => true],
         // Force
-        'N' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Newton', 'AllowPrefix' => true],
-        'dyn' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
-        'dy' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
-        'lbf' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
-        'pond' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pond', 'AllowPrefix' => true],
+        'N' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Newton', 'AllowPrefix' => true],
+        'dyn' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Dyne', 'AllowPrefix' => true],
+        'dy' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Dyne', 'AllowPrefix' => true],
+        'lbf' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Pound force', 'AllowPrefix' => false],
+        'pond' => ['Group' => self::CATEGORY_FORCE, 'UnitName' => 'Pond', 'AllowPrefix' => true],
         // Energy
-        'J' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Joule', 'AllowPrefix' => true],
-        'e' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Erg', 'AllowPrefix' => true],
-        'c' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
-        'cal' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
-        'eV' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
-        'ev' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
-        'HPh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
-        'hh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
-        'Wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
-        'wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
-        'flb' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
-        'BTU' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
-        'btu' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
+        'J' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Joule', 'AllowPrefix' => true],
+        'e' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Erg', 'AllowPrefix' => true],
+        'c' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Thermodynamic calorie', 'AllowPrefix' => true],
+        'cal' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'IT calorie', 'AllowPrefix' => true],
+        'eV' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Electron volt', 'AllowPrefix' => true],
+        'ev' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Electron volt', 'AllowPrefix' => true],
+        'HPh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Horsepower-hour', 'AllowPrefix' => false],
+        'hh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Horsepower-hour', 'AllowPrefix' => false],
+        'Wh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Watt-hour', 'AllowPrefix' => true],
+        'wh' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Watt-hour', 'AllowPrefix' => true],
+        'flb' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'Foot-pound', 'AllowPrefix' => false],
+        'BTU' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'BTU', 'AllowPrefix' => false],
+        'btu' => ['Group' => self::CATEGORY_ENERGY, 'UnitName' => 'BTU', 'AllowPrefix' => false],
         // Power
-        'HP' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
-        'h' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
-        '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],
+        'HP' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Horsepower', 'AllowPrefix' => false],
+        'h' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Horsepower', 'AllowPrefix' => false],
+        'W' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Watt', 'AllowPrefix' => true],
+        'w' => ['Group' => self::CATEGORY_POWER, 'UnitName' => 'Watt', 'AllowPrefix' => true],
+        'PS' => ['Group' => self::CATEGORY_POWER, 'UnitName' => '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],
+        'T' => ['Group' => self::CATEGORY_MAGNETISM, 'UnitName' => 'Tesla', 'AllowPrefix' => true],
+        'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'UnitName' => 'Gauss', 'AllowPrefix' => true],
         // Temperature
-        'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
-        'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
-        'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
-        'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
-        'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
-        'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
-        'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Rankine', 'AllowPrefix' => false],
-        'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Réaumur', 'AllowPrefix' => false],
+        'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Celsius', 'AllowPrefix' => false],
+        'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Celsius', 'AllowPrefix' => false],
+        'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
+        'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
+        'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Kelvin', 'AllowPrefix' => false],
+        'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Kelvin', 'AllowPrefix' => false],
+        'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Rankine', 'AllowPrefix' => false],
+        'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'UnitName' => 'Degrees Réaumur', 'AllowPrefix' => false],
         // Volume
-        'l' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
-        'L' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
-        'lt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
-        'tsp' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
-        'tspm' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Modern Teaspoon', 'AllowPrefix' => false],
-        'tbs' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
-        'oz' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
-        'cup' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cup', 'AllowPrefix' => false],
-        'pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
-        'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
-        'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
-        'qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Quart', 'AllowPrefix' => false],
-        'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
-        'gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
-        'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
-        'ang3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
-        'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
-        'barrel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Oil Barrel', 'AllowPrefix' => false],
-        'bushel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Bushel', 'AllowPrefix' => false],
-        'in3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
-        'in^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
-        'ft3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
-        'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
-        'ly3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
-        'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
-        'm3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
-        'm^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
-        'mi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
-        'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
-        'yd3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
-        'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
-        'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
-        'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
-        'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
-        'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
-        'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
-        'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
-        'GRT' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
-        'regton' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
-        'MTON' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
+        'l' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Litre', 'AllowPrefix' => true],
+        'L' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Litre', 'AllowPrefix' => true],
+        'lt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Litre', 'AllowPrefix' => true],
+        'tsp' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Teaspoon', 'AllowPrefix' => false],
+        'tspm' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Modern Teaspoon', 'AllowPrefix' => false],
+        'tbs' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Tablespoon', 'AllowPrefix' => false],
+        'oz' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Fluid Ounce', 'AllowPrefix' => false],
+        'cup' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cup', 'AllowPrefix' => false],
+        'pt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'U.S. Pint', 'AllowPrefix' => false],
+        'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'U.S. Pint', 'AllowPrefix' => false],
+        'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'U.K. Pint', 'AllowPrefix' => false],
+        'qt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Quart', 'AllowPrefix' => false],
+        'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
+        'gal' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Gallon', 'AllowPrefix' => false],
+        'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
+        'ang3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Angstrom', 'AllowPrefix' => true],
+        'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Angstrom', 'AllowPrefix' => true],
+        'barrel' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'US Oil Barrel', 'AllowPrefix' => false],
+        'bushel' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'US Bushel', 'AllowPrefix' => false],
+        'in3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Inch', 'AllowPrefix' => false],
+        'in^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Inch', 'AllowPrefix' => false],
+        'ft3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Foot', 'AllowPrefix' => false],
+        'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Foot', 'AllowPrefix' => false],
+        'ly3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Light Year', 'AllowPrefix' => false],
+        'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Light Year', 'AllowPrefix' => false],
+        'm3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Meter', 'AllowPrefix' => true],
+        'm^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Meter', 'AllowPrefix' => true],
+        'mi3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Mile', 'AllowPrefix' => false],
+        'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Mile', 'AllowPrefix' => false],
+        'yd3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Yard', 'AllowPrefix' => false],
+        'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Yard', 'AllowPrefix' => false],
+        'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
+        'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
+        'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
+        'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
+        'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
+        'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Cubic Pica', 'AllowPrefix' => false],
+        'GRT' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Gross Registered Ton', 'AllowPrefix' => false],
+        'regton' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Gross Registered Ton', 'AllowPrefix' => false],
+        'MTON' => ['Group' => self::CATEGORY_VOLUME, 'UnitName' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
         // Area
-        'ha' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Hectare', 'AllowPrefix' => true],
-        'uk_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'International Acre', 'AllowPrefix' => false],
-        'us_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
-        'ang2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
-        'ang^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
-        'ar' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Are', 'AllowPrefix' => true],
-        'ft2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
-        'ft^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
-        'in2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
-        'in^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
-        'ly2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
-        'ly^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
-        'm2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
-        'm^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
-        'Morgen' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Morgen', 'AllowPrefix' => false],
-        'mi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
-        'mi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
-        'Nmi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
-        'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
-        'Pica2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
-        'Pica^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
-        'Picapt2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
-        'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
-        'yd2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
-        'yd^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
+        'ha' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Hectare', 'AllowPrefix' => true],
+        'uk_acre' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'International Acre', 'AllowPrefix' => false],
+        'us_acre' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
+        'ang2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Angstrom', 'AllowPrefix' => true],
+        'ang^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Angstrom', 'AllowPrefix' => true],
+        'ar' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Are', 'AllowPrefix' => true],
+        'ft2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Feet', 'AllowPrefix' => false],
+        'ft^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Feet', 'AllowPrefix' => false],
+        'in2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Inches', 'AllowPrefix' => false],
+        'in^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Inches', 'AllowPrefix' => false],
+        'ly2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Light Years', 'AllowPrefix' => false],
+        'ly^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Light Years', 'AllowPrefix' => false],
+        'm2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Meters', 'AllowPrefix' => true],
+        'm^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Meters', 'AllowPrefix' => true],
+        'Morgen' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Morgen', 'AllowPrefix' => false],
+        'mi2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Miles', 'AllowPrefix' => false],
+        'mi^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Miles', 'AllowPrefix' => false],
+        'Nmi2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Nautical Miles', 'AllowPrefix' => false],
+        'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Nautical Miles', 'AllowPrefix' => false],
+        'Pica2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
+        'Pica^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
+        'Picapt2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
+        'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Pica', 'AllowPrefix' => false],
+        'yd2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Yards', 'AllowPrefix' => false],
+        'yd^2' => ['Group' => self::CATEGORY_AREA, 'UnitName' => 'Square Yards', 'AllowPrefix' => false],
         // Information
-        'byte' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Byte', 'AllowPrefix' => true],
-        'bit' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Bit', 'AllowPrefix' => true],
+        'byte' => ['Group' => self::CATEGORY_INFORMATION, 'UnitName' => 'Byte', 'AllowPrefix' => true],
+        'bit' => ['Group' => self::CATEGORY_INFORMATION, 'UnitName' => 'Bit', 'AllowPrefix' => true],
         // Speed
-        'm/s' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
-        'm/sec' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
-        'm/h' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
-        'm/hr' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
-        'mph' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Miles per hour', 'AllowPrefix' => false],
-        'admkn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Admiralty Knot', 'AllowPrefix' => false],
-        'kn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Knot', 'AllowPrefix' => false],
+        'm/s' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per second', 'AllowPrefix' => true],
+        'm/sec' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per second', 'AllowPrefix' => true],
+        'm/h' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per hour', 'AllowPrefix' => true],
+        'm/hr' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Meters per hour', 'AllowPrefix' => true],
+        'mph' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Miles per hour', 'AllowPrefix' => false],
+        'admkn' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Admiralty Knot', 'AllowPrefix' => false],
+        'kn' => ['Group' => self::CATEGORY_SPEED, 'UnitName' => 'Knot', 'AllowPrefix' => false],
     ];
 
     /**
      * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
      *
-     * @var mixed[]
+     * @var array<string, array{multiplier: float, name: string}>
      */
-    private static $conversionMultipliers = [
+    private static array $conversionMultipliers = [
         'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
         'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
         'E' => ['multiplier' => 1E18, 'name' => 'exa'],
@@ -230,9 +230,9 @@ class ConvertUOM
     /**
      * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
      *
-     * @var mixed[]
+     ** @var array<string, array{multiplier: float|int, name: string}>
      */
-    private static $binaryConversionMultipliers = [
+    private static array $binaryConversionMultipliers = [
         'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
         'Zi' => ['multiplier' => 2 ** 70, 'name' => 'zebi'],
         'Ei' => ['multiplier' => 2 ** 60, 'name' => 'exbi'],
@@ -246,9 +246,9 @@ class ConvertUOM
     /**
      * Details of the Units of measure conversion factors, organised by group.
      *
-     * @var mixed[]
+     * @var array<string, array<string, float>>
      */
-    private static $unitConversions = [
+    private static array $unitConversions = [
         // Conversion uses gram (g) as an intermediate unit
         self::CATEGORY_WEIGHT_AND_MASS => [
             'g' => 1.0,
@@ -435,10 +435,8 @@ class ConvertUOM
     /**
      *    getConversionGroups
      * Returns a list of the different conversion groups for UOM conversions.
-     *
-     * @return array
      */
-    public static function getConversionCategories()
+    public static function getConversionCategories(): array
     {
         $conversionGroups = [];
         foreach (self::$conversionUnits as $conversionUnit) {
@@ -452,11 +450,9 @@ class ConvertUOM
      *    getConversionGroupUnits
      * Returns an array of units of measure, for a specified conversion group, or for all groups.
      *
-     * @param string $category The group whose units of measure you want to retrieve
-     *
-     * @return array
+     * @param ?string $category The group whose units of measure you want to retrieve
      */
-    public static function getConversionCategoryUnits($category = null)
+    public static function getConversionCategoryUnits(?string $category = null): array
     {
         $conversionGroups = [];
         foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
@@ -471,18 +467,16 @@ class ConvertUOM
     /**
      * getConversionGroupUnitDetails.
      *
-     * @param string $category The group whose units of measure you want to retrieve
-     *
-     * @return array
+     * @param ?string $category The group whose units of measure you want to retrieve
      */
-    public static function getConversionCategoryUnitDetails($category = null)
+    public static function getConversionCategoryUnitDetails(?string $category = null): array
     {
         $conversionGroups = [];
         foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
             if (($category === null) || ($conversionGroup['Group'] == $category)) {
                 $conversionGroups[$conversionGroup['Group']][] = [
                     'unit' => $conversionUnit,
-                    'description' => $conversionGroup['Unit Name'],
+                    'description' => $conversionGroup['UnitName'],
                 ];
             }
         }
@@ -496,7 +490,7 @@ class ConvertUOM
      *
      * @return mixed[]
      */
-    public static function getConversionMultipliers()
+    public static function getConversionMultipliers(): array
     {
         return self::$conversionMultipliers;
     }
@@ -507,7 +501,7 @@ class ConvertUOM
      *
      * @return mixed[]
      */
-    public static function getBinaryConversionMultipliers()
+    public static function getBinaryConversionMultipliers(): array
     {
         return self::$binaryConversionMultipliers;
     }
@@ -546,7 +540,7 @@ class ConvertUOM
         try {
             [$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM);
             [$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM);
-        } catch (Exception $e) {
+        } catch (Exception) {
             return ExcelError::NA();
         }
 
@@ -564,7 +558,7 @@ class ConvertUOM
         } elseif ($fromUOM === $toUOM) {
             return $value / $toMultiplier;
         } elseif ($fromCategory === self::CATEGORY_TEMPERATURE) {
-            return self::convertTemperature($fromUOM, $toUOM, /** @scrutinizer ignore-type */ $value);
+            return self::convertTemperature($fromUOM, $toUOM, $value);
         }
 
         $baseValue = $value * (1.0 / self::$unitConversions[$fromCategory][$fromUOM]);
@@ -572,7 +566,7 @@ class ConvertUOM
         return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
     }
 
-    private static function getUOMDetails(string $uom)
+    private static function getUOMDetails(string $uom): array
     {
         if (isset(self::$conversionUnits[$uom])) {
             $unitCategory = self::$conversionUnits[$uom]['Group'];
@@ -621,12 +615,7 @@ class ConvertUOM
         throw new Exception('UoM Not Found');
     }
 
-    /**
-     * @param float|int $value
-     *
-     * @return float|int
-     */
-    protected static function convertTemperature(string $fromUOM, string $toUOM, $value)
+    protected static function convertTemperature(string $fromUOM, string $toUOM, float|int $value): float|int
     {
         $fromUOM = self::resolveTemperatureSynonyms($fromUOM);
         $toUOM = self::resolveTemperatureSynonyms($toUOM);
@@ -678,17 +667,13 @@ class ConvertUOM
         return $value;
     }
 
-    private static function resolveTemperatureSynonyms(string $uom)
+    private static function resolveTemperatureSynonyms(string $uom): string
     {
-        switch ($uom) {
-            case 'fah':
-                return 'F';
-            case 'cel':
-                return 'C';
-            case 'kel':
-                return 'K';
-        }
-
-        return $uom;
+        return match ($uom) {
+            'fah' => 'F',
+            'cel' => 'C',
+            'kel' => 'K',
+            default => $uom,
+        };
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
index c0202ea..7e5118f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php
@@ -7,10 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class EngineeringValidations
 {
-    /**
-     * @param mixed $value
-     */
-    public static function validateFloat($value): float
+    public static function validateFloat(mixed $value): float
     {
         if (!is_numeric($value)) {
             throw new Exception(ExcelError::VALUE());
@@ -19,10 +16,7 @@ class EngineeringValidations
         return (float) $value;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function validateInt($value): int
+    public static function validateInt(mixed $value): int
     {
         if (!is_numeric($value)) {
             throw new Exception(ExcelError::VALUE());
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php
index 6254d47..aee7e31 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php
@@ -10,7 +10,7 @@ class Erf
 {
     use ArrayEnabled;
 
-    private static $twoSqrtPi = 1.128379167095512574;
+    private const TWO_SQRT_PI = 1.128379167095512574;
 
     /**
      * ERF.
@@ -31,11 +31,10 @@ class Erf
      *                           If omitted, ERF integrates between zero and lower_limit
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function ERF(mixed $lower, mixed $upper = null): array|float|string
     {
         if (is_array($lower) || is_array($upper)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $lower, $upper);
@@ -64,11 +63,10 @@ class Erf
      * @param mixed $limit Float bound for integrating ERF, other bound is zero
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function ERFPRECISE(mixed $limit)
     {
         if (is_array($limit)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $limit);
@@ -77,13 +75,19 @@ class Erf
         return self::ERF($limit);
     }
 
-    //
-    //    Private method to calculate the erf value
-    //
-    public static function erfValue($value)
+    private static function makeFloat(mixed $value): float
     {
+        return is_numeric($value) ? ((float) $value) : 0.0;
+    }
+
+    /**
+     * Method to calculate the erf value.
+     */
+    public static function erfValue(float|int|string $value): float
+    {
+        $value = (float) $value;
         if (abs($value) > 2.2) {
-            return 1 - ErfC::ERFC($value);
+            return 1 - self::makeFloat(ErfC::ERFC($value));
         }
         $sum = $term = $value;
         $xsqr = ($value * $value);
@@ -100,6 +104,6 @@ class Erf
             }
         } while (abs($term / $sum) > Functions::PRECISION);
 
-        return self::$twoSqrtPi * $sum;
+        return self::TWO_SQRT_PI * $sum;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
index eb834b7..4365fec 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php
@@ -26,11 +26,10 @@ class ErfC
      * @param mixed $value The float lower bound for integrating ERFC
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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)
+    public static function ERFC(mixed $value)
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -43,13 +42,14 @@ class ErfC
         return ExcelError::VALUE();
     }
 
-    //
-    //    Private method to calculate the erfc value
-    //
-    private static $oneSqrtPi = 0.564189583547756287;
+    private const ONE_SQRT_PI = 0.564189583547756287;
 
-    private static function erfcValue($value)
+    /**
+     * Method to calculate the erfc value.
+     */
+    private static function erfcValue(float|int|string $value): float|int
     {
+        $value = (float) $value;
         if (abs($value) < 2.2) {
             return 1 - Erf::erfValue($value);
         }
@@ -72,6 +72,6 @@ class ErfC
             $q2 = $b / $d;
         } while ((abs($q1 - $q2) / $q2) > Functions::PRECISION);
 
-        return self::$oneSqrtPi * exp(-$value * $value) * $q2;
+        return self::ONE_SQRT_PI * exp(-$value * $value) * $q2;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php
index 0388f5a..ea893d6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Exception.php
@@ -6,16 +6,12 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 
 class Exception extends PhpSpreadsheetException
 {
+    public const CALCULATION_ENGINE_PUSH_TO_STACK = 1;
+
     /**
      * Error handler callback.
-     *
-     * @param mixed $code
-     * @param mixed $string
-     * @param mixed $file
-     * @param mixed $line
-     * @param mixed $context
      */
-    public static function errorHandlerCallback($code, $string, $file, $line, /** @scrutinizer ignore-unused */ $context): void
+    public static function errorHandlerCallback(int $code, string $string, string $file, int $line): void
     {
         $e = new self($string, $code);
         $e->line = $line;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php
index 6461961..890da60 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ExceptionHandler.php
@@ -9,7 +9,7 @@ class ExceptionHandler
      */
     public function __construct()
     {
-        /** @var callable */
+        /** @var callable $callable */
         $callable = [Exception::class, 'errorHandlerCallback'];
         set_error_handler($callable, E_ALL);
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
deleted file mode 100644
index 43e27c7..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial.php
+++ /dev/null
@@ -1,1391 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-use PhpOffice\PhpSpreadsheet\Calculation\Financial\Amortization;
-use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
-use PhpOffice\PhpSpreadsheet\Calculation\Financial\Depreciation;
-use PhpOffice\PhpSpreadsheet\Calculation\Financial\Dollar;
-use PhpOffice\PhpSpreadsheet\Calculation\Financial\InterestRate;
-use PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
-use PhpOffice\PhpSpreadsheet\Calculation\Financial\TreasuryBill;
-
-/**
- * @deprecated 1.18.0
- *
- * @codeCoverageIgnore
- */
-class Financial
-{
-    const FINANCIAL_MAX_ITERATIONS = 128;
-
-    const FINANCIAL_PRECISION = 1.0e-08;
-
-    /**
-     * ACCRINT.
-     *
-     * Returns the accrued interest for a security that pays periodic interest.
-     *
-     * Excel Function:
-     *        ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis][,calc_method])
-     *
-     * @deprecated 1.18.0
-     *      Use the periodic() method in the Financial\Securities\AccruedInterest class instead
-     * @see Securities\AccruedInterest::periodic()
-     *
-     * @param mixed $issue the security's issue date
-     * @param mixed $firstInterest the security's first interest date
-     * @param mixed $settlement The security's settlement date.
-     *                              The security settlement date is the date after the issue date
-     *                                  when the security is traded to the buyer.
-     * @param mixed $rate the security's annual coupon rate
-     * @param mixed $parValue The security's par value.
-     *                            If you omit par, ACCRINT uses $1,000.
-     * @param mixed $frequency The number of coupon payments per year.
-     *                             Valid frequency values are:
-     *                               1    Annual
-     *                               2    Semi-Annual
-     *                               4    Quarterly
-     * @param mixed $basis The type of day count to use.
-     *                         0 or omitted    US (NASD) 30/360
-     *                         1               Actual/actual
-     *                         2               Actual/360
-     *                         3               Actual/365
-     *                         4               European 30/360
-     * @param mixed $calcMethod
-     *                          If true, use Issue to Settlement
-     *                          If false, use FirstInterest to Settlement
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function ACCRINT(
-        $issue,
-        $firstInterest,
-        $settlement,
-        $rate,
-        $parValue = 1000,
-        $frequency = 1,
-        $basis = 0,
-        $calcMethod = true
-    ) {
-        return Securities\AccruedInterest::periodic(
-            $issue,
-            $firstInterest,
-            $settlement,
-            $rate,
-            $parValue,
-            $frequency,
-            $basis,
-            $calcMethod
-        );
-    }
-
-    /**
-     * ACCRINTM.
-     *
-     * Returns the accrued interest for a security that pays interest at maturity.
-     *
-     * Excel Function:
-     *        ACCRINTM(issue,settlement,rate[,par[,basis]])
-     *
-     * @deprecated 1.18.0
-     *      Use the atMaturity() method in the Financial\Securities\AccruedInterest class instead
-     * @see Financial\Securities\AccruedInterest::atMaturity()
-     *
-     * @param mixed $issue The security's issue date
-     * @param mixed $settlement The security's settlement (or maturity) date
-     * @param mixed $rate The security's annual coupon rate
-     * @param mixed $parValue The security's par value.
-     *                            If you omit par, ACCRINT uses $1,000.
-     * @param mixed $basis The type of day count to use.
-     *                            0 or omitted    US (NASD) 30/360
-     *                            1               Actual/actual
-     *                            2               Actual/360
-     *                            3               Actual/365
-     *                            4               European 30/360
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function ACCRINTM($issue, $settlement, $rate, $parValue = 1000, $basis = 0)
-    {
-        return Securities\AccruedInterest::atMaturity($issue, $settlement, $rate, $parValue, $basis);
-    }
-
-    /**
-     * AMORDEGRC.
-     *
-     * Returns the depreciation for each accounting period.
-     * This function is provided for the French accounting system. If an asset is purchased in
-     * the middle of the accounting period, the prorated depreciation is taken into account.
-     * The function is similar to AMORLINC, except that a depreciation coefficient is applied in
-     * the calculation depending on the life of the assets.
-     * This function will return the depreciation until the last period of the life of the assets
-     * or until the cumulated value of depreciation is greater than the cost of the assets minus
-     * the salvage value.
-     *
-     * Excel Function:
-     *        AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the AMORDEGRC() method in the Financial\Amortization class instead
-     * @see Financial\Amortization::AMORDEGRC()
-     *
-     * @param float $cost The cost of the asset
-     * @param mixed $purchased Date of the purchase of the asset
-     * @param mixed $firstPeriod Date of the end of the first period
-     * @param mixed $salvage The salvage value at the end of the life of the asset
-     * @param float $period The period
-     * @param float $rate Rate of depreciation
-     * @param int $basis The type of day count to use.
-     *                       0 or omitted    US (NASD) 30/360
-     *                       1                Actual/actual
-     *                       2                Actual/360
-     *                       3                Actual/365
-     *                       4                European 30/360
-     *
-     * @return float|string (string containing the error type if there is an error)
-     */
-    public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
-    {
-        return Amortization::AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
-    }
-
-    /**
-     * AMORLINC.
-     *
-     * Returns the depreciation for each accounting period.
-     * This function is provided for the French accounting system. If an asset is purchased in
-     * the middle of the accounting period, the prorated depreciation is taken into account.
-     *
-     * Excel Function:
-     *        AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the AMORLINC() method in the Financial\Amortization class instead
-     * @see Financial\Amortization::AMORLINC()
-     *
-     * @param float $cost The cost of the asset
-     * @param mixed $purchased Date of the purchase of the asset
-     * @param mixed $firstPeriod Date of the end of the first period
-     * @param mixed $salvage The salvage value at the end of the life of the asset
-     * @param float $period The period
-     * @param float $rate Rate of depreciation
-     * @param int $basis The type of day count to use.
-     *                       0 or omitted    US (NASD) 30/360
-     *                       1               Actual/actual
-     *                       2               Actual/360
-     *                       3               Actual/365
-     *                       4               European 30/360
-     *
-     * @return float|string (string containing the error type if there is an error)
-     */
-    public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
-    {
-        return Amortization::AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
-    }
-
-    /**
-     * COUPDAYBS.
-     *
-     * Returns the number of days from the beginning of the coupon period to the settlement date.
-     *
-     * Excel Function:
-     *        COUPDAYBS(settlement,maturity,frequency[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the COUPDAYBS() method in the Financial\Coupons class instead
-     * @see Financial\Coupons::COUPDAYBS()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                                The security settlement date is the date after the issue
-     *                                date when the security is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param int $frequency the number of coupon payments per year.
-     *                                    Valid frequency values are:
-     *                                        1    Annual
-     *                                        2    Semi-Annual
-     *                                        4    Quarterly
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return float|string
-     */
-    public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
-    {
-        return Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
-    }
-
-    /**
-     * COUPDAYS.
-     *
-     * Returns the number of days in the coupon period that contains the settlement date.
-     *
-     * Excel Function:
-     *        COUPDAYS(settlement,maturity,frequency[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the COUPDAYS() method in the Financial\Coupons class instead
-     * @see Financial\Coupons::COUPDAYS()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                                The security settlement date is the date after the issue
-     *                                date when the security is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param mixed $frequency the number of coupon payments per year.
-     *                                    Valid frequency values are:
-     *                                        1    Annual
-     *                                        2    Semi-Annual
-     *                                        4    Quarterly
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return float|string
-     */
-    public static function COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
-    {
-        return Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
-    }
-
-    /**
-     * COUPDAYSNC.
-     *
-     * Returns the number of days from the settlement date to the next coupon date.
-     *
-     * Excel Function:
-     *        COUPDAYSNC(settlement,maturity,frequency[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the COUPDAYSNC() method in the Financial\Coupons class instead
-     * @see Financial\Coupons::COUPDAYSNC()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                                The security settlement date is the date after the issue
-     *                                date when the security is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param mixed $frequency the number of coupon payments per year.
-     *                                    Valid frequency values are:
-     *                                        1    Annual
-     *                                        2    Semi-Annual
-     *                                        4    Quarterly
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return float|string
-     */
-    public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
-    {
-        return Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
-    }
-
-    /**
-     * COUPNCD.
-     *
-     * Returns the next coupon date after the settlement date.
-     *
-     * Excel Function:
-     *        COUPNCD(settlement,maturity,frequency[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the COUPNCD() method in the Financial\Coupons class instead
-     * @see Financial\Coupons::COUPNCD()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                                The security settlement date is the date after the issue
-     *                                date when the security is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param mixed $frequency the number of coupon payments per year.
-     *                                    Valid frequency values are:
-     *                                        1    Annual
-     *                                        2    Semi-Annual
-     *                                        4    Quarterly
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @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
-     */
-    public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
-    {
-        return Coupons::COUPNCD($settlement, $maturity, $frequency, $basis);
-    }
-
-    /**
-     * COUPNUM.
-     *
-     * Returns the number of coupons payable between the settlement date and maturity date,
-     * rounded up to the nearest whole coupon.
-     *
-     * Excel Function:
-     *        COUPNUM(settlement,maturity,frequency[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the COUPNUM() method in the Financial\Coupons class instead
-     * @see Financial\Coupons::COUPNUM()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                                The security settlement date is the date after the issue
-     *                                date when the security is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param mixed $frequency the number of coupon payments per year.
-     *                                    Valid frequency values are:
-     *                                        1    Annual
-     *                                        2    Semi-Annual
-     *                                        4    Quarterly
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return int|string
-     */
-    public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
-    {
-        return Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
-    }
-
-    /**
-     * COUPPCD.
-     *
-     * Returns the previous coupon date before the settlement date.
-     *
-     * Excel Function:
-     *        COUPPCD(settlement,maturity,frequency[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the COUPPCD() method in the Financial\Coupons class instead
-     * @see Financial\Coupons::COUPPCD()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                                The security settlement date is the date after the issue
-     *                                date when the security is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param mixed $frequency the number of coupon payments per year.
-     *                                    Valid frequency values are:
-     *                                        1    Annual
-     *                                        2    Semi-Annual
-     *                                        4    Quarterly
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @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
-     */
-    public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
-    {
-        return Coupons::COUPPCD($settlement, $maturity, $frequency, $basis);
-    }
-
-    /**
-     * CUMIPMT.
-     *
-     * Returns the cumulative interest paid on a loan between the start and end periods.
-     *
-     * Excel Function:
-     *        CUMIPMT(rate,nper,pv,start,end[,type])
-     *
-     * @deprecated 1.18.0
-     *      Use the interest() method in the Financial\CashFlow\Constant\Periodic\Cumulative class instead
-     * @see Financial\CashFlow\Constant\Periodic\Cumulative::interest()
-     *
-     * @param float $rate The Interest rate
-     * @param int $nper The total number of payment periods
-     * @param float $pv Present Value
-     * @param int $start The first period in the calculation.
-     *                       Payment periods are numbered beginning with 1.
-     * @param int $end the last period in the calculation
-     * @param int $type A number 0 or 1 and indicates when payments are due:
-     *                    0 or omitted    At the end of the period.
-     *                    1               At the beginning of the period.
-     *
-     * @return float|string
-     */
-    public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic\Cumulative::interest($rate, $nper, $pv, $start, $end, $type);
-    }
-
-    /**
-     * CUMPRINC.
-     *
-     * Returns the cumulative principal paid on a loan between the start and end periods.
-     *
-     * Excel Function:
-     *        CUMPRINC(rate,nper,pv,start,end[,type])
-     *
-     * @deprecated 1.18.0
-     *      Use the principal() method in the Financial\CashFlow\Constant\Periodic\Cumulative class instead
-     * @see Financial\CashFlow\Constant\Periodic\Cumulative::principal()
-     *
-     * @param float $rate The Interest rate
-     * @param int $nper The total number of payment periods
-     * @param float $pv Present Value
-     * @param int $start The first period in the calculation.
-     *                       Payment periods are numbered beginning with 1.
-     * @param int $end the last period in the calculation
-     * @param int $type A number 0 or 1 and indicates when payments are due:
-     *                    0 or omitted    At the end of the period.
-     *                    1               At the beginning of the period.
-     *
-     * @return float|string
-     */
-    public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic\Cumulative::principal($rate, $nper, $pv, $start, $end, $type);
-    }
-
-    /**
-     * DB.
-     *
-     * Returns the depreciation of an asset for a specified period using the
-     * fixed-declining balance method.
-     * This form of depreciation is used if you want to get a higher depreciation value
-     * at the beginning of the depreciation (as opposed to linear depreciation). The
-     * depreciation value is reduced with every depreciation period by the depreciation
-     * already deducted from the initial cost.
-     *
-     * Excel Function:
-     *        DB(cost,salvage,life,period[,month])
-     *
-     * @deprecated 1.18.0
-     *      Use the DB() method in the Financial\Depreciation class instead
-     * @see Financial\Depreciation::DB()
-     *
-     * @param float $cost Initial cost of the asset
-     * @param float $salvage Value at the end of the depreciation.
-     *                                (Sometimes called the salvage value of the asset)
-     * @param int $life Number of periods over which the asset is depreciated.
-     *                                (Sometimes called the useful life of the asset)
-     * @param int $period The period for which you want to calculate the
-     *                                depreciation. Period must use the same units as life.
-     * @param int $month Number of months in the first year. If month is omitted,
-     *                                it defaults to 12.
-     *
-     * @return float|string
-     */
-    public static function DB($cost, $salvage, $life, $period, $month = 12)
-    {
-        return Depreciation::DB($cost, $salvage, $life, $period, $month);
-    }
-
-    /**
-     * DDB.
-     *
-     * Returns the depreciation of an asset for a specified period using the
-     * double-declining balance method or some other method you specify.
-     *
-     * Excel Function:
-     *        DDB(cost,salvage,life,period[,factor])
-     *
-     * @deprecated 1.18.0
-     *      Use the DDB() method in the Financial\Depreciation class instead
-     * @see Financial\Depreciation::DDB()
-     *
-     * @param float $cost Initial cost of the asset
-     * @param float $salvage Value at the end of the depreciation.
-     *                                (Sometimes called the salvage value of the asset)
-     * @param int $life Number of periods over which the asset is depreciated.
-     *                                (Sometimes called the useful life of the asset)
-     * @param int $period The period for which you want to calculate the
-     *                                depreciation. Period must use the same units as life.
-     * @param float $factor The rate at which the balance declines.
-     *                                If factor is omitted, it is assumed to be 2 (the
-     *                                double-declining balance method).
-     *
-     * @return float|string
-     */
-    public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
-    {
-        return Depreciation::DDB($cost, $salvage, $life, $period, $factor);
-    }
-
-    /**
-     * DISC.
-     *
-     * Returns the discount rate for a security.
-     *
-     * Excel Function:
-     *        DISC(settlement,maturity,price,redemption[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the discount() method in the Financial\Securities\Rates class instead
-     * @see Financial\Securities\Rates::discount()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                                The security settlement date is the date after the issue
-     *                                date when the security is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param int $price The security's price per $100 face value
-     * @param int $redemption The security's redemption value per $100 face value
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return float|string
-     */
-    public static function DISC($settlement, $maturity, $price, $redemption, $basis = 0)
-    {
-        return Financial\Securities\Rates::discount($settlement, $maturity, $price, $redemption, $basis);
-    }
-
-    /**
-     * DOLLARDE.
-     *
-     * Converts a dollar price expressed as an integer part and a fraction
-     *        part into a dollar price expressed as a decimal number.
-     * Fractional dollar numbers are sometimes used for security prices.
-     *
-     * Excel Function:
-     *        DOLLARDE(fractional_dollar,fraction)
-     *
-     * @deprecated 1.18.0
-     *      Use the decimal() method in the Financial\Dollar class instead
-     * @see Financial\Dollar::decimal()
-     *
-     * @param array|float $fractional_dollar Fractional Dollar
-     * @param array|int $fraction Fraction
-     *
-     * @return array|float|string
-     */
-    public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
-    {
-        return Dollar::decimal($fractional_dollar, $fraction);
-    }
-
-    /**
-     * DOLLARFR.
-     *
-     * Converts a dollar price expressed as a decimal number into a dollar price
-     *        expressed as a fraction.
-     * Fractional dollar numbers are sometimes used for security prices.
-     *
-     * Excel Function:
-     *        DOLLARFR(decimal_dollar,fraction)
-     *
-     * @deprecated 1.18.0
-     *      Use the fractional() method in the Financial\Dollar class instead
-     * @see Financial\Dollar::fractional()
-     *
-     * @param array|float $decimal_dollar Decimal Dollar
-     * @param array|int $fraction Fraction
-     *
-     * @return array|float|string
-     */
-    public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
-    {
-        return Dollar::fractional($decimal_dollar, $fraction);
-    }
-
-    /**
-     * EFFECT.
-     *
-     * Returns the effective interest rate given the nominal rate and the number of
-     *        compounding payments per year.
-     *
-     * Excel Function:
-     *        EFFECT(nominal_rate,npery)
-     *
-     * @deprecated 1.18.0
-     *      Use the effective() method in the Financial\InterestRate class instead
-     * @see Financial\InterestRate::effective()
-     *
-     * @param float $nominalRate Nominal interest rate
-     * @param int $periodsPerYear Number of compounding payments per year
-     *
-     * @return float|string
-     */
-    public static function EFFECT($nominalRate = 0, $periodsPerYear = 0)
-    {
-        return Financial\InterestRate::effective($nominalRate, $periodsPerYear);
-    }
-
-    /**
-     * FV.
-     *
-     * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
-     *
-     * Excel Function:
-     *        FV(rate,nper,pmt[,pv[,type]])
-     *
-     * @deprecated 1.18.0
-     *      Use the futureValue() method in the Financial\CashFlow\Constant\Periodic class instead
-     * @see Financial\CashFlow\Constant\Periodic::futureValue()
-     *
-     * @param float $rate The interest rate per period
-     * @param int $nper Total number of payment periods in an annuity
-     * @param float $pmt The payment made each period: it cannot change over the
-     *                            life of the annuity. Typically, pmt contains principal
-     *                            and interest but no other fees or taxes.
-     * @param float $pv present Value, or the lump-sum amount that a series of
-     *                            future payments is worth right now
-     * @param int $type A number 0 or 1 and indicates when payments are due:
-     *                                0 or omitted    At the end of the period.
-     *                                1                At the beginning of the period.
-     *
-     * @return float|string
-     */
-    public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic::futureValue($rate, $nper, $pmt, $pv, $type);
-    }
-
-    /**
-     * FVSCHEDULE.
-     *
-     * Returns the future value of an initial principal after applying a series of compound interest rates.
-     * Use FVSCHEDULE to calculate the future value of an investment with a variable or adjustable rate.
-     *
-     * Excel Function:
-     *        FVSCHEDULE(principal,schedule)
-     *
-     * @deprecated 1.18.0
-     *      Use the futureValue() method in the Financial\CashFlow\Single class instead
-     * @see Financial\CashFlow\Single::futureValue()
-     *
-     * @param float $principal the present value
-     * @param float[] $schedule an array of interest rates to apply
-     *
-     * @return float|string
-     */
-    public static function FVSCHEDULE($principal, $schedule)
-    {
-        return Financial\CashFlow\Single::futureValue($principal, $schedule);
-    }
-
-    /**
-     * INTRATE.
-     *
-     * Returns the interest rate for a fully invested security.
-     *
-     * Excel Function:
-     *        INTRATE(settlement,maturity,investment,redemption[,basis])
-     *
-     * @deprecated 1.18.0
-     *      Use the interest() method in the Financial\Securities\Rates class instead
-     * @see Financial\Securities\Rates::interest()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                              The security settlement date is the date after the issue date when the security
-     *                                  is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                            The maturity date is the date when the security expires.
-     * @param int $investment the amount invested in the security
-     * @param int $redemption the amount to be received at maturity
-     * @param int $basis The type of day count to use.
-     *                       0 or omitted    US (NASD) 30/360
-     *                       1               Actual/actual
-     *                       2               Actual/360
-     *                       3               Actual/365
-     *                       4               European 30/360
-     *
-     * @return float|string
-     */
-    public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis = 0)
-    {
-        return Financial\Securities\Rates::interest($settlement, $maturity, $investment, $redemption, $basis);
-    }
-
-    /**
-     * IPMT.
-     *
-     * Returns the interest payment for a given period for an investment based on periodic, constant payments
-     *         and a constant interest rate.
-     *
-     * Excel Function:
-     *        IPMT(rate,per,nper,pv[,fv][,type])
-     *
-     * @deprecated 1.18.0
-     *      Use the payment() method in the Financial\CashFlow\Constant\Periodic\Interest class instead
-     * @see Financial\CashFlow\Constant\Periodic\Interest::payment()
-     *
-     * @param float $rate Interest rate per period
-     * @param int $per Period for which we want to find the interest
-     * @param int $nper Number of periods
-     * @param float $pv Present Value
-     * @param float $fv Future Value
-     * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
-     *
-     * @return float|string
-     */
-    public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic\Interest::payment($rate, $per, $nper, $pv, $fv, $type);
-    }
-
-    /**
-     * IRR.
-     *
-     * Returns the internal rate of return for a series of cash flows represented by the numbers in values.
-     * These cash flows do not have to be even, as they would be for an annuity. However, the cash flows must occur
-     * at regular intervals, such as monthly or annually. The internal rate of return is the interest rate received
-     * for an investment consisting of payments (negative values) and income (positive values) that occur at regular
-     * periods.
-     *
-     * Excel Function:
-     *        IRR(values[,guess])
-     *
-     * @deprecated 1.18.0
-     *      Use the rate() method in the Financial\CashFlow\Variable\Periodic class instead
-     * @see Financial\CashFlow\Variable\Periodic::rate()
-     *
-     * @param mixed $values An array or a reference to cells that contain numbers for which you want
-     *                                    to calculate the internal rate of return.
-     *                                Values must contain at least one positive value and one negative value to
-     *                                    calculate the internal rate of return.
-     * @param mixed $guess A number that you guess is close to the result of IRR
-     *
-     * @return float|string
-     */
-    public static function IRR($values, $guess = 0.1)
-    {
-        return Financial\CashFlow\Variable\Periodic::rate($values, $guess);
-    }
-
-    /**
-     * ISPMT.
-     *
-     * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
-     *
-     * Excel Function:
-     *     =ISPMT(interest_rate, period, number_payments, pv)
-     *
-     * @deprecated 1.18.0
-     *      Use the schedulePayment() method in the Financial\CashFlow\Constant\Periodic\Interest class instead
-     * @see Financial\CashFlow\Constant\Periodic\Interest::schedulePayment()
-     *
-     * interest_rate is the interest rate for the investment
-     *
-     * period is the period to calculate the interest rate.  It must be betweeen 1 and number_payments.
-     *
-     * number_payments is the number of payments for the annuity
-     *
-     * pv is the loan amount or present value of the payments
-     *
-     * @param array $args
-     *
-     * @return float|string
-     */
-    public static function ISPMT(...$args)
-    {
-        return Financial\CashFlow\Constant\Periodic\Interest::schedulePayment(...$args);
-    }
-
-    /**
-     * MIRR.
-     *
-     * Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both
-     *        the cost of the investment and the interest received on reinvestment of cash.
-     *
-     * Excel Function:
-     *        MIRR(values,finance_rate, reinvestment_rate)
-     *
-     * @deprecated 1.18.0
-     *      Use the modifiedRate() method in the Financial\CashFlow\Variable\Periodic class instead
-     * @see Financial\CashFlow\Variable\Periodic::modifiedRate()
-     *
-     * @param mixed $values An array or a reference to cells that contain a series of payments and
-     *                         income occurring at regular intervals.
-     *                      Payments are negative value, income is positive values.
-     * @param mixed $finance_rate The interest rate you pay on the money used in the cash flows
-     * @param mixed $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function MIRR($values, $finance_rate, $reinvestment_rate)
-    {
-        return Financial\CashFlow\Variable\Periodic::modifiedRate($values, $finance_rate, $reinvestment_rate);
-    }
-
-    /**
-     * NOMINAL.
-     *
-     * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
-     *
-     * Excel Function:
-     *        NOMINAL(effect_rate, npery)
-     *
-     * @deprecated 1.18.0
-     *      Use the nominal() method in the Financial\InterestRate class instead
-     * @see Financial\InterestRate::nominal()
-     *
-     * @param float $effectiveRate Effective interest rate
-     * @param int $periodsPerYear Number of compounding payments per year
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function NOMINAL($effectiveRate = 0, $periodsPerYear = 0)
-    {
-        return InterestRate::nominal($effectiveRate, $periodsPerYear);
-    }
-
-    /**
-     * NPER.
-     *
-     * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
-     *
-     * @deprecated 1.18.0
-     *      Use the periods() method in the Financial\CashFlow\Constant\Periodic class instead
-     * @see Financial\CashFlow\Constant\Periodic::periods()
-     *
-     * @param float $rate Interest rate per period
-     * @param int $pmt Periodic payment (annuity)
-     * @param float $pv Present Value
-     * @param float $fv Future Value
-     * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic::periods($rate, $pmt, $pv, $fv, $type);
-    }
-
-    /**
-     * NPV.
-     *
-     * Returns the Net Present Value of a cash flow series given a discount rate.
-     *
-     * @deprecated 1.18.0
-     *      Use the presentValue() method in the Financial\CashFlow\Variable\Periodic class instead
-     * @see Financial\CashFlow\Variable\Periodic::presentValue()
-     *
-     * @param array $args
-     *
-     * @return float
-     */
-    public static function NPV(...$args)
-    {
-        return Financial\CashFlow\Variable\Periodic::presentValue(...$args);
-    }
-
-    /**
-     * PDURATION.
-     *
-     * Calculates the number of periods required for an investment to reach a specified value.
-     *
-     * @deprecated 1.18.0
-     *      Use the periods() method in the Financial\CashFlow\Single class instead
-     * @see Financial\CashFlow\Single::periods()
-     *
-     * @param float $rate Interest rate per period
-     * @param float $pv Present Value
-     * @param float $fv Future Value
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function PDURATION($rate = 0, $pv = 0, $fv = 0)
-    {
-        return Financial\CashFlow\Single::periods($rate, $pv, $fv);
-    }
-
-    /**
-     * PMT.
-     *
-     * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
-     *
-     * @deprecated 1.18.0
-     *      Use the annuity() method in the Financial\CashFlow\Constant\Periodic\Payments class instead
-     * @see Financial\CashFlow\Constant\Periodic\Payments::annuity()
-     *
-     * @param float $rate Interest rate per period
-     * @param int $nper Number of periods
-     * @param float $pv Present Value
-     * @param float $fv Future Value
-     * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic\Payments::annuity($rate, $nper, $pv, $fv, $type);
-    }
-
-    /**
-     * PPMT.
-     *
-     * Returns the interest payment for a given period for an investment based on periodic, constant payments
-     *         and a constant interest rate.
-     *
-     * @deprecated 1.18.0
-     *      Use the interestPayment() method in the Financial\CashFlow\Constant\Periodic\Payments class instead
-     * @see Financial\CashFlow\Constant\Periodic\Payments::interestPayment()
-     *
-     * @param float $rate Interest rate per period
-     * @param int $per Period for which we want to find the interest
-     * @param int $nper Number of periods
-     * @param float $pv Present Value
-     * @param float $fv Future Value
-     * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic\Payments::interestPayment($rate, $per, $nper, $pv, $fv, $type);
-    }
-
-    /**
-     * PRICE.
-     *
-     * Returns the price per $100 face value of a security that pays periodic interest.
-     *
-     * @deprecated 1.18.0
-     *      Use the price() method in the Financial\Securities\Price class instead
-     * @see Financial\Securities\Price::price()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                              The security settlement date is the date after the issue date when the security
-     *                              is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                                The maturity date is the date when the security expires.
-     * @param float $rate the security's annual coupon rate
-     * @param float $yield the security's annual yield
-     * @param float $redemption The number of coupon payments per year.
-     *                              For annual payments, frequency = 1;
-     *                              for semiannual, frequency = 2;
-     *                              for quarterly, frequency = 4.
-     * @param int $frequency
-     * @param int $basis The type of day count to use.
-     *                       0 or omitted    US (NASD) 30/360
-     *                       1                Actual/actual
-     *                       2                Actual/360
-     *                       3                Actual/365
-     *                       4                European 30/360
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0)
-    {
-        return Securities\Price::price($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis);
-    }
-
-    /**
-     * PRICEDISC.
-     *
-     * Returns the price per $100 face value of a discounted security.
-     *
-     * @deprecated 1.18.0
-     *      Use the priceDiscounted() method in the Financial\Securities\Price class instead
-     * @see Financial\Securities\Price::priceDiscounted()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                              The security settlement date is the date after the issue date when the security
-     *                              is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                            The maturity date is the date when the security expires.
-     * @param int $discount The security's discount rate
-     * @param int $redemption The security's redemption value per $100 face value
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
-    {
-        return Securities\Price::priceDiscounted($settlement, $maturity, $discount, $redemption, $basis);
-    }
-
-    /**
-     * PRICEMAT.
-     *
-     * Returns the price per $100 face value of a security that pays interest at maturity.
-     *
-     * @deprecated 1.18.0
-     *      Use the priceAtMaturity() method in the Financial\Securities\Price class instead
-     * @see Financial\Securities\Price::priceAtMaturity()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                              The security's settlement date is the date after the issue date when the security
-     *                              is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                            The maturity date is the date when the security expires.
-     * @param mixed $issue The security's issue date
-     * @param int $rate The security's interest rate at date of issue
-     * @param int $yield The security's annual yield
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
-    {
-        return Securities\Price::priceAtMaturity($settlement, $maturity, $issue, $rate, $yield, $basis);
-    }
-
-    /**
-     * PV.
-     *
-     * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
-     *
-     * @deprecated 1.18.0
-     *      Use the presentValue() method in the Financial\CashFlow\Constant\Periodic class instead
-     * @see Financial\CashFlow\Constant\Periodic::presentValue()
-     *
-     * @param float $rate Interest rate per period
-     * @param int $nper Number of periods
-     * @param float $pmt Periodic payment (annuity)
-     * @param float $fv Future Value
-     * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
-    {
-        return Financial\CashFlow\Constant\Periodic::presentValue($rate, $nper, $pmt, $fv, $type);
-    }
-
-    /**
-     * RATE.
-     *
-     * Returns the interest rate per period of an annuity.
-     * RATE is calculated by iteration and can have zero or more solutions.
-     * If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
-     * RATE returns the #NUM! error value.
-     *
-     * Excel Function:
-     *        RATE(nper,pmt,pv[,fv[,type[,guess]]])
-     *
-     * @deprecated 1.18.0
-     *      Use the rate() method in the Financial\CashFlow\Constant\Periodic\Interest class instead
-     * @see Financial\CashFlow\Constant\Periodic\Interest::rate()
-     *
-     * @param mixed $nper The total number of payment periods in an annuity
-     * @param mixed $pmt The payment made each period and cannot change over the life
-     *                                    of the annuity.
-     *                                Typically, pmt includes principal and interest but no other
-     *                                    fees or taxes.
-     * @param mixed $pv The present value - the total amount that a series of future
-     *                                    payments is worth now
-     * @param mixed $fv The future value, or a cash balance you want to attain after
-     *                                    the last payment is made. If fv is omitted, it is assumed
-     *                                    to be 0 (the future value of a loan, for example, is 0).
-     * @param mixed $type A number 0 or 1 and indicates when payments are due:
-     *                                        0 or omitted    At the end of the period.
-     *                                        1                At the beginning of the period.
-     * @param mixed $guess Your guess for what the rate will be.
-     *                                    If you omit guess, it is assumed to be 10 percent.
-     *
-     * @return float|string
-     */
-    public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
-    {
-        return Financial\CashFlow\Constant\Periodic\Interest::rate($nper, $pmt, $pv, $fv, $type, $guess);
-    }
-
-    /**
-     * RECEIVED.
-     *
-     * Returns the amount received at maturity for a fully invested Security.
-     *
-     * @deprecated 1.18.0
-     *      Use the received() method in the Financial\Securities\Price class instead
-     * @see Financial\Securities\Price::received()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                              The security settlement date is the date after the issue date when the security
-     *                                  is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                            The maturity date is the date when the security expires.
-     * @param mixed $investment The amount invested in the security
-     * @param mixed $discount The security's discount rate
-     * @param mixed $basis The type of day count to use.
-     *                         0 or omitted    US (NASD) 30/360
-     *                         1               Actual/actual
-     *                         2               Actual/360
-     *                         3               Actual/365
-     *                         4               European 30/360
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0)
-    {
-        return Financial\Securities\Price::received($settlement, $maturity, $investment, $discount, $basis);
-    }
-
-    /**
-     * RRI.
-     *
-     * Calculates the interest rate required for an investment to grow to a specified future value .
-     *
-     * @deprecated 1.18.0
-     *      Use the interestRate() method in the Financial\CashFlow\Single class instead
-     * @see Financial\CashFlow\Single::interestRate()
-     *
-     * @param float $nper The number of periods over which the investment is made
-     * @param float $pv Present Value
-     * @param float $fv Future Value
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function RRI($nper = 0, $pv = 0, $fv = 0)
-    {
-        return Financial\CashFlow\Single::interestRate($nper, $pv, $fv);
-    }
-
-    /**
-     * SLN.
-     *
-     * Returns the straight-line depreciation of an asset for one period
-     *
-     * @deprecated 1.18.0
-     *      Use the SLN() method in the Financial\Depreciation class instead
-     * @see Financial\Depreciation::SLN()
-     *
-     * @param mixed $cost Initial cost of the asset
-     * @param mixed $salvage Value at the end of the depreciation
-     * @param mixed $life Number of periods over which the asset is depreciated
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function SLN($cost, $salvage, $life)
-    {
-        return Depreciation::SLN($cost, $salvage, $life);
-    }
-
-    /**
-     * SYD.
-     *
-     * Returns the sum-of-years' digits depreciation of an asset for a specified period.
-     *
-     * @deprecated 1.18.0
-     *      Use the SYD() method in the Financial\Depreciation class instead
-     * @see Financial\Depreciation::SYD()
-     *
-     * @param mixed $cost Initial cost of the asset
-     * @param mixed $salvage Value at the end of the depreciation
-     * @param mixed $life Number of periods over which the asset is depreciated
-     * @param mixed $period Period
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function SYD($cost, $salvage, $life, $period)
-    {
-        return Depreciation::SYD($cost, $salvage, $life, $period);
-    }
-
-    /**
-     * TBILLEQ.
-     *
-     * Returns the bond-equivalent yield for a Treasury bill.
-     *
-     * @deprecated 1.18.0
-     *      Use the bondEquivalentYield() method in the Financial\TreasuryBill class instead
-     * @see Financial\TreasuryBill::bondEquivalentYield()
-     *
-     * @param mixed $settlement The Treasury bill's settlement date.
-     *                          The Treasury bill's settlement date is the date after the issue date when the
-     *                              Treasury bill is traded to the buyer.
-     * @param mixed $maturity The Treasury bill's maturity date.
-     *                                The maturity date is the date when the Treasury bill expires.
-     * @param int $discount The Treasury bill's discount rate
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function TBILLEQ($settlement, $maturity, $discount)
-    {
-        return TreasuryBill::bondEquivalentYield($settlement, $maturity, $discount);
-    }
-
-    /**
-     * TBILLPRICE.
-     *
-     * Returns the price per $100 face value for a Treasury bill.
-     *
-     * @deprecated 1.18.0
-     *      Use the price() method in the Financial\TreasuryBill class instead
-     * @see Financial\TreasuryBill::price()
-     *
-     * @param mixed $settlement The Treasury bill's settlement date.
-     *                                The Treasury bill's settlement date is the date after the issue date
-     *                                    when the Treasury bill is traded to the buyer.
-     * @param mixed $maturity The Treasury bill's maturity date.
-     *                                The maturity date is the date when the Treasury bill expires.
-     * @param int $discount The Treasury bill's discount rate
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function TBILLPRICE($settlement, $maturity, $discount)
-    {
-        return TreasuryBill::price($settlement, $maturity, $discount);
-    }
-
-    /**
-     * TBILLYIELD.
-     *
-     * Returns the yield for a Treasury bill.
-     *
-     * @deprecated 1.18.0
-     *      Use the yield() method in the Financial\TreasuryBill class instead
-     * @see Financial\TreasuryBill::yield()
-     *
-     * @param mixed $settlement The Treasury bill's settlement date.
-     *                                The Treasury bill's settlement date is the date after the issue date
-     *                                    when the Treasury bill is traded to the buyer.
-     * @param mixed $maturity The Treasury bill's maturity date.
-     *                                The maturity date is the date when the Treasury bill expires.
-     * @param int $price The Treasury bill's price per $100 face value
-     *
-     * @return float|mixed|string
-     */
-    public static function TBILLYIELD($settlement, $maturity, $price)
-    {
-        return TreasuryBill::yield($settlement, $maturity, $price);
-    }
-
-    /**
-     * XIRR.
-     *
-     * Returns the internal rate of return for a schedule of cash flows that is not necessarily periodic.
-     *
-     * Excel Function:
-     *        =XIRR(values,dates,guess)
-     *
-     * @deprecated 1.18.0
-     *      Use the rate() method in the Financial\CashFlow\Variable\NonPeriodic class instead
-     * @see Financial\CashFlow\Variable\NonPeriodic::rate()
-     *
-     * @param float[] $values     A series of cash flow payments
-     *                                The series of values must contain at least one positive value & one negative value
-     * @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
-     *
-     * @return float|mixed|string
-     */
-    public static function XIRR($values, $dates, $guess = 0.1)
-    {
-        return Financial\CashFlow\Variable\NonPeriodic::rate($values, $dates, $guess);
-    }
-
-    /**
-     * XNPV.
-     *
-     * Returns the net present value for a schedule of cash flows that is not necessarily periodic.
-     * To calculate the net present value for a series of cash flows that is periodic, use the NPV function.
-     *
-     * Excel Function:
-     *        =XNPV(rate,values,dates)
-     *
-     * @deprecated 1.18.0
-     *      Use the presentValue() method in the Financial\CashFlow\Variable\NonPeriodic class instead
-     * @see Financial\CashFlow\Variable\NonPeriodic::presentValue()
-     *
-     * @param float $rate the discount rate to apply to the cash flows
-     * @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
-     *                          The first payment is optional and corresponds to a cost or payment that occurs
-     *                              at the beginning of the investment.
-     *                          If the first value is a cost or payment, it must be a negative value.
-     *                             All succeeding payments are discounted based on a 365-day year.
-     *                          The series of values must contain at least one positive value and one negative value.
-     * @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
-     *                         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.
-     *
-     * @return float|mixed|string
-     */
-    public static function XNPV($rate, $values, $dates)
-    {
-        return Financial\CashFlow\Variable\NonPeriodic::presentValue($rate, $values, $dates);
-    }
-
-    /**
-     * YIELDDISC.
-     *
-     * Returns the annual yield of a security that pays interest at maturity.
-     *
-     * @deprecated 1.18.0
-     *      Use the yieldDiscounted() method in the Financial\Securities\Yields class instead
-     * @see Financial\Securities\Yields::yieldDiscounted()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                              The security's settlement date is the date after the issue date when the security
-     *                              is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                            The maturity date is the date when the security expires.
-     * @param int $price The security's price per $100 face value
-     * @param int $redemption The security's redemption value per $100 face value
-     * @param int $basis The type of day count to use.
-     *                                        0 or omitted    US (NASD) 30/360
-     *                                        1                Actual/actual
-     *                                        2                Actual/360
-     *                                        3                Actual/365
-     *                                        4                European 30/360
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
-    {
-        return Securities\Yields::yieldDiscounted($settlement, $maturity, $price, $redemption, $basis);
-    }
-
-    /**
-     * YIELDMAT.
-     *
-     * Returns the annual yield of a security that pays interest at maturity.
-     *
-     * @deprecated 1.18.0
-     *      Use the yieldAtMaturity() method in the Financial\Securities\Yields class instead
-     * @see Financial\Securities\Yields::yieldAtMaturity()
-     *
-     * @param mixed $settlement The security's settlement date.
-     *                              The security's settlement date is the date after the issue date when the security
-     *                              is traded to the buyer.
-     * @param mixed $maturity The security's maturity date.
-     *                            The maturity date is the date when the security expires.
-     * @param mixed $issue The security's issue date
-     * @param int $rate The security's interest rate at date of issue
-     * @param int $price The security's price per $100 face value
-     * @param int $basis The type of day count to use.
-     *                       0 or omitted    US (NASD) 30/360
-     *                       1               Actual/actual
-     *                       2               Actual/360
-     *                       3               Actual/365
-     *                       4               European 30/360
-     *
-     * @return float|string Result, or a string containing an error
-     */
-    public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
-    {
-        return Securities\Yields::yieldAtMaturity($settlement, $maturity, $issue, $rate, $price, $basis);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
index 691ba40..e56fabe 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
@@ -9,6 +9,8 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 
 class Amortization
 {
+    private const ROUNDING_ADJUSTMENT = (PHP_VERSION_ID < 80400) ? 0 : 1e-14;
+
     /**
      * AMORDEGRC.
      *
@@ -40,14 +42,14 @@ class Amortization
      * @return float|string (string containing the error type if there is an error)
      */
     public static function AMORDEGRC(
-        $cost,
-        $purchased,
-        $firstPeriod,
-        $salvage,
-        $period,
-        $rate,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $cost,
+        mixed $purchased,
+        mixed $firstPeriod,
+        mixed $salvage,
+        mixed $period,
+        mixed $rate,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|float {
         $cost = Functions::flattenSingleValue($cost);
         $purchased = Functions::flattenSingleValue($purchased);
         $firstPeriod = Functions::flattenSingleValue($firstPeriod);
@@ -74,12 +76,13 @@ class Amortization
         if (is_string($yearFracx)) {
             return $yearFracx;
         }
-        /** @var float */
+        /** @var float $yearFrac */
         $yearFrac = $yearFracx;
 
         $amortiseCoeff = self::getAmortizationCoefficient($rate);
 
         $rate *= $amortiseCoeff;
+        $rate += self::ROUNDING_ADJUSTMENT;
         $fNRate = round($yearFrac * $rate * $cost, 0);
         $cost -= $fNRate;
         $fRest = $cost - $salvage;
@@ -89,13 +92,10 @@ class Amortization
             $fRest -= $fNRate;
 
             if ($fRest < 0.0) {
-                switch ($period - $n) {
-                    case 0:
-                    case 1:
-                        return round($cost * 0.5, 0);
-                    default:
-                        return 0.0;
-                }
+                return match ($period - $n) {
+                    1 => round($cost * 0.5, 0),
+                    default => 0.0,
+                };
             }
             $cost -= $fNRate;
         }
@@ -129,14 +129,14 @@ class Amortization
      * @return float|string (string containing the error type if there is an error)
      */
     public static function AMORLINC(
-        $cost,
-        $purchased,
-        $firstPeriod,
-        $salvage,
-        $period,
-        $rate,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $cost,
+        mixed $purchased,
+        mixed $firstPeriod,
+        mixed $salvage,
+        mixed $period,
+        mixed $rate,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|float {
         $cost = Functions::flattenSingleValue($cost);
         $purchased = Functions::flattenSingleValue($purchased);
         $firstPeriod = Functions::flattenSingleValue($firstPeriod);
@@ -167,12 +167,13 @@ class Amortization
         if (is_string($yearFracx)) {
             return $yearFracx;
         }
-        /** @var float */
+        /** @var float $yearFrac */
         $yearFrac = $yearFracx;
 
         if (
-            ($basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) &&
-            ($yearFrac < 1) && (Functions::scalar(DateTimeExcel\Helpers::isLeapYear($purchasedYear)))
+            $basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
+            && $yearFrac < 1
+            && DateTimeExcel\Helpers::isLeapYear(Functions::scalar($purchasedYear))
         ) {
             $yearFrac *= 365 / 366;
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
index 8ebe9ed..f5719b6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/CashFlowValidations.php
@@ -9,25 +9,19 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class CashFlowValidations extends FinancialValidations
 {
-    /**
-     * @param mixed $rate
-     */
-    public static function validateRate($rate): float
+    public static function validateRate(mixed $rate): float
     {
         $rate = self::validateFloat($rate);
 
         return $rate;
     }
 
-    /**
-     * @param mixed $type
-     */
-    public static function validatePeriodType($type): int
+    public static function validatePeriodType(mixed $type): int
     {
         $rate = self::validateInt($type);
         if (
-            $type !== FinancialConstants::PAYMENT_END_OF_PERIOD &&
-            $type !== FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD
+            $type !== FinancialConstants::PAYMENT_END_OF_PERIOD
+            && $type !== FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD
         ) {
             throw new Exception(ExcelError::NAN());
         }
@@ -35,18 +29,12 @@ class CashFlowValidations extends FinancialValidations
         return $rate;
     }
 
-    /**
-     * @param mixed $presentValue
-     */
-    public static function validatePresentValue($presentValue): float
+    public static function validatePresentValue(mixed $presentValue): float
     {
         return self::validateFloat($presentValue);
     }
 
-    /**
-     * @param mixed $futureValue
-     */
-    public static function validateFutureValue($futureValue): float
+    public static function validateFutureValue(mixed $futureValue): float
     {
         return self::validateFloat($futureValue);
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php
index a19c37f..08cef3e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic.php
@@ -28,16 +28,14 @@ class Periodic
      * @param mixed $type A number 0 or 1 and indicates when payments are due:
      *                      0 or omitted    At the end of the period.
      *                      1               At the beginning of the period.
-     *
-     * @return float|string
      */
     public static function futureValue(
-        $rate,
-        $numberOfPeriods,
-        $payment = 0.0,
-        $presentValue = 0.0,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
-    ) {
+        mixed $rate,
+        mixed $numberOfPeriods,
+        mixed $payment = 0.0,
+        mixed $presentValue = 0.0,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+    ): string|float {
         $rate = Functions::flattenSingleValue($rate);
         $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
         $payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
@@ -71,12 +69,12 @@ class Periodic
      * @return float|string Result, or a string containing an error
      */
     public static function presentValue(
-        $rate,
-        $numberOfPeriods,
-        $payment = 0.0,
-        $futureValue = 0.0,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
-    ) {
+        mixed $rate,
+        mixed $numberOfPeriods,
+        mixed $payment = 0.0,
+        mixed $futureValue = 0.0,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+    ): string|float {
         $rate = Functions::flattenSingleValue($rate);
         $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
         $payment = ($payment === null) ? 0.0 : Functions::flattenSingleValue($payment);
@@ -115,11 +113,11 @@ class Periodic
      * @return float|string Result, or a string containing an error
      */
     public static function periods(
-        $rate,
-        $payment,
-        $presentValue,
-        $futureValue = 0.0,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+        mixed $rate,
+        mixed $payment,
+        mixed $presentValue,
+        mixed $futureValue = 0.0,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
     ) {
         $rate = Functions::flattenSingleValue($rate);
         $payment = Functions::flattenSingleValue($payment);
@@ -153,8 +151,8 @@ class Periodic
         int $type
     ): float {
         if ($rate !== null && $rate != 0) {
-            return -$presentValue *
-                (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
+            return -$presentValue
+                * (1 + $rate) ** $numberOfPeriods - $payment * (1 + $rate * $type) * ((1 + $rate) ** $numberOfPeriods - 1)
                     / $rate;
         }
 
@@ -176,23 +174,20 @@ class Periodic
         return -$futureValue - $payment * $numberOfPeriods;
     }
 
-    /**
-     * @return float|string
-     */
     private static function calculatePeriods(
         float $rate,
         float $payment,
         float $presentValue,
         float $futureValue,
         int $type
-    ) {
+    ): string|float {
         if ($rate != 0.0) {
             if ($presentValue == 0.0) {
                 return ExcelError::NAN();
             }
 
-            return log(($payment * (1 + $rate * $type) / $rate - $futureValue) /
-                    ($presentValue + $payment * (1 + $rate * $type) / $rate)) / log(1 + $rate);
+            return log(($payment * (1 + $rate * $type) / $rate - $futureValue)
+                    / ($presentValue + $payment * (1 + $rate * $type) / $rate)) / log(1 + $rate);
         }
 
         return (-$presentValue - $futureValue) / $payment;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
index b7aaffd..9435909 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Cumulative.php
@@ -27,17 +27,15 @@ class Cumulative
      * @param mixed $type A number 0 or 1 and indicates when payments are due:
      *                    0 or omitted    At the end of the period.
      *                    1               At the beginning of the period.
-     *
-     * @return float|string
      */
     public static function interest(
-        $rate,
-        $periods,
-        $presentValue,
-        $start,
-        $end,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
-    ) {
+        mixed $rate,
+        mixed $periods,
+        mixed $presentValue,
+        mixed $start,
+        mixed $end,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+    ): string|float|int {
         $rate = Functions::flattenSingleValue($rate);
         $periods = Functions::flattenSingleValue($periods);
         $presentValue = Functions::flattenSingleValue($presentValue);
@@ -92,17 +90,15 @@ class Cumulative
      * @param mixed $type A number 0 or 1 and indicates when payments are due:
      *                    0 or omitted    At the end of the period.
      *                    1               At the beginning of the period.
-     *
-     * @return float|string
      */
     public static function principal(
-        $rate,
-        $periods,
-        $presentValue,
-        $start,
-        $end,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
-    ) {
+        mixed $rate,
+        mixed $periods,
+        mixed $presentValue,
+        mixed $start,
+        mixed $end,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+    ): string|float|int {
         $rate = Functions::flattenSingleValue($rate);
         $periods = Functions::flattenSingleValue($periods);
         $presentValue = Functions::flattenSingleValue($presentValue);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
index 4a82514..ad68ec1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Interest.php
@@ -29,17 +29,15 @@ class Interest
      * @param mixed $presentValue Present Value
      * @param mixed $futureValue Future Value
      * @param mixed $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
-     *
-     * @return float|string
      */
     public static function payment(
-        $interestRate,
-        $period,
-        $numberOfPeriods,
-        $presentValue,
-        $futureValue = 0,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
-    ) {
+        mixed $interestRate,
+        mixed $period,
+        mixed $numberOfPeriods,
+        mixed $presentValue,
+        mixed $futureValue = 0,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+    ): string|float {
         $interestRate = Functions::flattenSingleValue($interestRate);
         $period = Functions::flattenSingleValue($period);
         $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
@@ -88,10 +86,8 @@ class Interest
      * @param mixed $period is the period to calculate the interest rate.  It must be betweeen 1 and number_payments.
      * @param mixed $numberOfPeriods is the number of payments for the annuity
      * @param mixed $principleRemaining is the loan amount or present value of the payments
-     *
-     * @return float|string
      */
-    public static function schedulePayment($interestRate, $period, $numberOfPeriods, $principleRemaining)
+    public static function schedulePayment(mixed $interestRate, mixed $period, mixed $numberOfPeriods, mixed $principleRemaining): string|float
     {
         $interestRate = Functions::flattenSingleValue($interestRate);
         $period = Functions::flattenSingleValue($period);
@@ -152,17 +148,15 @@ class Interest
      *                      1               At the beginning of the period.
      * @param mixed $guess Your guess for what the rate will be.
      *                          If you omit guess, it is assumed to be 10 percent.
-     *
-     * @return float|string
      */
     public static function rate(
-        $numberOfPeriods,
-        $payment,
-        $presentValue,
-        $futureValue = 0.0,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD,
-        $guess = 0.1
-    ) {
+        mixed $numberOfPeriods,
+        mixed $payment,
+        mixed $presentValue,
+        mixed $futureValue = 0.0,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD,
+        mixed $guess = 0.1
+    ): string|float {
         $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
         $payment = Functions::flattenSingleValue($payment);
         $presentValue = Functions::flattenSingleValue($presentValue);
@@ -171,7 +165,7 @@ class Interest
         $guess = ($guess === null) ? 0.1 : Functions::flattenSingleValue($guess);
 
         try {
-            $numberOfPeriods = CashFlowValidations::validateInt($numberOfPeriods);
+            $numberOfPeriods = CashFlowValidations::validateFloat($numberOfPeriods);
             $payment = CashFlowValidations::validateFloat($payment);
             $presentValue = CashFlowValidations::validatePresentValue($presentValue);
             $futureValue = CashFlowValidations::validateFutureValue($futureValue);
@@ -199,7 +193,7 @@ class Interest
         return $close ? $rate : ExcelError::NAN();
     }
 
-    private static function rateNextGuess($rate, $numberOfPeriods, $payment, $presentValue, $futureValue, $type)
+    private static function rateNextGuess(float $rate, float $numberOfPeriods, float $payment, float $presentValue, float $futureValue, int $type): string|float
     {
         if ($rate == 0.0) {
             return ExcelError::NAN();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
index ca989e0..ea9abb9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/InterestAndPrincipal.php
@@ -6,9 +6,9 @@ use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstan
 
 class InterestAndPrincipal
 {
-    protected $interest;
+    protected float $interest;
 
-    protected $principal;
+    protected float $principal;
 
     public function __construct(
         float $rate = 0.0,
@@ -24,7 +24,7 @@ class InterestAndPrincipal
         $principal = 0.0;
         for ($i = 1; $i <= $period; ++$i) {
             $interest = ($type === FinancialConstants::PAYMENT_BEGINNING_OF_PERIOD && $i == 1) ? 0 : -$capital * $rate;
-            $principal = $payment - $interest;
+            $principal = (float) $payment - $interest;
             $capital += $principal;
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
index 83965f9..41e88f9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Constant/Periodic/Payments.php
@@ -24,12 +24,12 @@ class Payments
      * @return float|string Result, or a string containing an error
      */
     public static function annuity(
-        $interestRate,
-        $numberOfPeriods,
-        $presentValue,
-        $futureValue = 0,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
-    ) {
+        mixed $interestRate,
+        mixed $numberOfPeriods,
+        mixed $presentValue,
+        mixed $futureValue = 0,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+    ): string|float {
         $interestRate = Functions::flattenSingleValue($interestRate);
         $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
         $presentValue = Functions::flattenSingleValue($presentValue);
@@ -48,8 +48,8 @@ class Payments
 
         // Calculate
         if ($interestRate != 0.0) {
-            return (-$futureValue - $presentValue * (1 + $interestRate) ** $numberOfPeriods) /
-                (1 + $interestRate * $type) / (((1 + $interestRate) ** $numberOfPeriods - 1) / $interestRate);
+            return (-$futureValue - $presentValue * (1 + $interestRate) ** $numberOfPeriods)
+                / (1 + $interestRate * $type) / (((1 + $interestRate) ** $numberOfPeriods - 1) / $interestRate);
         }
 
         return (-$presentValue - $futureValue) / $numberOfPeriods;
@@ -71,13 +71,13 @@ class Payments
      * @return float|string Result, or a string containing an error
      */
     public static function interestPayment(
-        $interestRate,
-        $period,
-        $numberOfPeriods,
-        $presentValue,
-        $futureValue = 0,
-        $type = FinancialConstants::PAYMENT_END_OF_PERIOD
-    ) {
+        mixed $interestRate,
+        mixed $period,
+        mixed $numberOfPeriods,
+        mixed $presentValue,
+        mixed $futureValue = 0,
+        mixed $type = FinancialConstants::PAYMENT_END_OF_PERIOD
+    ): string|float {
         $interestRate = Functions::flattenSingleValue($interestRate);
         $period = Functions::flattenSingleValue($period);
         $numberOfPeriods = Functions::flattenSingleValue($numberOfPeriods);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
index 058e89c..6f60a2a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
@@ -19,10 +19,8 @@ class Single
      *
      * @param mixed $principal the present value
      * @param float[] $schedule an array of interest rates to apply
-     *
-     * @return float|string
      */
-    public static function futureValue($principal, $schedule)
+    public static function futureValue(mixed $principal, array $schedule): string|float
     {
         $principal = Functions::flattenSingleValue($principal);
         $schedule = Functions::flattenArray($schedule);
@@ -52,7 +50,7 @@ class Single
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function periods($rate, $presentValue, $futureValue)
+    public static function periods(mixed $rate, mixed $presentValue, mixed $futureValue): string|float
     {
         $rate = Functions::flattenSingleValue($rate);
         $presentValue = Functions::flattenSingleValue($presentValue);
@@ -79,13 +77,13 @@ class Single
      *
      * Calculates the interest rate required for an investment to grow to a specified future value .
      *
-     * @param float $periods The number of periods over which the investment is made
-     * @param float $presentValue Present Value
-     * @param float $futureValue Future Value
+     * @param array|float $periods The number of periods over which the investment is made
+     * @param array|float $presentValue Present Value
+     * @param array|float $futureValue Future Value
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function interestRate($periods = 0.0, $presentValue = 0.0, $futureValue = 0.0)
+    public static function interestRate(array|float $periods = 0.0, array|float $presentValue = 0.0, array|float $futureValue = 0.0): string|float
     {
         $periods = Functions::flattenSingleValue($periods);
         $presentValue = Functions::flattenSingleValue($presentValue);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
index b812613..8c6f615 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php
@@ -29,10 +29,8 @@ class NonPeriodic
      *                                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 mixed $guess        An optional guess at the expected answer
-     *
-     * @return float|string
      */
-    public static function rate($values, $dates, $guess = self::DEFAULT_GUESS)
+    public static function rate(array $values, array $dates, mixed $guess = self::DEFAULT_GUESS): float|string
     {
         $rslt = self::xirrPart1($values, $dates);
         if ($rslt !== '') {
@@ -51,8 +49,11 @@ class NonPeriodic
         $f2 = self::xnpvOrdered($x2, $values, $dates, false);
         $found = false;
         for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
-            if (!is_numeric($f1) || !is_numeric($f2)) {
-                break;
+            if (!is_numeric($f1)) {
+                return $f1;
+            }
+            if (!is_numeric($f2)) {
+                return $f2;
             }
             $f1 = (float) $f1;
             $f2 = (float) $f2;
@@ -68,11 +69,32 @@ class NonPeriodic
                 $f2 = self::xnpvOrdered($x2, $values, $dates, false);
             }
         }
-        if (!$found) {
-            return ExcelError::NAN();
+        if ($found) {
+            return self::xirrPart3($values, $dates, $x1, $x2);
         }
 
-        return self::xirrPart3($values, $dates, $x1, $x2);
+        // Newton-Raphson didn't work - try bisection
+        $x1 = $guess - 0.5;
+        $x2 = $guess + 0.5;
+        for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+            $f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
+            $f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
+            if (!is_numeric($f1) || !is_numeric($f2)) {
+                break;
+            }
+            if ($f1 * $f2 <= 0) {
+                $found = true;
+
+                break;
+            }
+            $x1 -= 0.5;
+            $x2 += 0.5;
+        }
+        if ($found) {
+            return self::xirrBisection($values, $dates, $x1, $x2);
+        }
+
+        return ExcelError::NAN();
     }
 
     /**
@@ -84,7 +106,7 @@ class NonPeriodic
      * Excel Function:
      *        =XNPV(rate,values,dates)
      *
-     * @param float $rate the discount rate to apply to the cash flows
+     * @param array|float $rate the discount rate to apply to the cash flows
      * @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
      *                          The first payment is optional and corresponds to a cost or payment that occurs
      *                              at the beginning of the investment.
@@ -94,10 +116,8 @@ class NonPeriodic
      * @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
      *                         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.
-     *
-     * @return float|string
      */
-    public static function presentValue($rate, $values, $dates)
+    public static function presentValue(array|float $rate, array $values, array $dates): float|string
     {
         return self::xnpvOrdered($rate, $values, $dates, true);
     }
@@ -107,11 +127,7 @@ class NonPeriodic
         return $neg && $pos;
     }
 
-    /**
-     * @param mixed $values
-     * @param mixed $dates
-     */
-    private static function xirrPart1(&$values, &$dates): string
+    private static function xirrPart1(mixed &$values, mixed &$dates): string
     {
         $values = Functions::flattenArray($values);
         $dates = Functions::flattenArray($dates);
@@ -158,10 +174,7 @@ class NonPeriodic
         return '';
     }
 
-    /**
-     * @return float|string
-     */
-    private static function xirrPart3(array $values, array $dates, float $x1, float $x2)
+    private static function xirrPart3(array $values, array $dates, float $x1, float $x2): float|string
     {
         $f = self::xnpvOrdered($x1, $values, $dates, false);
         if ($f < 0.0) {
@@ -190,14 +203,43 @@ class NonPeriodic
         return $rslt;
     }
 
-    /**
-     * @param mixed $rate
-     * @param mixed $values
-     * @param mixed $dates
-     *
-     * @return float|string
-     */
-    private static function xnpvOrdered($rate, $values, $dates, bool $ordered = true)
+    private static function xirrBisection(array $values, array $dates, float $x1, float $x2): string|float
+    {
+        $rslt = ExcelError::NAN();
+        for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
+            $rslt = ExcelError::NAN();
+            $f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
+            $f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
+            if (!is_numeric($f1) || !is_numeric($f2)) {
+                break;
+            }
+            $f1 = (float) $f1;
+            $f2 = (float) $f2;
+            if (abs($f1) < self::FINANCIAL_PRECISION && abs($f2) < self::FINANCIAL_PRECISION) {
+                break;
+            }
+            if ($f1 * $f2 > 0) {
+                break;
+            }
+            $rslt = ($x1 + $x2) / 2;
+            $f3 = self::xnpvOrdered($rslt, $values, $dates, false, true);
+            if (!is_float($f3)) {
+                break;
+            }
+            if ($f3 * $f1 < 0) {
+                $x2 = $rslt;
+            } else {
+                $x1 = $rslt;
+            }
+            if (abs($f3) < self::FINANCIAL_PRECISION) {
+                break;
+            }
+        }
+
+        return $rslt;
+    }
+
+    private static function xnpvOrdered(mixed $rate, mixed $values, mixed $dates, bool $ordered = true, bool $capAtNegative1 = false): float|string
     {
         $rate = Functions::flattenSingleValue($rate);
         $values = Functions::flattenArray($values);
@@ -206,6 +248,9 @@ class NonPeriodic
 
         try {
             self::validateXnpv($rate, $values, $dates);
+            if ($capAtNegative1 && $rate <= -1) {
+                $rate = -1.0 + 1.0E-10;
+            }
             $date0 = DateTimeExcel\Helpers::getDateValue($dates[0]);
         } catch (Exception $e) {
             return $e->getMessage();
@@ -225,7 +270,7 @@ class NonPeriodic
             if ($date0 > $datei) {
                 $dif = $ordered ? ExcelError::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd'));
             } else {
-                $dif = DateTimeExcel\Difference::interval($date0, $datei, 'd');
+                $dif = Functions::scalar(DateTimeExcel\Difference::interval($date0, $datei, 'd'));
             }
             if (!is_numeric($dif)) {
                 return $dif;
@@ -240,10 +285,7 @@ class NonPeriodic
         return is_finite($xnpv) ? $xnpv : ExcelError::VALUE();
     }
 
-    /**
-     * @param mixed $rate
-     */
-    private static function validateXnpv($rate, array $values, array $dates): void
+    private static function validateXnpv(mixed $rate, array $values, array $dates): void
     {
         if (!is_numeric($rate)) {
             throw new Exception(ExcelError::VALUE());
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
index 545102f..21e537b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
@@ -28,10 +28,8 @@ class Periodic
      *                                Values must contain at least one positive value and one negative value to
      *                                    calculate the internal rate of return.
      * @param mixed $guess A number that you guess is close to the result of IRR
-     *
-     * @return float|string
      */
-    public static function rate($values, $guess = 0.1)
+    public static function rate(mixed $values, mixed $guess = 0.1): string|float
     {
         if (!is_array($values)) {
             return ExcelError::VALUE();
@@ -99,7 +97,7 @@ class Periodic
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function modifiedRate($values, $financeRate, $reinvestmentRate)
+    public static function modifiedRate(mixed $values, mixed $financeRate, mixed $reinvestmentRate): string|float
     {
         if (!is_array($values)) {
             return ExcelError::DIV0();
@@ -112,7 +110,7 @@ class Periodic
         $rr = 1.0 + $reinvestmentRate;
         $fr = 1.0 + $financeRate;
 
-        $npvPos = $npvNeg = self::$zeroPointZero;
+        $npvPos = $npvNeg = 0.0;
         foreach ($values as $i => $v) {
             if ($v >= 0) {
                 $npvPos += $v / $rr ** $i;
@@ -121,7 +119,7 @@ class Periodic
             }
         }
 
-        if ($npvNeg === self::$zeroPointZero || $npvPos === self::$zeroPointZero) {
+        if ($npvNeg === 0.0 || $npvPos === 0.0) {
             return ExcelError::DIV0();
         }
 
@@ -131,23 +129,14 @@ class Periodic
         return is_finite($mirr) ? $mirr : ExcelError::NAN();
     }
 
-    /**
-     * Sop to Scrutinizer.
-     *
-     * @var float
-     */
-    private static $zeroPointZero = 0.0;
-
     /**
      * NPV.
      *
      * Returns the Net Present Value of a cash flow series given a discount rate.
      *
-     * @param mixed $rate
-     *
-     * @return float
+     * @param array $args
      */
-    public static function presentValue($rate, ...$args)
+    public static function presentValue(mixed $rate, ...$args): int|float
     {
         $returnValue = 0;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php
index 14c2d01..c2fcab3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Coupons.php
@@ -39,15 +39,13 @@ class Coupons
      *                         2               Actual/360
      *                         3               Actual/365
      *                         4               European 30/360
-     *
-     * @return float|string
      */
     public static function COUPDAYBS(
-        $settlement,
-        $maturity,
-        $frequency,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $frequency,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|int|float {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $frequency = Functions::flattenSingleValue($frequency);
@@ -102,15 +100,13 @@ class Coupons
      *                         2               Actual/360
      *                         3               Actual/365
      *                         4               European 30/360
-     *
-     * @return float|string
      */
     public static function COUPDAYS(
-        $settlement,
-        $maturity,
-        $frequency,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $frequency,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|int|float {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $frequency = Functions::flattenSingleValue($frequency);
@@ -173,15 +169,13 @@ class Coupons
      *                         2               Actual/360
      *                         3               Actual/365
      *                         4               European 30/360
-     *
-     * @return float|string
      */
     public static function COUPDAYSNC(
-        $settlement,
-        $maturity,
-        $frequency,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $frequency,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|float {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $frequency = Functions::flattenSingleValue($frequency);
@@ -199,8 +193,8 @@ class Coupons
             return $e->getMessage();
         }
 
-        /** @var int */
-        $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+        /** @var int $daysPerYear */
+        $daysPerYear = Helpers::daysPerYear(Functions::Scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
         $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
 
         if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) {
@@ -239,15 +233,14 @@ class Coupons
      *                         3               Actual/365
      *                         4               European 30/360
      *
-     * @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
+     * @return float|string Excel date/time serial value or error message
      */
     public static function COUPNCD(
-        $settlement,
-        $maturity,
-        $frequency,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $frequency,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|float {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $frequency = Functions::flattenSingleValue($frequency);
@@ -260,8 +253,7 @@ class Coupons
             $maturity = FinancialValidations::validateMaturityDate($maturity);
             self::validateCouponPeriod($settlement, $maturity);
             $frequency = FinancialValidations::validateFrequency($frequency);
-            $basis = FinancialValidations::validateBasis($basis);
-            self::doNothing($basis);
+            FinancialValidations::validateBasis($basis);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -294,15 +286,13 @@ class Coupons
      *                         2               Actual/360
      *                         3               Actual/365
      *                         4               European 30/360
-     *
-     * @return int|string
      */
     public static function COUPNUM(
-        $settlement,
-        $maturity,
-        $frequency,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $frequency,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|int {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $frequency = Functions::flattenSingleValue($frequency);
@@ -315,8 +305,7 @@ class Coupons
             $maturity = FinancialValidations::validateMaturityDate($maturity);
             self::validateCouponPeriod($settlement, $maturity);
             $frequency = FinancialValidations::validateFrequency($frequency);
-            $basis = FinancialValidations::validateBasis($basis);
-            self::doNothing($basis);
+            FinancialValidations::validateBasis($basis);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -355,15 +344,14 @@ class Coupons
      *                         3               Actual/365
      *                         4               European 30/360
      *
-     * @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
+     * @return float|string Excel date/time serial value or error message
      */
     public static function COUPPCD(
-        $settlement,
-        $maturity,
-        $frequency,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $frequency,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|float {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $frequency = Functions::flattenSingleValue($frequency);
@@ -376,8 +364,7 @@ class Coupons
             $maturity = FinancialValidations::validateMaturityDate($maturity);
             self::validateCouponPeriod($settlement, $maturity);
             $frequency = FinancialValidations::validateFrequency($frequency);
-            $basis = FinancialValidations::validateBasis($basis);
-            self::doNothing($basis);
+            FinancialValidations::validateBasis($basis);
         } catch (Exception $e) {
             return $e->getMessage();
         }
@@ -417,10 +404,4 @@ class Coupons
             throw new Exception(ExcelError::NAN());
         }
     }
-
-    /** @param mixed $basis */
-    private static function doNothing($basis): bool
-    {
-        return $basis;
-    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
index 8e1a2fc..6ef1899 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php
@@ -8,8 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class Depreciation
 {
-    /** @var float */
-    private static $zeroPointZero = 0.0;
+    private static float $zeroPointZero = 0.0;
 
     /**
      * DB.
@@ -33,10 +32,8 @@ class Depreciation
      *                          depreciation. Period must use the same units as life.
      * @param mixed $month Number of months in the first year. If month is omitted,
      *                         it defaults to 12.
-     *
-     * @return float|string
      */
-    public static function DB($cost, $salvage, $life, $period, $month = 12)
+    public static function DB(mixed $cost, mixed $salvage, mixed $life, mixed $period, mixed $month = 12): string|float|int
     {
         $cost = Functions::flattenSingleValue($cost);
         $salvage = Functions::flattenSingleValue($salvage);
@@ -99,10 +96,8 @@ class Depreciation
      * @param mixed $factor The rate at which the balance declines.
      *                                If factor is omitted, it is assumed to be 2 (the
      *                                double-declining balance method).
-     *
-     * @return float|string
      */
-    public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
+    public static function DDB(mixed $cost, mixed $salvage, mixed $life, mixed $period, mixed $factor = 2.0): float|string
     {
         $cost = Functions::flattenSingleValue($cost);
         $salvage = Functions::flattenSingleValue($salvage);
@@ -150,7 +145,7 @@ class Depreciation
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function SLN($cost, $salvage, $life)
+    public static function SLN(mixed $cost, mixed $salvage, mixed $life): string|float
     {
         $cost = Functions::flattenSingleValue($cost);
         $salvage = Functions::flattenSingleValue($salvage);
@@ -183,7 +178,7 @@ class Depreciation
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function SYD($cost, $salvage, $life, $period)
+    public static function SYD(mixed $cost, mixed $salvage, mixed $life, mixed $period): string|float
     {
         $cost = Functions::flattenSingleValue($cost);
         $salvage = Functions::flattenSingleValue($salvage);
@@ -208,7 +203,7 @@ class Depreciation
         return $syd;
     }
 
-    private static function validateCost($cost, bool $negativeValueAllowed = false): float
+    private static function validateCost(mixed $cost, bool $negativeValueAllowed = false): float
     {
         $cost = FinancialValidations::validateFloat($cost);
         if ($cost < 0.0 && $negativeValueAllowed === false) {
@@ -218,7 +213,7 @@ class Depreciation
         return $cost;
     }
 
-    private static function validateSalvage($salvage, bool $negativeValueAllowed = false): float
+    private static function validateSalvage(mixed $salvage, bool $negativeValueAllowed = false): float
     {
         $salvage = FinancialValidations::validateFloat($salvage);
         if ($salvage < 0.0 && $negativeValueAllowed === false) {
@@ -228,7 +223,7 @@ class Depreciation
         return $salvage;
     }
 
-    private static function validateLife($life, bool $negativeValueAllowed = false): float
+    private static function validateLife(mixed $life, bool $negativeValueAllowed = false): float
     {
         $life = FinancialValidations::validateFloat($life);
         if ($life < 0.0 && $negativeValueAllowed === false) {
@@ -238,7 +233,7 @@ class Depreciation
         return $life;
     }
 
-    private static function validatePeriod($period, bool $negativeValueAllowed = false): float
+    private static function validatePeriod(mixed $period, bool $negativeValueAllowed = false): float
     {
         $period = FinancialValidations::validateFloat($period);
         if ($period <= 0.0 && $negativeValueAllowed === false) {
@@ -248,7 +243,7 @@ class Depreciation
         return $period;
     }
 
-    private static function validateMonth($month): int
+    private static function validateMonth(mixed $month): int
     {
         $month = FinancialValidations::validateInt($month);
         if ($month < 1) {
@@ -258,7 +253,7 @@ class Depreciation
         return $month;
     }
 
-    private static function validateFactor($factor): float
+    private static function validateFactor(mixed $factor): float
     {
         $factor = FinancialValidations::validateFloat($factor);
         if ($factor <= 0.0) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
index b1f0d25..b0581f6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Dollar.php
@@ -25,11 +25,10 @@ class Dollar
      *                            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
+     * @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)
+    public static function format(mixed $number, mixed $precision = 2)
     {
         return Format::DOLLAR($number, $precision);
     }
@@ -48,10 +47,8 @@ class Dollar
      *              Or can be an array of values
      * @param mixed $fraction Fraction
      *              Or can be an array of values
-     *
-     * @return array|float|string
      */
-    public static function decimal($fractionalDollar = null, $fraction = 0)
+    public static function decimal(mixed $fractionalDollar = null, mixed $fraction = 0): array|string|float
     {
         if (is_array($fractionalDollar) || is_array($fraction)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $fractionalDollar, $fraction);
@@ -96,10 +93,8 @@ class Dollar
      *              Or can be an array of values
      * @param mixed $fraction Fraction
      *              Or can be an array of values
-     *
-     * @return array|float|string
      */
-    public static function fractional($decimalDollar = null, $fraction = 0)
+    public static function fractional(mixed $decimalDollar = null, mixed $fraction = 0): array|string|float
     {
         if (is_array($decimalDollar) || is_array($fraction)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $decimalDollar, $fraction);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
index 1b04419..e596fc3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/FinancialValidations.php
@@ -9,34 +9,22 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class FinancialValidations
 {
-    /**
-     * @param mixed $date
-     */
-    public static function validateDate($date): float
+    public static function validateDate(mixed $date): float
     {
         return DateTimeExcel\Helpers::getDateValue($date);
     }
 
-    /**
-     * @param mixed $settlement
-     */
-    public static function validateSettlementDate($settlement): float
+    public static function validateSettlementDate(mixed $settlement): float
     {
         return self::validateDate($settlement);
     }
 
-    /**
-     * @param mixed $maturity
-     */
-    public static function validateMaturityDate($maturity): float
+    public static function validateMaturityDate(mixed $maturity): float
     {
         return self::validateDate($maturity);
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function validateFloat($value): float
+    public static function validateFloat(mixed $value): float
     {
         if (!is_numeric($value)) {
             throw new Exception(ExcelError::VALUE());
@@ -45,10 +33,7 @@ class FinancialValidations
         return (float) $value;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function validateInt($value): int
+    public static function validateInt(mixed $value): int
     {
         if (!is_numeric($value)) {
             throw new Exception(ExcelError::VALUE());
@@ -57,10 +42,7 @@ class FinancialValidations
         return (int) floor((float) $value);
     }
 
-    /**
-     * @param mixed $rate
-     */
-    public static function validateRate($rate): float
+    public static function validateRate(mixed $rate): float
     {
         $rate = self::validateFloat($rate);
         if ($rate < 0.0) {
@@ -70,16 +52,13 @@ class FinancialValidations
         return $rate;
     }
 
-    /**
-     * @param mixed $frequency
-     */
-    public static function validateFrequency($frequency): int
+    public static function validateFrequency(mixed $frequency): int
     {
         $frequency = self::validateInt($frequency);
         if (
-            ($frequency !== FinancialConstants::FREQUENCY_ANNUAL) &&
-            ($frequency !== FinancialConstants::FREQUENCY_SEMI_ANNUAL) &&
-            ($frequency !== FinancialConstants::FREQUENCY_QUARTERLY)
+            ($frequency !== FinancialConstants::FREQUENCY_ANNUAL)
+            && ($frequency !== FinancialConstants::FREQUENCY_SEMI_ANNUAL)
+            && ($frequency !== FinancialConstants::FREQUENCY_QUARTERLY)
         ) {
             throw new Exception(ExcelError::NAN());
         }
@@ -87,10 +66,7 @@ class FinancialValidations
         return $frequency;
     }
 
-    /**
-     * @param mixed $basis
-     */
-    public static function validateBasis($basis): int
+    public static function validateBasis(mixed $basis): int
     {
         if (!is_numeric($basis)) {
             throw new Exception(ExcelError::VALUE());
@@ -104,10 +80,7 @@ class FinancialValidations
         return $basis;
     }
 
-    /**
-     * @param mixed $price
-     */
-    public static function validatePrice($price): float
+    public static function validatePrice(mixed $price): float
     {
         $price = self::validateFloat($price);
         if ($price < 0.0) {
@@ -117,10 +90,7 @@ class FinancialValidations
         return $price;
     }
 
-    /**
-     * @param mixed $parValue
-     */
-    public static function validateParValue($parValue): float
+    public static function validateParValue(mixed $parValue): float
     {
         $parValue = self::validateFloat($parValue);
         if ($parValue < 0.0) {
@@ -130,10 +100,7 @@ class FinancialValidations
         return $parValue;
     }
 
-    /**
-     * @param mixed $yield
-     */
-    public static function validateYield($yield): float
+    public static function validateYield(mixed $yield): float
     {
         $yield = self::validateFloat($yield);
         if ($yield < 0.0) {
@@ -143,10 +110,7 @@ class FinancialValidations
         return $yield;
     }
 
-    /**
-     * @param mixed $discount
-     */
-    public static function validateDiscount($discount): float
+    public static function validateDiscount(mixed $discount): float
     {
         $discount = self::validateFloat($discount);
         if ($discount <= 0.0) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
index c7f1f46..aa28712 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Helpers.php
@@ -24,7 +24,7 @@ class Helpers
      *
      * @return int|string Result, or a string containing an error
      */
-    public static function daysPerYear($year, $basis = 0)
+    public static function daysPerYear($year, $basis = 0): string|int
     {
         if (!is_numeric($basis)) {
             return ExcelError::NAN();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
index 1cbe265..2916df6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php
@@ -19,10 +19,8 @@ class InterestRate
      *
      * @param mixed $nominalRate Nominal interest rate as a float
      * @param mixed $periodsPerYear Integer number of compounding payments per year
-     *
-     * @return float|string
      */
-    public static function effective($nominalRate = 0, $periodsPerYear = 0)
+    public static function effective(mixed $nominalRate = 0, mixed $periodsPerYear = 0): string|float
     {
         $nominalRate = Functions::flattenSingleValue($nominalRate);
         $periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
@@ -51,7 +49,7 @@ class InterestRate
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function nominal($effectiveRate = 0, $periodsPerYear = 0)
+    public static function nominal(mixed $effectiveRate = 0, mixed $periodsPerYear = 0): string|float
     {
         $effectiveRate = Functions::flattenSingleValue($effectiveRate);
         $periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
index e1bf04b..eb57abf 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php
@@ -40,21 +40,20 @@ class AccruedInterest
      *                         2               Actual/360
      *                         3               Actual/365
      *                         4               European 30/360
-     * @param mixed $calcMethod
+     * @param mixed $calcMethod Unused by PhpSpreadsheet, and apparently by Excel (https://exceljet.net/functions/accrint-function)
      *
      * @return float|string Result, or a string containing an error
      */
     public static function periodic(
-        $issue,
-        $firstInterest,
-        $settlement,
-        $rate,
-        $parValue = 1000,
-        $frequency = FinancialConstants::FREQUENCY_ANNUAL,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD,
-        $calcMethod = self::ACCRINT_CALCMODE_ISSUE_TO_SETTLEMENT
+        mixed $issue,
+        mixed $firstInterest,
+        mixed $settlement,
+        mixed $rate,
+        mixed $parValue = 1000,
+        mixed $frequency = FinancialConstants::FREQUENCY_ANNUAL,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD,
+        mixed $calcMethod = self::ACCRINT_CALCMODE_ISSUE_TO_SETTLEMENT
     ) {
-        self::doNothing($calcMethod);
         $issue = Functions::flattenSingleValue($issue);
         $firstInterest = Functions::flattenSingleValue($firstInterest);
         $settlement = Functions::flattenSingleValue($settlement);
@@ -73,8 +72,7 @@ class AccruedInterest
             SecurityValidations::validateSecurityPeriod($issue, $settlement);
             $rate = SecurityValidations::validateRate($rate);
             $parValue = SecurityValidations::validateParValue($parValue);
-            $frequency = SecurityValidations::validateFrequency($frequency);
-            self::doNothing($frequency);
+            SecurityValidations::validateFrequency($frequency);
             $basis = SecurityValidations::validateBasis($basis);
         } catch (Exception $e) {
             return $e->getMessage();
@@ -117,11 +115,11 @@ class AccruedInterest
      * @return float|string Result, or a string containing an error
      */
     public static function atMaturity(
-        $issue,
-        $settlement,
-        $rate,
-        $parValue = 1000,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+        mixed $issue,
+        mixed $settlement,
+        mixed $rate,
+        mixed $parValue = 1000,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
     ) {
         $issue = Functions::flattenSingleValue($issue);
         $settlement = Functions::flattenSingleValue($settlement);
@@ -150,10 +148,4 @@ class AccruedInterest
 
         return $parValue * $rate * $daysBetweenIssueAndSettlement;
     }
-
-    /** @param mixed $arg */
-    private static function doNothing($arg): bool
-    {
-        return (bool) $arg;
-    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
index de1748a..b07b2c9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
@@ -28,7 +28,6 @@ class Price
      *                              For annual payments, frequency = 1;
      *                              for semiannual, frequency = 2;
      *                              for quarterly, frequency = 4.
-     * @param mixed $frequency
      * @param mixed $basis The type of day count to use.
      *                         0 or omitted    US (NASD) 30/360
      *                         1               Actual/actual
@@ -39,14 +38,14 @@ class Price
      * @return float|string Result, or a string containing an error
      */
     public static function price(
-        $settlement,
-        $maturity,
-        $rate,
-        $yield,
-        $redemption,
-        $frequency,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $rate,
+        mixed $yield,
+        mixed $redemption,
+        mixed $frequency,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): string|float {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $rate = Functions::flattenSingleValue($rate);
@@ -70,10 +69,10 @@ class Price
             return $e->getMessage();
         }
 
-        $dsc = Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
-        $e = Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
-        $n = Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
-        $a = Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
+        $dsc = (float) Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
+        $e = (float) Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
+        $n = (int) Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
+        $a = (float) Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
 
         $baseYF = 1.0 + ($yield / $frequency);
         $rfp = 100 * ($rate / $frequency);
@@ -110,11 +109,11 @@ class Price
      * @return float|string Result, or a string containing an error
      */
     public static function priceDiscounted(
-        $settlement,
-        $maturity,
-        $discount,
-        $redemption,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+        mixed $settlement,
+        mixed $maturity,
+        mixed $discount,
+        mixed $redemption,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
     ) {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
@@ -167,12 +166,12 @@ class Price
      * @return float|string Result, or a string containing an error
      */
     public static function priceAtMaturity(
-        $settlement,
-        $maturity,
-        $issue,
-        $rate,
-        $yield,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+        mixed $settlement,
+        mixed $maturity,
+        mixed $issue,
+        mixed $rate,
+        mixed $yield,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
     ) {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
@@ -195,7 +194,7 @@ class Price
             return $e->getMessage();
         }
 
-        $daysPerYear = Functions::scalar(Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis));
+        $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
         if (!is_numeric($daysPerYear)) {
             return $daysPerYear;
         }
@@ -218,9 +217,9 @@ class Price
         }
         $daysBetweenSettlementAndMaturity *= $daysPerYear;
 
-        return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
-            (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
-            (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
+        return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100))
+            / (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield))
+            - (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
     }
 
     /**
@@ -245,11 +244,11 @@ class Price
      * @return float|string Result, or a string containing an error
      */
     public static function received(
-        $settlement,
-        $maturity,
-        $investment,
-        $discount,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+        mixed $settlement,
+        mixed $maturity,
+        mixed $investment,
+        mixed $discount,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
     ) {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
@@ -276,7 +275,7 @@ class Price
         $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
         if (!is_numeric($daysBetweenSettlementAndMaturity)) {
             //    return date error
-            return $daysBetweenSettlementAndMaturity;
+            return Functions::scalar($daysBetweenSettlementAndMaturity);
         }
 
         return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
index f8d8673..2989a29 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
@@ -31,16 +31,14 @@ class Rates
      *                         2               Actual/360
      *                         3               Actual/365
      *                         4               European 30/360
-     *
-     * @return float|string
      */
     public static function discount(
-        $settlement,
-        $maturity,
-        $price,
-        $redemption,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $price,
+        mixed $redemption,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): float|string {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $price = Functions::flattenSingleValue($price);
@@ -94,16 +92,14 @@ class Rates
      *                         2               Actual/360
      *                         3               Actual/365
      *                         4               European 30/360
-     *
-     * @return float|string
      */
     public static function interest(
-        $settlement,
-        $maturity,
-        $investment,
-        $redemption,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
-    ) {
+        mixed $settlement,
+        mixed $maturity,
+        mixed $investment,
+        mixed $redemption,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+    ): float|string {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
         $investment = Functions::flattenSingleValue($investment);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
index d3196f0..a0804cb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
@@ -8,29 +8,19 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class SecurityValidations extends FinancialValidations
 {
-    /**
-     * @param mixed $issue
-     */
-    public static function validateIssueDate($issue): float
+    public static function validateIssueDate(mixed $issue): float
     {
         return self::validateDate($issue);
     }
 
-    /**
-     * @param mixed $settlement
-     * @param mixed $maturity
-     */
-    public static function validateSecurityPeriod($settlement, $maturity): void
+    public static function validateSecurityPeriod(mixed $settlement, mixed $maturity): void
     {
         if ($settlement >= $maturity) {
             throw new Exception(ExcelError::NAN());
         }
     }
 
-    /**
-     * @param mixed $redemption
-     */
-    public static function validateRedemption($redemption): float
+    public static function validateRedemption(mixed $redemption): float
     {
         $redemption = self::validateFloat($redemption);
         if ($redemption <= 0.0) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
index bb2e8ae..a4c5a48 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
@@ -32,11 +32,11 @@ class Yields
      * @return float|string Result, or a string containing an error
      */
     public static function yieldDiscounted(
-        $settlement,
-        $maturity,
-        $price,
-        $redemption,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+        mixed $settlement,
+        mixed $maturity,
+        mixed $price,
+        mixed $redemption,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
     ) {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
@@ -57,7 +57,7 @@ class Yields
             return $e->getMessage();
         }
 
-        $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+        $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
         if (!is_numeric($daysPerYear)) {
             return $daysPerYear;
         }
@@ -94,12 +94,12 @@ class Yields
      * @return float|string Result, or a string containing an error
      */
     public static function yieldAtMaturity(
-        $settlement,
-        $maturity,
-        $issue,
-        $rate,
-        $price,
-        $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
+        mixed $settlement,
+        mixed $maturity,
+        mixed $issue,
+        mixed $rate,
+        mixed $price,
+        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
     ) {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
@@ -122,7 +122,7 @@ class Yields
             return $e->getMessage();
         }
 
-        $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
+        $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
         if (!is_numeric($daysPerYear)) {
             return $daysPerYear;
         }
@@ -145,9 +145,9 @@ class Yields
         }
         $daysBetweenSettlementAndMaturity *= $daysPerYear;
 
-        return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) -
-                    (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
-                (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
-            ($daysPerYear / $daysBetweenSettlementAndMaturity);
+        return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate)
+                    - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate)))
+                / (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate)))
+            * ($daysPerYear / $daysBetweenSettlementAndMaturity);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
index 7ee34f7..699efcc 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
@@ -24,7 +24,7 @@ class TreasuryBill
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function bondEquivalentYield($settlement, $maturity, $discount)
+    public static function bondEquivalentYield(mixed $settlement, mixed $maturity, mixed $discount): string|float
     {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
@@ -69,7 +69,7 @@ class TreasuryBill
      *
      * @return float|string Result, or a string containing an error
      */
-    public static function price($settlement, $maturity, $discount)
+    public static function price(mixed $settlement, mixed $maturity, mixed $discount): string|float
     {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
@@ -115,11 +115,9 @@ class TreasuryBill
      *                                    the Treasury bill is traded to the buyer.
      * @param mixed $maturity The Treasury bill's maturity date.
      *                                The maturity date is the date when the Treasury bill expires.
-     * @param mixed $price The Treasury bill's price per $100 face value
-     *
-     * @return float|string
+     * @param float|string $price The Treasury bill's price per $100 face value
      */
-    public static function yield($settlement, $maturity, $price)
+    public static function yield(mixed $settlement, mixed $maturity, $price): string|float
     {
         $settlement = Functions::flattenSingleValue($settlement);
         $maturity = Functions::flattenSingleValue($maturity);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
index 14c5ae5..9868b82 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaParser.php
@@ -46,24 +46,22 @@ class FormulaParser
 
     /**
      * Formula.
-     *
-     * @var string
      */
-    private $formula;
+    private string $formula;
 
     /**
      * Tokens.
      *
      * @var FormulaToken[]
      */
-    private $tokens = [];
+    private array $tokens = [];
 
     /**
      * Create a new FormulaParser.
      *
      * @param ?string $formula Formula to parse
      */
-    public function __construct($formula = '')
+    public function __construct(?string $formula = '')
     {
         // Check parameters
         if ($formula === null) {
@@ -78,10 +76,8 @@ class FormulaParser
 
     /**
      * Get Formula.
-     *
-     * @return string
      */
-    public function getFormula()
+    public function getFormula(): string
     {
         return $this->formula;
     }
@@ -102,10 +98,8 @@ class FormulaParser
 
     /**
      * Get Token count.
-     *
-     * @return int
      */
-    public function getTokenCount()
+    public function getTokenCount(): int
     {
         return count($this->tokens);
     }
@@ -115,7 +109,7 @@ class FormulaParser
      *
      * @return FormulaToken[]
      */
-    public function getTokens()
+    public function getTokens(): array
     {
         return $this->tokens;
     }
@@ -217,7 +211,7 @@ class FormulaParser
             }
 
             // scientific notation check
-            if (strpos(self::OPERATORS_SN, $this->formula[$index]) !== false) {
+            if (str_contains(self::OPERATORS_SN, $this->formula[$index])) {
                 if (strlen($value) > 1) {
                     if (preg_match('/^[1-9]{1}(\\.\\d+)?E{1}$/', $this->formula[$index]) != 0) {
                         $value .= $this->formula[$index];
@@ -232,7 +226,7 @@ class FormulaParser
 
             // establish state-dependent character evaluations
             if ($this->formula[$index] == self::QUOTE_DOUBLE) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     // unexpected
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
                     $value = '';
@@ -244,7 +238,7 @@ class FormulaParser
             }
 
             if ($this->formula[$index] == self::QUOTE_SINGLE) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     // unexpected
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
                     $value = '';
@@ -264,7 +258,7 @@ class FormulaParser
             }
 
             if ($this->formula[$index] == self::ERROR_START) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     // unexpected
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
                     $value = '';
@@ -278,7 +272,7 @@ class FormulaParser
 
             // mark start and end of arrays and array rows
             if ($this->formula[$index] == self::BRACE_OPEN) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     // unexpected
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
                     $value = '';
@@ -298,11 +292,12 @@ class FormulaParser
             }
 
             if ($this->formula[$index] == self::SEMICOLON) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                     $value = '';
                 }
 
+                /** @var FormulaToken $tmp */
                 $tmp = array_pop($stack);
                 $tmp->setValue('');
                 $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -321,16 +316,18 @@ class FormulaParser
             }
 
             if ($this->formula[$index] == self::BRACE_CLOSE) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                     $value = '';
                 }
 
+                /** @var FormulaToken $tmp */
                 $tmp = array_pop($stack);
                 $tmp->setValue('');
                 $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
                 $tokens1[] = $tmp;
 
+                /** @var FormulaToken $tmp */
                 $tmp = array_pop($stack);
                 $tmp->setValue('');
                 $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -343,7 +340,7 @@ class FormulaParser
 
             // trim white-space
             if ($this->formula[$index] == self::WHITESPACE) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                     $value = '';
                 }
@@ -359,7 +356,7 @@ class FormulaParser
             // multi-character comparators
             if (($index + 2) <= $formulaLength) {
                 if (in_array(substr($this->formula, $index, 2), $COMPARATORS_MULTI)) {
-                    if (strlen($value) > 0) {
+                    if ($value !== '') {
                         $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                         $value = '';
                     }
@@ -371,8 +368,8 @@ class FormulaParser
             }
 
             // standard infix operators
-            if (strpos(self::OPERATORS_INFIX, $this->formula[$index]) !== false) {
-                if (strlen($value) > 0) {
+            if (str_contains(self::OPERATORS_INFIX, $this->formula[$index])) {
+                if ($value !== '') {
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                     $value = '';
                 }
@@ -383,8 +380,8 @@ class FormulaParser
             }
 
             // standard postfix operators (only one)
-            if (strpos(self::OPERATORS_POSTFIX, $this->formula[$index]) !== false) {
-                if (strlen($value) > 0) {
+            if (str_contains(self::OPERATORS_POSTFIX, $this->formula[$index])) {
+                if ($value !== '') {
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                     $value = '';
                 }
@@ -396,7 +393,7 @@ class FormulaParser
 
             // start subexpression or function
             if ($this->formula[$index] == self::PAREN_OPEN) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     $tmp = new FormulaToken($value, FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
                     $tokens1[] = $tmp;
                     $stack[] = clone $tmp;
@@ -413,11 +410,12 @@ class FormulaParser
 
             // function, subexpression, or array parameters, or operand unions
             if ($this->formula[$index] == self::COMMA) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                     $value = '';
                 }
 
+                /** @var FormulaToken $tmp */
                 $tmp = array_pop($stack);
                 $tmp->setValue('');
                 $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -435,11 +433,12 @@ class FormulaParser
 
             // stop subexpression
             if ($this->formula[$index] == self::PAREN_CLOSE) {
-                if (strlen($value) > 0) {
+                if ($value !== '') {
                     $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
                     $value = '';
                 }
 
+                /** @var FormulaToken $tmp */
                 $tmp = array_pop($stack);
                 $tmp->setValue('');
                 $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
@@ -456,7 +455,7 @@ class FormulaParser
         }
 
         // dump remaining accumulation
-        if (strlen($value) > 0) {
+        if ($value !== '') {
             $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
         }
 
@@ -475,10 +474,6 @@ class FormulaParser
                 $nextToken = null;
             }
 
-            if ($token === null) {
-                continue;
-            }
-
             if ($token->getTokenType() != FormulaToken::TOKEN_TYPE_WHITESPACE) {
                 $tokens2[] = $token;
 
@@ -491,9 +486,9 @@ class FormulaParser
 
             if (
                 !(
-                    (($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)
+                    (($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)
                 )
             ) {
                 continue;
@@ -505,9 +500,9 @@ class FormulaParser
 
             if (
                 !(
-                    (($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)
+                    (($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)
                 )
             ) {
                 continue;
@@ -528,26 +523,17 @@ class FormulaParser
             } else {
                 $previousToken = null;
             }
-            //if (isset($tokens2[$i + 1])) {
-            //    $nextToken = $tokens2[$i + 1];
-            //} else {
-            //    $nextToken = null;
-            //}
-
-            if ($token === null) {
-                continue;
-            }
 
             if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '-') {
                 if ($i == 0) {
                     $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
                 } elseif (
-                    (($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_OPERATORPOSTFIX) ||
-                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+                    (($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_OPERATORPOSTFIX)
+                    || ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
                 ) {
                     $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
                 } else {
@@ -563,12 +549,12 @@ class FormulaParser
                 if ($i == 0) {
                     continue;
                 } elseif (
-                    (($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_OPERATORPOSTFIX) ||
-                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
+                    (($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_OPERATORPOSTFIX)
+                    || ($previousToken?->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
                 ) {
                     $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
                 } else {
@@ -581,10 +567,10 @@ class FormulaParser
             }
 
             if (
-                $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
-                $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
+                $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX
+                && $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
             ) {
-                if (strpos('<>=', substr($token->getValue(), 0, 1)) !== false) {
+                if (str_contains('<>=', substr($token->getValue(), 0, 1))) {
                     $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
                 } elseif ($token->getValue() == '&') {
                     $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
@@ -598,8 +584,8 @@ class FormulaParser
             }
 
             if (
-                $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND &&
-                $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
+                $token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND
+                && $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING
             ) {
                 if (!is_numeric($token->getValue())) {
                     if (strtoupper($token->getValue()) == 'TRUE' || strtoupper($token->getValue()) == 'FALSE') {
@@ -617,8 +603,8 @@ class FormulaParser
             }
 
             if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
-                if (strlen($token->getValue()) > 0) {
-                    if (substr($token->getValue(), 0, 1) == '@') {
+                if ($token->getValue() !== '') {
+                    if (str_starts_with($token->getValue(), '@')) {
                         $token->setValue(substr($token->getValue(), 1));
                     }
                 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
index 68e5eea..cc7d48f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/FormulaToken.php
@@ -54,33 +54,26 @@ class FormulaToken
 
     /**
      * Value.
-     *
-     * @var string
      */
-    private $value;
+    private string $value;
 
     /**
      * Token Type (represented by TOKEN_TYPE_*).
-     *
-     * @var string
      */
-    private $tokenType;
+    private string $tokenType;
 
     /**
      * Token SubType (represented by TOKEN_SUBTYPE_*).
-     *
-     * @var string
      */
-    private $tokenSubType;
+    private string $tokenSubType;
 
     /**
      * Create a new FormulaToken.
      *
-     * @param string $value
      * @param string $tokenType Token type (represented by TOKEN_TYPE_*)
      * @param string $tokenSubType Token Subtype (represented by TOKEN_SUBTYPE_*)
      */
-    public function __construct($value, $tokenType = self::TOKEN_TYPE_UNKNOWN, $tokenSubType = self::TOKEN_SUBTYPE_NOTHING)
+    public function __construct(string $value, string $tokenType = self::TOKEN_TYPE_UNKNOWN, string $tokenSubType = self::TOKEN_SUBTYPE_NOTHING)
     {
         // Initialise values
         $this->value = $value;
@@ -90,60 +83,48 @@ class FormulaToken
 
     /**
      * Get Value.
-     *
-     * @return string
      */
-    public function getValue()
+    public function getValue(): string
     {
         return $this->value;
     }
 
     /**
      * Set Value.
-     *
-     * @param string $value
      */
-    public function setValue($value): void
+    public function setValue(string $value): void
     {
         $this->value = $value;
     }
 
     /**
      * Get Token Type (represented by TOKEN_TYPE_*).
-     *
-     * @return string
      */
-    public function getTokenType()
+    public function getTokenType(): string
     {
         return $this->tokenType;
     }
 
     /**
      * Set Token Type (represented by TOKEN_TYPE_*).
-     *
-     * @param string $value
      */
-    public function setTokenType($value): void
+    public function setTokenType(string $value): void
     {
         $this->tokenType = $value;
     }
 
     /**
      * Get Token SubType (represented by TOKEN_SUBTYPE_*).
-     *
-     * @return string
      */
-    public function getTokenSubType()
+    public function getTokenSubType(): string
     {
         return $this->tokenSubType;
     }
 
     /**
      * Set Token SubType (represented by TOKEN_SUBTYPE_*).
-     *
-     * @param string $value
      */
-    public function setTokenSubType($value): void
+    public function setTokenSubType(string $value): void
     {
         $this->tokenSubType = $value;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
index 747a1f4..77f8317 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Functions.php
@@ -26,19 +26,17 @@ class Functions
     const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
     const RETURNDATE_EXCEL = 'E';
 
+    public const NOT_YET_IMPLEMENTED = '#Not Yet Implemented';
+
     /**
      * Compatibility mode to use for error checking and responses.
-     *
-     * @var string
      */
-    protected static $compatibilityMode = self::COMPATIBILITY_EXCEL;
+    protected static string $compatibilityMode = self::COMPATIBILITY_EXCEL;
 
     /**
      * Data Type to use when returning date values.
-     *
-     * @var string
      */
-    protected static $returnDateType = self::RETURNDATE_EXCEL;
+    protected static string $returnDateType = self::RETURNDATE_EXCEL;
 
     /**
      * Set the Compatibility Mode.
@@ -51,12 +49,12 @@ class Functions
      *
      * @return bool (Success or Failure)
      */
-    public static function setCompatibilityMode($compatibilityMode)
+    public static function setCompatibilityMode(string $compatibilityMode): bool
     {
         if (
-            ($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
-            ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
-            ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
+            ($compatibilityMode == self::COMPATIBILITY_EXCEL)
+            || ($compatibilityMode == self::COMPATIBILITY_GNUMERIC)
+            || ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
         ) {
             self::$compatibilityMode = $compatibilityMode;
 
@@ -75,7 +73,7 @@ class Functions
      *                    Functions::COMPATIBILITY_GNUMERIC     'Gnumeric'
      *                    Functions::COMPATIBILITY_OPENOFFICE   'OpenOfficeCalc'
      */
-    public static function getCompatibilityMode()
+    public static function getCompatibilityMode(): string
     {
         return self::$compatibilityMode;
     }
@@ -91,12 +89,12 @@ class Functions
      *
      * @return bool Success or failure
      */
-    public static function setReturnDateType($returnDateType)
+    public static function setReturnDateType(string $returnDateType): bool
     {
         if (
-            ($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
-            ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) ||
-            ($returnDateType == self::RETURNDATE_EXCEL)
+            ($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP)
+            || ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT)
+            || ($returnDateType == self::RETURNDATE_EXCEL)
         ) {
             self::$returnDateType = $returnDateType;
 
@@ -115,7 +113,7 @@ class Functions
      *                    Functions::RETURNDATE_PHP_DATETIME_OBJECT    'O'
      *                    Functions::RETURNDATE_EXCEL            '     'E'
      */
-    public static function getReturnDateType()
+    public static function getReturnDateType(): string
     {
         return self::$returnDateType;
     }
@@ -125,31 +123,31 @@ class Functions
      *
      * @return string #Not Yet Implemented
      */
-    public static function DUMMY()
+    public static function DUMMY(): string
     {
-        return '#Not Yet Implemented';
+        return self::NOT_YET_IMPLEMENTED;
     }
 
-    public static function isMatrixValue($idx)
+    public static function isMatrixValue(mixed $idx): bool
     {
         return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
     }
 
-    public static function isValue($idx)
+    public static function isValue(mixed $idx): bool
     {
         return substr_count($idx, '.') === 0;
     }
 
-    public static function isCellValue($idx)
+    public static function isCellValue(mixed $idx): bool
     {
         return substr_count($idx, '.') > 1;
     }
 
-    public static function ifCondition($condition)
+    public static function ifCondition(mixed $condition): string
     {
         $condition = self::flattenSingleValue($condition);
 
-        if ($condition === '') {
+        if ($condition === '' || $condition === null) {
             return '=""';
         }
         if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='], true)) {
@@ -180,7 +178,7 @@ class Functions
         return str_replace('""""', '""', $operator . $operand);
     }
 
-    private static function operandSpecialHandling($operand)
+    private static function operandSpecialHandling(mixed $operand): mixed
     {
         if (is_numeric($operand) || is_bool($operand)) {
             return $operand;
@@ -201,333 +199,14 @@ class Functions
         return $operand;
     }
 
-    /**
-     * NULL.
-     *
-     * Returns the error value #NULL!
-     *
-     * @deprecated 1.23.0 Use the null() method in the Information\ExcelError class instead
-     * @see Information\ExcelError::null()
-     *
-     * @return string #NULL!
-     */
-    public static function null()
-    {
-        return Information\ExcelError::null();
-    }
-
-    /**
-     * NaN.
-     *
-     * Returns the error value #NUM!
-     *
-     * @deprecated 1.23.0 Use the NAN() method in the Information\Error class instead
-     * @see Information\ExcelError::NAN()
-     *
-     * @return string #NUM!
-     */
-    public static function NAN()
-    {
-        return Information\ExcelError::NAN();
-    }
-
-    /**
-     * REF.
-     *
-     * Returns the error value #REF!
-     *
-     * @deprecated 1.23.0 Use the REF() method in the Information\ExcelError class instead
-     * @see Information\ExcelError::REF()
-     *
-     * @return string #REF!
-     */
-    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 Use the NA() method in the Information\ExcelError class instead
-     * @see Information\ExcelError::NA()
-     *
-     * @return string #N/A!
-     */
-    public static function NA()
-    {
-        return Information\ExcelError::NA();
-    }
-
-    /**
-     * VALUE.
-     *
-     * Returns the error value #VALUE!
-     *
-     * @deprecated 1.23.0 Use the VALUE() method in the Information\ExcelError class instead
-     * @see Information\ExcelError::VALUE()
-     *
-     * @return string #VALUE!
-     */
-    public static function VALUE()
-    {
-        return Information\ExcelError::VALUE();
-    }
-
-    /**
-     * NAME.
-     *
-     * Returns the error value #NAME?
-     *
-     * @deprecated 1.23.0 Use the NAME() method in the Information\ExcelError class instead
-     * @see Information\ExcelError::NAME()
-     *
-     * @return string #NAME?
-     */
-    public static function NAME()
-    {
-        return Information\ExcelError::NAME();
-    }
-
-    /**
-     * DIV0.
-     *
-     * @deprecated 1.23.0 Use the DIV0() method in the Information\ExcelError class instead
-     * @see Information\ExcelError::DIV0()
-     *
-     * @return string #Not Yet Implemented
-     */
-    public static function DIV0()
-    {
-        return Information\ExcelError::DIV0();
-    }
-
-    /**
-     * ERROR_TYPE.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the type() method in the Information\ExcelError class instead
-     * @see Information\ExcelError::type()
-     *
-     * @return array|int|string
-     */
-    public static function errorType($value = '')
-    {
-        return Information\ExcelError::type($value);
-    }
-
-    /**
-     * IS_BLANK.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isBlank() method in the Information\Value class instead
-     * @see Information\Value::isBlank()
-     *
-     * @return array|bool
-     */
-    public static function isBlank($value = null)
-    {
-        return Information\Value::isBlank($value);
-    }
-
-    /**
-     * IS_ERR.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isErr() method in the Information\ErrorValue class instead
-     * @see Information\ErrorValue::isErr()
-     *
-     * @return array|bool
-     */
-    public static function isErr($value = '')
-    {
-        return Information\ErrorValue::isErr($value);
-    }
-
-    /**
-     * IS_ERROR.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isError() method in the Information\ErrorValue class instead
-     * @see Information\ErrorValue::isError()
-     *
-     * @return array|bool
-     */
-    public static function isError($value = '')
-    {
-        return Information\ErrorValue::isError($value);
-    }
-
-    /**
-     * IS_NA.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isNa() method in the Information\ErrorValue class instead
-     * @see Information\ErrorValue::isNa()
-     *
-     * @return array|bool
-     */
-    public static function isNa($value = '')
-    {
-        return Information\ErrorValue::isNa($value);
-    }
-
-    /**
-     * IS_EVEN.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isEven() method in the Information\Value class instead
-     * @see Information\Value::isEven()
-     *
-     * @return array|bool|string
-     */
-    public static function isEven($value = null)
-    {
-        return Information\Value::isEven($value);
-    }
-
-    /**
-     * IS_ODD.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isOdd() method in the Information\Value class instead
-     * @see Information\Value::isOdd()
-     *
-     * @return array|bool|string
-     */
-    public static function isOdd($value = null)
-    {
-        return Information\Value::isOdd($value);
-    }
-
-    /**
-     * IS_NUMBER.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isNumber() method in the Information\Value class instead
-     * @see Information\Value::isNumber()
-     *
-     * @return array|bool
-     */
-    public static function isNumber($value = null)
-    {
-        return Information\Value::isNumber($value);
-    }
-
-    /**
-     * IS_LOGICAL.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isLogical() method in the Information\Value class instead
-     * @see Information\Value::isLogical()
-     *
-     * @return array|bool
-     */
-    public static function isLogical($value = null)
-    {
-        return Information\Value::isLogical($value);
-    }
-
-    /**
-     * IS_TEXT.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isText() method in the Information\Value class instead
-     * @see Information\Value::isText()
-     *
-     * @return array|bool
-     */
-    public static function isText($value = null)
-    {
-        return Information\Value::isText($value);
-    }
-
-    /**
-     * IS_NONTEXT.
-     *
-     * @param mixed $value Value to check
-     *
-     * @deprecated 1.23.0 Use the isNonText() method in the Information\Value class instead
-     * @see Information\Value::isNonText()
-     *
-     * @return array|bool
-     */
-    public static function isNonText($value = null)
-    {
-        return Information\Value::isNonText($value);
-    }
-
-    /**
-     * N.
-     *
-     * Returns a value converted to a number
-     *
-     * @deprecated 1.23.0 Use the asNumber() method in the Information\Value class instead
-     * @see Information\Value::asNumber()
-     *
-     * @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
-     *        A date                The serial number of that date
-     *        TRUE                1
-     *        FALSE                0
-     *        An error value        The error value
-     *        Anything else        0
-     */
-    public static function n($value = null)
-    {
-        return Information\Value::asNumber($value);
-    }
-
-    /**
-     * TYPE.
-     *
-     * Returns a number that identifies the type of a value
-     *
-     * @deprecated 1.23.0 Use the type() method in the Information\Value class instead
-     * @see Information\Value::type()
-     *
-     * @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)
-    {
-        return Information\Value::type($value);
-    }
-
     /**
      * Convert a multi-dimensional array to a simple 1-dimensional array.
      *
-     * @param array|mixed $array Array to be flattened
+     * @param mixed $array Array to be flattened
      *
      * @return array Flattened array
      */
-    public static function flattenArray($array)
+    public static function flattenArray(mixed $array): array
     {
         if (!is_array($array)) {
             return (array) $array;
@@ -549,12 +228,7 @@ class Functions
         return $flattened;
     }
 
-    /**
-     * @param mixed $value
-     *
-     * @return null|mixed
-     */
-    public static function scalar($value)
+    public static function scalar(mixed $value): mixed
     {
         if (!is_array($value)) {
             return $value;
@@ -574,7 +248,7 @@ class Functions
      *
      * @return array Flattened array
      */
-    public static function flattenArrayIndexed($array)
+    public static function flattenArrayIndexed($array): array
     {
         if (!is_array($array)) {
             return (array) $array;
@@ -604,10 +278,8 @@ class Functions
      * Convert an array to a single scalar value by extracting the first element.
      *
      * @param mixed $value Array or scalar value
-     *
-     * @return mixed
      */
-    public static function flattenSingleValue($value = '')
+    public static function flattenSingleValue(mixed $value): mixed
     {
         while (is_array($value)) {
             $value = array_shift($value);
@@ -616,26 +288,10 @@ class Functions
         return $value;
     }
 
-    /**
-     * ISFORMULA.
-     *
-     * @deprecated 1.23.0 Use the isFormula() method in the Information\Value class instead
-     * @see Information\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)
-    {
-        return Information\Value::isFormula($cellReference, $cell);
-    }
-
     public static function expandDefinedName(string $coordinate, Cell $cell): string
     {
         $worksheet = $cell->getWorksheet();
-        $spreadsheet = $worksheet->getParent();
+        $spreadsheet = $worksheet->getParentOrThrow();
         // Uppercase coordinate
         $pCoordinatex = strtoupper($coordinate);
         // Eliminate leading equal sign
@@ -644,8 +300,8 @@ class Functions
         if ($defined !== null) {
             $worksheet2 = $defined->getWorkSheet();
             if (!$defined->isFormula() && $worksheet2 !== null) {
-                $coordinate = "'" . $worksheet2->getTitle() . "'!" .
-                    (string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
+                $coordinate = "'" . $worksheet2->getTitle() . "'!"
+                    . (string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
             }
         }
 
@@ -659,7 +315,7 @@ class Functions
 
     public static function trimSheetFromCellReference(string $coordinate): string
     {
-        if (strpos($coordinate, '!') !== false) {
+        if (str_contains($coordinate, '!')) {
             $coordinate = substr($coordinate, strrpos($coordinate, '!') + 1);
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
index 4b9f818..f3a7462 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Information;
 
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 
 class ErrorValue
 {
@@ -14,11 +15,10 @@ class ErrorValue
      * @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
+     * @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 = '')
+    public static function isErr(mixed $value = ''): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -33,11 +33,10 @@ class ErrorValue
      * @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
+     * @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 = '')
+    public static function isError(mixed $value = '', bool $tryNotImplemented = false): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -46,6 +45,9 @@ class ErrorValue
         if (!is_string($value)) {
             return false;
         }
+        if ($tryNotImplemented && $value === Functions::NOT_YET_IMPLEMENTED) {
+            return true;
+        }
 
         return in_array($value, ExcelError::ERROR_CODES, true);
     }
@@ -56,11 +58,10 @@ class ErrorValue
      * @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
+     * @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 = '')
+    public static function isNa(mixed $value = ''): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php
index 06f3866..d9aabfd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php
@@ -30,21 +30,7 @@ class ExcelError
         '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
+    public static function throwError(mixed $value): string
     {
         return in_array($value, self::ERROR_CODES, true) ? $value : self::ERROR_CODES['value'];
     }
@@ -53,10 +39,8 @@ class ExcelError
      * ERROR_TYPE.
      *
      * @param mixed $value Value to check
-     *
-     * @return array|int|string
      */
-    public static function type($value = '')
+    public static function type(mixed $value = ''): array|int|string
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php
index 2e524db..c9a7a0a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php
@@ -20,11 +20,10 @@ class Value
      * @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
+     * @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)
+    public static function isBlank(mixed $value = null): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -37,10 +36,8 @@ class Value
      * IS_REF.
      *
      * @param mixed $value Value to check
-     *
-     * @return bool
      */
-    public static function isRef($value, ?Cell $cell = null)
+    public static function isRef(mixed $value, ?Cell $cell = null): bool
     {
         if ($cell === null || $value === $cell->getCoordinate()) {
             return false;
@@ -49,10 +46,10 @@ class Value
         $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) {
+            if (!empty($worksheet) && $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheet) === null) {
                 return false;
             }
-            [$column, $row] = Coordinate::indexesFromString($cellValue);
+            [$column, $row] = Coordinate::indexesFromString($cellValue ?? '');
             if ($column > 16384 || $row > 1048576) {
                 return false;
             }
@@ -60,7 +57,7 @@ class Value
             return true;
         }
 
-        $namedRange = $cell->getWorksheet()->getParent()->getNamedRange($value);
+        $namedRange = $cell->getWorksheet()->getParentOrThrow()->getNamedRange($value);
 
         return $namedRange instanceof NamedRange;
     }
@@ -71,11 +68,10 @@ class Value
      * @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
+     * @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)
+    public static function isEven(mixed $value = null): array|string|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -96,11 +92,10 @@ class Value
      * @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
+     * @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)
+    public static function isOdd(mixed $value = null): array|string|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -121,11 +116,10 @@ class Value
      * @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
+     * @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)
+    public static function isNumber(mixed $value = null): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -144,11 +138,10 @@ class Value
      * @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
+     * @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)
+    public static function isLogical(mixed $value = null): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -163,11 +156,10 @@ class Value
      * @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
+     * @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)
+    public static function isText(mixed $value = null): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -182,11 +174,10 @@ class Value
      * @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
+     * @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)
+    public static function isNonText(mixed $value = null): array|bool
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -200,10 +191,8 @@ class Value
      *
      * @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)
+    public static function isFormula(mixed $cellReference = '', ?Cell $cell = null): array|bool|string
     {
         if ($cell === null) {
             return ExcelError::REF();
@@ -211,7 +200,7 @@ class Value
 
         $fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
 
-        if (strpos($cellReference, '!') !== false) {
+        if (str_contains($cellReference, '!')) {
             $cellReference = Functions::trimSheetFromCellReference($cellReference);
             $cellReferences = Coordinate::extractAllCellReferencesInRange($cellReference);
             if (count($cellReferences) > 1) {
@@ -227,7 +216,7 @@ class Value
         $worksheetName = str_replace("''", "'", trim($matches[2], "'"));
 
         $worksheet = (!empty($worksheetName))
-            ? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
+            ? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
             : $cell->getWorksheet();
 
         return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
@@ -264,7 +253,7 @@ class Value
                 return (int) $value;
             case 'string':
                 //    Errors
-                if ((strlen($value) > 0) && ($value[0] == '#')) {
+                if (($value !== '') && ($value[0] == '#')) {
                     return $value;
                 }
 
@@ -281,7 +270,7 @@ class Value
      *
      * @param null|mixed $value The value you want tested
      *
-     * @return number N converts values listed in the following table
+     * @return int N converts values listed in the following table
      *        If value is or refers to N returns
      *        A number            1
      *        Text                2
@@ -289,7 +278,7 @@ class Value
      *        An error value      16
      *        Array or Matrix     64
      */
-    public static function type($value = null)
+    public static function type($value = null): int
     {
         $value = Functions::flattenArrayIndexed($value);
         if (is_array($value) && (count($value) > 1)) {
@@ -316,7 +305,7 @@ class Value
             return 64;
         } elseif (is_string($value)) {
             //    Errors
-            if ((strlen($value) > 0) && ($value[0] == '#')) {
+            if (($value !== '') && ($value[0] == '#')) {
                 return 16;
             }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
index 8b53464..22c95e8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Internal;
 
 class MakeMatrix
 {
+    /** @param array $args */
     public static function make(...$args): array
     {
         return $args;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php
deleted file mode 100644
index 92e779e..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php
+++ /dev/null
@@ -1,303 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-use PhpOffice\PhpSpreadsheet\Calculation\Logical\Boolean;
-
-/**
- * @deprecated 1.17.0
- */
-class Logical
-{
-    /**
-     * TRUE.
-     *
-     * Returns the boolean TRUE.
-     *
-     * Excel Function:
-     *        =TRUE()
-     *
-     * @deprecated 1.17.0
-     * Use the TRUE() method in the Logical\Boolean class instead
-     * @see Logical\Boolean::TRUE()
-     *
-     * @return bool True
-     */
-    public static function true(): bool
-    {
-        return Boolean::true();
-    }
-
-    /**
-     * FALSE.
-     *
-     * Returns the boolean FALSE.
-     *
-     * Excel Function:
-     *        =FALSE()
-     *
-     * @deprecated 1.17.0
-     * Use the FALSE() method in the Logical\Boolean class instead
-     * @see Logical\Boolean::FALSE()
-     *
-     * @return bool False
-     */
-    public static function false(): bool
-    {
-        return Boolean::false();
-    }
-
-    /**
-     * LOGICAL_AND.
-     *
-     * Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
-     *
-     * Excel Function:
-     *        =AND(logical1[,logical2[, ...]])
-     *
-     *        The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
-     *            or references that contain logical values.
-     *
-     *        Boolean arguments are treated as True or False as appropriate
-     *        Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
-     *        If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
-     *            holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
-     *
-     * @deprecated 1.17.0
-     * Use the logicalAnd() method in the Logical\Operations class instead
-     * @see Logical\Operations::logicalAnd()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return bool|string the logical AND of the arguments
-     */
-    public static function logicalAnd(...$args)
-    {
-        return Logical\Operations::logicalAnd(...$args);
-    }
-
-    /**
-     * LOGICAL_OR.
-     *
-     * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
-     *
-     * Excel Function:
-     *        =OR(logical1[,logical2[, ...]])
-     *
-     *        The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
-     *            or references that contain logical values.
-     *
-     *        Boolean arguments are treated as True or False as appropriate
-     *        Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
-     *        If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
-     *            holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
-     *
-     * @deprecated 1.17.0
-     * Use the logicalOr() method in the Logical\Operations class instead
-     * @see Logical\Operations::logicalOr()
-     *
-     * @param mixed $args Data values
-     *
-     * @return bool|string the logical OR of the arguments
-     */
-    public static function logicalOr(...$args)
-    {
-        return Logical\Operations::logicalOr(...$args);
-    }
-
-    /**
-     * LOGICAL_XOR.
-     *
-     * Returns the Exclusive Or logical operation for one or more supplied conditions.
-     * i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE,
-     *      and FALSE otherwise.
-     *
-     * Excel Function:
-     *        =XOR(logical1[,logical2[, ...]])
-     *
-     *        The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
-     *            or references that contain logical values.
-     *
-     *        Boolean arguments are treated as True or False as appropriate
-     *        Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
-     *        If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
-     *            holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
-     *
-     * @deprecated 1.17.0
-     * Use the logicalXor() method in the Logical\Operations class instead
-     * @see Logical\Operations::logicalXor()
-     *
-     * @param mixed $args Data values
-     *
-     * @return bool|string the logical XOR of the arguments
-     */
-    public static function logicalXor(...$args)
-    {
-        return Logical\Operations::logicalXor(...$args);
-    }
-
-    /**
-     * NOT.
-     *
-     * Returns the boolean inverse of the argument.
-     *
-     * Excel Function:
-     *        =NOT(logical)
-     *
-     *        The argument must evaluate to a logical value such as TRUE or FALSE
-     *
-     *        Boolean arguments are treated as True or False as appropriate
-     *        Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
-     *        If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
-     *            holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
-     *
-     * @deprecated 1.17.0
-     * Use the NOT() method in the Logical\Operations class instead
-     * @see Logical\Operations::NOT()
-     *
-     * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
-     *
-     * @return array|bool|string the boolean inverse of the argument
-     */
-    public static function NOT($logical = false)
-    {
-        return Logical\Operations::NOT($logical);
-    }
-
-    /**
-     * STATEMENT_IF.
-     *
-     * Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.
-     *
-     * Excel Function:
-     *        =IF(condition[,returnIfTrue[,returnIfFalse]])
-     *
-     *        Condition is any value or expression that can be evaluated to TRUE or FALSE.
-     *            For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,
-     *            the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
-     *            This argument can use any comparison calculation operator.
-     *        ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
-     *            For example, if this argument is the text string "Within budget" and the condition argument
-     *                evaluates to TRUE, then the IF function returns the text "Within budget"
-     *            If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero).
-     *               To display the word TRUE, use the logical value TRUE for this argument.
-     *            ReturnIfTrue can be another formula.
-     *        ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
-     *            For example, if this argument is the text string "Over budget" and the condition argument
-     *                evaluates to FALSE, then the IF function returns the text "Over budget".
-     *            If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
-     *            If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
-     *            ReturnIfFalse can be another formula.
-     *
-     * @deprecated 1.17.0
-     * Use the statementIf() method in the Logical\Conditional class instead
-     * @see Logical\Conditional::statementIf()
-     *
-     * @param mixed $condition Condition to evaluate
-     * @param mixed $returnIfTrue Value to return when condition is true
-     * @param mixed $returnIfFalse Optional value to return when condition is false
-     *
-     * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
-     */
-    public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
-    {
-        return Logical\Conditional::statementIf($condition, $returnIfTrue, $returnIfFalse);
-    }
-
-    /**
-     * STATEMENT_SWITCH.
-     *
-     * Returns corresponding with first match (any data type such as a string, numeric, date, etc).
-     *
-     * Excel Function:
-     *        =SWITCH (expression, value1, result1, value2, result2, ... value_n, result_n [, default])
-     *
-     *        Expression
-     *              The expression to compare to a list of values.
-     *        value1, value2, ... value_n
-     *              A list of values that are compared to expression.
-     *              The SWITCH function is looking for the first value that matches the expression.
-     *        result1, result2, ... result_n
-     *              A list of results. The SWITCH function returns the corresponding result when a value
-     *              matches expression.
-     *         default
-     *              Optional. It is the default to return if expression does not match any of the values
-     *              (value1, value2, ... value_n).
-     *
-     * @deprecated 1.17.0
-     * Use the statementSwitch() method in the Logical\Conditional class instead
-     * @see Logical\Conditional::statementSwitch()
-     *
-     * @param mixed $arguments Statement arguments
-     *
-     * @return mixed The value of matched expression
-     */
-    public static function statementSwitch(...$arguments)
-    {
-        return Logical\Conditional::statementSwitch(...$arguments);
-    }
-
-    /**
-     * IFERROR.
-     *
-     * Excel Function:
-     *        =IFERROR(testValue,errorpart)
-     *
-     * @deprecated 1.17.0
-     * Use the IFERROR() method in the Logical\Conditional class instead
-     * @see Logical\Conditional::IFERROR()
-     *
-     * @param mixed $testValue Value to check, is also the value returned when no error
-     * @param mixed $errorpart Value to return when testValue is an error condition
-     *
-     * @return mixed The value of errorpart or testValue determined by error condition
-     */
-    public static function IFERROR($testValue = '', $errorpart = '')
-    {
-        return Logical\Conditional::IFERROR($testValue, $errorpart);
-    }
-
-    /**
-     * IFNA.
-     *
-     * Excel Function:
-     *        =IFNA(testValue,napart)
-     *
-     * @deprecated 1.17.0
-     * Use the IFNA() method in the Logical\Conditional class instead
-     * @see Logical\Conditional::IFNA()
-     *
-     * @param mixed $testValue Value to check, is also the value returned when not an NA
-     * @param mixed $napart Value to return when testValue is an NA condition
-     *
-     * @return mixed The value of errorpart or testValue determined by error condition
-     */
-    public static function IFNA($testValue = '', $napart = '')
-    {
-        return Logical\Conditional::IFNA($testValue, $napart);
-    }
-
-    /**
-     * IFS.
-     *
-     * Excel Function:
-     *         =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n)
-     *
-     *         testValue1 ... testValue_n
-     *             Conditions to Evaluate
-     *         returnIfTrue1 ... returnIfTrue_n
-     *             Value returned if corresponding testValue (nth) was true
-     *
-     * @deprecated 1.17.0
-     * Use the IFS() method in the Logical\Conditional class instead
-     * @see Logical\Conditional::IFS()
-     *
-     * @param mixed ...$arguments Statement arguments
-     *
-     * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
-     */
-    public static function IFS(...$arguments)
-    {
-        return Logical\Conditional::IFS(...$arguments);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php
index 55d5f32..9ac36c5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Conditional.php
@@ -46,7 +46,7 @@ class Conditional
      *
      * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
      */
-    public static function statementIf($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
+    public static function statementIf(mixed $condition = true, mixed $returnIfTrue = 0, mixed $returnIfFalse = false): mixed
     {
         $condition = ($condition === null) ? true : Functions::flattenSingleValue($condition);
 
@@ -86,7 +86,7 @@ class Conditional
      *
      * @return mixed The value of matched expression
      */
-    public static function statementSwitch(...$arguments)
+    public static function statementSwitch(mixed ...$arguments): mixed
     {
         $result = ExcelError::VALUE();
 
@@ -132,7 +132,7 @@ class Conditional
      *         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 = '')
+    public static function IFERROR(mixed $testValue = '', mixed $errorpart = ''): mixed
     {
         if (is_array($testValue)) {
             return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $errorpart);
@@ -159,7 +159,7 @@ class Conditional
      *         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 = '')
+    public static function IFNA(mixed $testValue = '', mixed $napart = ''): mixed
     {
         if (is_array($testValue)) {
             return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $napart);
@@ -187,7 +187,7 @@ class Conditional
      *
      * @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
      */
-    public static function IFS(...$arguments)
+    public static function IFS(mixed ...$arguments)
     {
         $argumentCount = count($arguments);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
index 2e2faa1..16bb5dd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php
@@ -31,25 +31,9 @@ class Operations
      *
      * @return bool|string the logical AND of the arguments
      */
-    public static function logicalAnd(...$args)
+    public static function logicalAnd(mixed ...$args)
     {
-        $args = Functions::flattenArray($args);
-
-        if (count($args) == 0) {
-            return ExcelError::VALUE();
-        }
-
-        $args = array_filter($args, function ($value) {
-            return $value !== null || (is_string($value) && trim($value) == '');
-        });
-
-        $returnValue = self::countTrueValues($args);
-        if (is_string($returnValue)) {
-            return $returnValue;
-        }
-        $argCount = count($args);
-
-        return ($returnValue > 0) && ($returnValue == $argCount);
+        return self::countTrueValues($args, fn (int $trueValueCount, int $count): bool => $trueValueCount === $count);
     }
 
     /**
@@ -72,24 +56,9 @@ class Operations
      *
      * @return bool|string the logical OR of the arguments
      */
-    public static function logicalOr(...$args)
+    public static function logicalOr(mixed ...$args)
     {
-        $args = Functions::flattenArray($args);
-
-        if (count($args) == 0) {
-            return ExcelError::VALUE();
-        }
-
-        $args = array_filter($args, function ($value) {
-            return $value !== null || (is_string($value) && trim($value) == '');
-        });
-
-        $returnValue = self::countTrueValues($args);
-        if (is_string($returnValue)) {
-            return $returnValue;
-        }
-
-        return $returnValue > 0;
+        return self::countTrueValues($args, fn (int $trueValueCount): bool => $trueValueCount > 0);
     }
 
     /**
@@ -114,24 +83,9 @@ class Operations
      *
      * @return bool|string the logical XOR of the arguments
      */
-    public static function logicalXor(...$args)
+    public static function logicalXor(mixed ...$args)
     {
-        $args = Functions::flattenArray($args);
-
-        if (count($args) == 0) {
-            return ExcelError::VALUE();
-        }
-
-        $args = array_filter($args, function ($value) {
-            return $value !== null || (is_string($value) && trim($value) == '');
-        });
-
-        $returnValue = self::countTrueValues($args);
-        if (is_string($returnValue)) {
-            return $returnValue;
-        }
-
-        return $returnValue % 2 == 1;
+        return self::countTrueValues($args, fn (int $trueValueCount): bool => $trueValueCount % 2 === 1);
     }
 
     /**
@@ -156,7 +110,7 @@ class Operations
      *         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)
+    public static function NOT(mixed $logical = false): array|bool|string
     {
         if (is_array($logical)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $logical);
@@ -176,32 +130,34 @@ class Operations
         return !$logical;
     }
 
-    /**
-     * @return int|string
-     */
-    private static function countTrueValues(array $args)
+    private static function countTrueValues(array $args, callable $func): bool|string
     {
         $trueValueCount = 0;
+        $count = 0;
 
-        foreach ($args as $arg) {
+        $aArgs = Functions::flattenArrayIndexed($args);
+        foreach ($aArgs as $k => $arg) {
+            ++$count;
             // Is it a boolean value?
             if (is_bool($arg)) {
                 $trueValueCount += $arg;
-            } elseif ((is_numeric($arg)) && (!is_string($arg))) {
-                $trueValueCount += ((int) $arg != 0);
             } elseif (is_string($arg)) {
+                $isLiteral = !Functions::isCellValue($k);
                 $arg = mb_strtoupper($arg, 'UTF-8');
-                if (($arg == 'TRUE') || ($arg == Calculation::getTRUE())) {
-                    $arg = true;
-                } elseif (($arg == 'FALSE') || ($arg == Calculation::getFALSE())) {
-                    $arg = false;
+                if ($isLiteral && ($arg == 'TRUE' || $arg == Calculation::getTRUE())) {
+                    ++$trueValueCount;
+                } elseif ($isLiteral && ($arg == 'FALSE' || $arg == Calculation::getFALSE())) {
+                    //$trueValueCount += 0;
                 } else {
-                    return ExcelError::VALUE();
+                    --$count;
                 }
-                $trueValueCount += ($arg != 0);
+            } elseif (is_int($arg) || is_float($arg)) {
+                $trueValueCount += (int) ($arg != 0);
+            } else {
+                --$count;
             }
         }
 
-        return $trueValueCount;
+        return ($count === 0) ? ExcelError::VALUE() : $func($trueValueCount, $count);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
deleted file mode 100644
index 354cb83..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
+++ /dev/null
@@ -1,400 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Address;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Indirect;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Lookup;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Offset;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\RowColumnInformation;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\VLookup;
-use PhpOffice\PhpSpreadsheet\Cell\Cell;
-use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
-
-/**
- * @deprecated 1.18.0
- */
-class LookupRef
-{
-    /**
-     * CELL_ADDRESS.
-     *
-     * Creates a cell address as text, given specified row and column numbers.
-     *
-     * Excel Function:
-     *        =ADDRESS(row, column, [relativity], [referenceStyle], [sheetText])
-     *
-     * @deprecated 1.18.0
-     *      Use the cell() method in the LookupRef\Address class instead
-     * @see LookupRef\Address::cell()
-     *
-     * @param mixed $row Row number to use in the cell reference
-     * @param mixed $column Column number to use in the cell reference
-     * @param int $relativity Flag indicating the type of reference to return
-     *                                1 or omitted    Absolute
-     *                                2               Absolute row; relative column
-     *                                3               Relative row; absolute column
-     *                                4               Relative
-     * @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 array|string $sheetText Optional Name of worksheet to use
-     *
-     * @return array|string
-     */
-    public static function cellAddress($row, $column, $relativity = 1, $referenceStyle = true, $sheetText = '')
-    {
-        return Address::cell($row, $column, $relativity, $referenceStyle, $sheetText);
-    }
-
-    /**
-     * COLUMN.
-     *
-     * Returns the column number of the given cell reference
-     *     If the cell reference is a range of cells, COLUMN returns the column numbers of each column
-     *        in the reference as a horizontal array.
-     *     If cell reference is omitted, and the function is being called through the calculation engine,
-     *        then it is assumed to be the reference of the cell in which the COLUMN function appears;
-     *        otherwise this function returns 1.
-     *
-     * Excel Function:
-     *        =COLUMN([cellAddress])
-     *
-     * @deprecated 1.18.0
-     *      Use the COLUMN() method in the LookupRef\RowColumnInformation class instead
-     * @see LookupRef\RowColumnInformation::COLUMN()
-     *
-     * @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
-     *
-     * @return int|int[]|string
-     */
-    public static function COLUMN($cellAddress = null, ?Cell $cell = null)
-    {
-        return RowColumnInformation::COLUMN($cellAddress, $cell);
-    }
-
-    /**
-     * COLUMNS.
-     *
-     * Returns the number of columns in an array or reference.
-     *
-     * Excel Function:
-     *        =COLUMNS(cellAddress)
-     *
-     * @deprecated 1.18.0
-     *      Use the COLUMNS() method in the LookupRef\RowColumnInformation class instead
-     * @see LookupRef\RowColumnInformation::COLUMNS()
-     *
-     * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
-     *                                          for which you want the number of columns
-     *
-     * @return int|string The number of columns in cellAddress, or a string if arguments are invalid
-     */
-    public static function COLUMNS($cellAddress = null)
-    {
-        return RowColumnInformation::COLUMNS($cellAddress);
-    }
-
-    /**
-     * ROW.
-     *
-     * Returns the row number of the given cell reference
-     *     If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
-     *        as a vertical array.
-     *     If cell reference is omitted, and the function is being called through the calculation engine,
-     *        then it is assumed to be the reference of the cell in which the ROW function appears;
-     *        otherwise this function returns 1.
-     *
-     * Excel Function:
-     *        =ROW([cellAddress])
-     *
-     * @deprecated 1.18.0
-     *      Use the ROW() method in the LookupRef\RowColumnInformation class instead
-     * @see LookupRef\RowColumnInformation::ROW()
-     *
-     * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
-     *
-     * @return int|mixed[]|string
-     */
-    public static function ROW($cellAddress = null, ?Cell $cell = null)
-    {
-        return RowColumnInformation::ROW($cellAddress, $cell);
-    }
-
-    /**
-     * ROWS.
-     *
-     * Returns the number of rows in an array or reference.
-     *
-     * Excel Function:
-     *        =ROWS(cellAddress)
-     *
-     * @deprecated 1.18.0
-     *      Use the ROWS() method in the LookupRef\RowColumnInformation class instead
-     * @see LookupRef\RowColumnInformation::ROWS()
-     *
-     * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
-     *                                          for which you want the number of rows
-     *
-     * @return int|string The number of rows in cellAddress, or a string if arguments are invalid
-     */
-    public static function ROWS($cellAddress = null)
-    {
-        return RowColumnInformation::ROWS($cellAddress);
-    }
-
-    /**
-     * HYPERLINK.
-     *
-     * Excel Function:
-     *        =HYPERLINK(linkURL,displayName)
-     *
-     * @deprecated 1.18.0
-     *      Use the set() method in the LookupRef\Hyperlink class instead
-     * @see LookupRef\Hyperlink::set()
-     *
-     * @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 $cell The cell to set the hyperlink in
-     *
-     * @return string The value of $displayName (or $linkURL if $displayName was blank)
-     */
-    public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $cell = null)
-    {
-        return LookupRef\Hyperlink::set($linkURL, $displayName, $cell);
-    }
-
-    /**
-     * INDIRECT.
-     *
-     * Returns the reference specified by a text string.
-     * References are immediately evaluated to display their contents.
-     *
-     * Excel Function:
-     *        =INDIRECT(cellAddress)
-     *
-     * @deprecated 1.18.0
-     *      Use the INDIRECT() method in the LookupRef\Indirect class instead
-     * @see LookupRef\Indirect::INDIRECT()
-     *
-     * @param array|string $cellAddress $cellAddress The cell address of 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
-     *
-     * NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010
-     */
-    public static function INDIRECT($cellAddress, Cell $cell)
-    {
-        return Indirect::INDIRECT($cellAddress, true, $cell);
-    }
-
-    /**
-     * OFFSET.
-     *
-     * Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.
-     * The reference that is returned can be a single cell or a range of cells. You can specify the number of rows and
-     * the number of columns to be returned.
-     *
-     * Excel Function:
-     *        =OFFSET(cellAddress, rows, cols, [height], [width])
-     *
-     * @deprecated 1.18.0
-     *      Use the OFFSET() method in the LookupRef\Offset class instead
-     * @see LookupRef\Offset::OFFSET()
-     *
-     * @param null|string $cellAddress The reference from which you want to base the offset.
-     *                                     Reference must refer to a cell or range of adjacent cells;
-     *                                     otherwise, OFFSET returns the #VALUE! error value.
-     * @param mixed $rows The number of rows, up or down, that you want the upper-left cell to refer to.
-     *                        Using 5 as the rows argument specifies that the upper-left cell in the
-     *                        reference is five rows below reference. Rows can be positive (which means
-     *                        below the starting reference) or negative (which means above the starting
-     *                        reference).
-     * @param mixed $columns The number of columns, to the left or right, that you want the upper-left cell
-     *                           of the result to refer to. Using 5 as the cols argument specifies that the
-     *                           upper-left cell in the reference is five columns to the right of reference.
-     *                           Cols can be positive (which means to the right of the starting reference)
-     *                           or negative (which means to the left of the starting reference).
-     * @param mixed $height The height, in number of rows, that you want the returned reference to be.
-     *                          Height must be a positive number.
-     * @param mixed $width The width, in number of columns, that you want the returned reference to be.
-     *                         Width must be a positive number.
-     *
-     * @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 $cell = null)
-    {
-        return Offset::OFFSET($cellAddress, $rows, $columns, $height, $width, $cell);
-    }
-
-    /**
-     * CHOOSE.
-     *
-     * Uses lookup_value to return a value from the list of value arguments.
-     * Use CHOOSE to select one of up to 254 values based on the lookup_value.
-     *
-     * Excel Function:
-     *        =CHOOSE(index_num, value1, [value2], ...)
-     *
-     * @deprecated 1.18.0
-     *      Use the choose() method in the LookupRef\Selection class instead
-     * @see LookupRef\Selection::choose()
-     *
-     * @return mixed The selected value
-     */
-    public static function CHOOSE(...$chooseArgs)
-    {
-        return LookupRef\Selection::choose(...$chooseArgs);
-    }
-
-    /**
-     * MATCH.
-     *
-     * The MATCH function searches for a specified item in a range of cells
-     *
-     * Excel Function:
-     *        =MATCH(lookup_value, lookup_array, [match_type])
-     *
-     * @deprecated 1.18.0
-     *      Use the MATCH() method in the LookupRef\ExcelMatch class instead
-     * @see LookupRef\ExcelMatch::MATCH()
-     *
-     * @param mixed $lookupValue The value that you want to match in lookup_array
-     * @param mixed $lookupArray The range of cells being searched
-     * @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 array|int|string The relative position of the found item
-     */
-    public static function MATCH($lookupValue, $lookupArray, $matchType = 1)
-    {
-        return LookupRef\ExcelMatch::MATCH($lookupValue, $lookupArray, $matchType);
-    }
-
-    /**
-     * INDEX.
-     *
-     * Uses an index to choose a value from a reference or array
-     *
-     * Excel Function:
-     *        =INDEX(range_array, row_num, [column_num])
-     *
-     * @deprecated 1.18.0
-     *      Use the index() method in the LookupRef\Matrix class instead
-     * @see LookupRef\Matrix::index()
-     *
-     * @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.
-     * @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.
-     * @param mixed $matrix
-     *
-     * @return mixed the value of a specified cell or array of cells
-     */
-    public static function INDEX($matrix, $rowNum = 0, $columnNum = 0)
-    {
-        return Matrix::index($matrix, $rowNum, $columnNum);
-    }
-
-    /**
-     * TRANSPOSE.
-     *
-     * @deprecated 1.18.0
-     *      Use the transpose() method in the LookupRef\Matrix class instead
-     * @see LookupRef\Matrix::transpose()
-     *
-     * @param array $matrixData A matrix of values
-     *
-     * @return array
-     *
-     * Unlike the Excel TRANSPOSE function, which will only work on a single row or column,
-     *     this function will transpose a full matrix
-     */
-    public static function TRANSPOSE($matrixData)
-    {
-        return Matrix::transpose($matrixData);
-    }
-
-    /**
-     * VLOOKUP
-     * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
-     *     in the same row based on the index_number.
-     *
-     * @deprecated 1.18.0
-     *      Use the lookup() method in the LookupRef\VLookup class instead
-     * @see LookupRef\VLookup::lookup()
-     *
-     * @param mixed $lookup_value The value that you want to match in lookup_array
-     * @param mixed $lookup_array The range of cells being searched
-     * @param mixed $index_number The column number in table_array from which the matching value must be returned.
-     *                                The first column is 1.
-     * @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
-     *
-     * @return mixed The value of the found cell
-     */
-    public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
-    {
-        return VLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
-    }
-
-    /**
-     * HLOOKUP
-     * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value
-     *     in the same column based on the index_number.
-     *
-     * @deprecated 1.18.0
-     *      Use the lookup() method in the LookupRef\HLookup class instead
-     * @see LookupRef\HLookup::lookup()
-     *
-     * @param mixed $lookup_value The value that you want to match in lookup_array
-     * @param mixed $lookup_array The range of cells being searched
-     * @param mixed $index_number The row number in table_array from which the matching value must be returned.
-     *                                The first row is 1.
-     * @param mixed $not_exact_match determines if you are looking for an exact match based on lookup_value
-     *
-     * @return mixed The value of the found cell
-     */
-    public static function HLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
-    {
-        return HLookup::lookup($lookup_value, $lookup_array, $index_number, $not_exact_match);
-    }
-
-    /**
-     * LOOKUP
-     * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
-     *
-     * @deprecated 1.18.0
-     *      Use the lookup() method in the LookupRef\Lookup class instead
-     * @see LookupRef\Lookup::lookup()
-     *
-     * @param mixed $lookup_value The value that you want to match in lookup_array
-     * @param mixed $lookup_vector The range of cells being searched
-     * @param null|mixed $result_vector The column from which the matching value must be returned
-     *
-     * @return mixed The value of the found cell
-     */
-    public static function LOOKUP($lookup_value, $lookup_vector, $result_vector = null)
-    {
-        return Lookup::lookup($lookup_value, $lookup_vector, $result_vector);
-    }
-
-    /**
-     * FORMULATEXT.
-     *
-     * @deprecated 1.18.0
-     *      Use the text() method in the LookupRef\Formula class instead
-     * @see LookupRef\Formula::text()
-     *
-     * @param mixed $cellReference The cell to check
-     * @param Cell $cell The current cell (containing this formula)
-     *
-     * @return string
-     */
-    public static function FORMULATEXT($cellReference = '', ?Cell $cell = null)
-    {
-        return LookupRef\Formula::text($cellReference, $cell);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php
index 0d2db8b..0a5347b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php
@@ -44,15 +44,14 @@ class Address
      * @param mixed $sheetName Optional Name of worksheet to use
      *                      Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed as the $testValue argument, then the returned result will also be
+     * @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 = '')
+    public static function cell(mixed $row, mixed $column, mixed $relativity = 1, mixed $referenceStyle = true, mixed $sheetName = ''): array|string
     {
         if (
-            is_array($row) || is_array($column) ||
-            is_array($relativity) || is_array($referenceStyle) || is_array($sheetName)
+            is_array($row) || is_array($column)
+            || is_array($relativity) || is_array($referenceStyle) || is_array($sheetName)
         ) {
             return self::evaluateArrayArguments(
                 [self::class, __FUNCTION__],
@@ -83,10 +82,10 @@ class Address
         return self::formatAsR1C1($row, $column, $relativity, $sheetName);
     }
 
-    private static function sheetName(string $sheetName)
+    private static function sheetName(string $sheetName): string
     {
         if ($sheetName > '') {
-            if (strpos($sheetName, ' ') !== false || strpos($sheetName, '[') !== false) {
+            if (str_contains($sheetName, ' ') || str_contains($sheetName, '[')) {
                 $sheetName = "'{$sheetName}'";
             }
             $sheetName .= '!';
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
index e09477c..43e89c9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
@@ -30,9 +30,9 @@ 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 array|int|string The relative position of the found item
+     * @return array|float|int|string The relative position of the found item
      */
-    public static function MATCH($lookupValue, $lookupArray, $matchType = self::MATCHTYPE_LARGEST_VALUE)
+    public static function MATCH(mixed $lookupValue, mixed $lookupArray, mixed $matchType = self::MATCHTYPE_LARGEST_VALUE): array|string|int|float
     {
         if (is_array($lookupValue)) {
             return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $matchType);
@@ -63,20 +63,11 @@ class ExcelMatch
             $lookupValue = StringHelper::strToLower($lookupValue);
         }
 
-        $valueKey = null;
-        switch ($matchType) {
-            case self::MATCHTYPE_LARGEST_VALUE:
-                $valueKey = self::matchLargestValue($lookupArray, $lookupValue, $keySet);
-
-                break;
-            case self::MATCHTYPE_FIRST_VALUE:
-                $valueKey = self::matchFirstValue($lookupArray, $lookupValue);
-
-                break;
-            case self::MATCHTYPE_SMALLEST_VALUE:
-            default:
-                $valueKey = self::matchSmallestValue($lookupArray, $lookupValue);
-        }
+        $valueKey = match ($matchType) {
+            self::MATCHTYPE_LARGEST_VALUE => self::matchLargestValue($lookupArray, $lookupValue, $keySet),
+            self::MATCHTYPE_FIRST_VALUE => self::matchFirstValue($lookupArray, $lookupValue),
+            default => self::matchSmallestValue($lookupArray, $lookupValue),
+        };
 
         if ($valueKey !== null) {
             return ++$valueKey;
@@ -86,12 +77,7 @@ class ExcelMatch
         return ExcelError::NA();
     }
 
-    /**
-     * @param mixed $lookupValue
-     *
-     * @return mixed
-     */
-    private static function matchFirstValue(array $lookupArray, $lookupValue)
+    private static function matchFirstValue(array $lookupArray, mixed $lookupValue): int|string|null
     {
         if (is_string($lookupValue)) {
             $valueIsString = true;
@@ -127,12 +113,7 @@ class ExcelMatch
         return null;
     }
 
-    /**
-     * @param mixed $lookupValue
-     *
-     * @return mixed
-     */
-    private static function matchLargestValue(array $lookupArray, $lookupValue, array $keySet)
+    private static function matchLargestValue(array $lookupArray, mixed $lookupValue, array $keySet): mixed
     {
         if (is_string($lookupValue)) {
             if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
@@ -166,12 +147,7 @@ class ExcelMatch
         return null;
     }
 
-    /**
-     * @param mixed $lookupValue
-     *
-     * @return mixed
-     */
-    private static function matchSmallestValue(array $lookupArray, $lookupValue)
+    private static function matchSmallestValue(array $lookupArray, mixed $lookupValue): int|string|null
     {
         $valueKey = null;
         if (is_string($lookupValue)) {
@@ -212,10 +188,7 @@ class ExcelMatch
         return $valueKey;
     }
 
-    /**
-     * @param mixed $lookupValue
-     */
-    private static function validateLookupValue($lookupValue): void
+    private static function validateLookupValue(mixed $lookupValue): void
     {
         // Lookup_value type has to be number, text, or logical values
         if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
@@ -223,10 +196,7 @@ class ExcelMatch
         }
     }
 
-    /**
-     * @param mixed $matchType
-     */
-    private static function validateMatchType($matchType): int
+    private static function validateMatchType(mixed $matchType): int
     {
         // Match_type is 0, 1 or -1
         // However Excel accepts other numeric values,
@@ -254,10 +224,7 @@ class ExcelMatch
         }
     }
 
-    /**
-     * @param mixed $matchType
-     */
-    private static function prepareLookupArray(array $lookupArray, $matchType): array
+    private static function prepareLookupArray(array $lookupArray, mixed $matchType): array
     {
         // Lookup_array should contain only number, text, or logical values, or empty (null) cells
         foreach ($lookupArray as $i => $value) {
@@ -270,8 +237,8 @@ class ExcelMatch
                 $lookupArray[$i] = StringHelper::strToLower($value);
             }
             if (
-                ($value === null) &&
-                (($matchType == self::MATCHTYPE_LARGEST_VALUE) || ($matchType == self::MATCHTYPE_SMALLEST_VALUE))
+                ($value === null)
+                && (($matchType == self::MATCHTYPE_LARGEST_VALUE) || ($matchType == self::MATCHTYPE_SMALLEST_VALUE))
             ) {
                 unset($lookupArray[$i]);
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
index 74fa832..e3b6cbe 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php
@@ -6,14 +6,7 @@ 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)
+    public static function filter(array $lookupArray, mixed $matchArray, mixed $ifEmpty = null): mixed
     {
         if (!is_array($matchArray)) {
             return ExcelError::VALUE();
@@ -52,9 +45,7 @@ class Filter
 
         return array_filter(
             array_values($lookupArray),
-            function ($index) use ($matchArray): bool {
-                return (bool) $matchArray[$index];
-            },
+            fn ($index): bool => (bool) $matchArray[$index],
             ARRAY_FILTER_USE_KEY
         );
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
index 834fe52..5c7f405 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php
@@ -12,11 +12,9 @@ class Formula
      * FORMULATEXT.
      *
      * @param mixed $cellReference The cell to check
-     * @param Cell $cell The current cell (containing this formula)
-     *
-     * @return string
+     * @param ?Cell $cell The current cell (containing this formula)
      */
-    public static function text($cellReference = '', ?Cell $cell = null)
+    public static function text(mixed $cellReference = '', ?Cell $cell = null): string
     {
         if ($cell === null) {
             return ExcelError::REF();
@@ -27,13 +25,13 @@ class Formula
         $cellReference = $matches[6] . $matches[7];
         $worksheetName = trim($matches[3], "'");
         $worksheet = (!empty($worksheetName))
-            ? $cell->getWorksheet()->getParent()->getSheetByName($worksheetName)
+            ? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
             : $cell->getWorksheet();
 
         if (
-            $worksheet === null ||
-            !$worksheet->cellExists($cellReference) ||
-            !$worksheet->getCell($cellReference)->isFormula()
+            $worksheet === null
+            || !$worksheet->cellExists($cellReference)
+            || !$worksheet->getCell($cellReference)->isFormula()
         ) {
             return ExcelError::NA();
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
index e2d27bd..fd83700 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php
@@ -25,9 +25,9 @@ class HLookup extends LookupBase
      *
      * @return mixed The value of the found cell
      */
-    public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
+    public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
     {
-        if (is_array($lookupValue)) {
+        if (is_array($lookupValue) || is_array($indexNumber)) {
             return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
         }
 
@@ -64,7 +64,7 @@ class HLookup extends LookupBase
      * @param mixed $lookupValue The value that you want to match in lookup_array
      * @param  int|string $column
      */
-    private static function hLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
+    private static function hLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
     {
         $lookupLower = StringHelper::strToLower((string) $lookupValue);
 
@@ -76,8 +76,8 @@ class HLookup extends LookupBase
             $cellDataLower = StringHelper::strToLower((string) $rowData);
 
             if (
-                $notExactMatch &&
-                (($bothNumeric && $rowData > $lookupValue) || ($bothNotNumeric && $cellDataLower > $lookupLower))
+                $notExactMatch
+                && (($bothNumeric && $rowData > $lookupValue) || ($bothNotNumeric && $cellDataLower > $lookupLower))
             ) {
                 break;
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
index 76a194b..191144b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php
@@ -49,7 +49,7 @@ class Helpers
             $cellAddress = $cellAddress1;
             $a1 = self::CELLADDRESS_USE_A1;
         }
-        if (strpos($cellAddress, ':') !== false) {
+        if (str_contains($cellAddress, ':')) {
             [$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
         }
         $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1, $baseRow, $baseCol);
@@ -60,13 +60,13 @@ class Helpers
     public static function extractWorksheet(string $cellAddress, Cell $cell): array
     {
         $sheetName = '';
-        if (strpos($cellAddress, '!') !== false) {
+        if (str_contains($cellAddress, '!')) {
             [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
             $sheetName = trim($sheetName, "'");
         }
 
         $worksheet = ($sheetName !== '')
-            ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+            ? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($sheetName)
             : $cell->getWorksheet();
 
         return [$cellAddress, $worksheet, $sheetName];
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
index 5387833..455442a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php
@@ -16,11 +16,11 @@ 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 $cell 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)
+     * @return string The value of $displayName (or $linkURL if $displayName was blank)
      */
-    public static function set($linkURL = '', $displayName = null, ?Cell $cell = null)
+    public static function set(mixed $linkURL = '', mixed $displayName = null, ?Cell $cell = null): string
     {
         $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL);
         $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
index bd76ce9..d53900d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php
@@ -19,7 +19,7 @@ class Indirect
      * @param mixed $a1fmt Expect bool Helpers::CELLADDRESS_USE_A1 or CELLADDRESS_USE_R1C1,
      *                      but can be provided as numeric which is cast to bool
      */
-    private static function a1Format($a1fmt): bool
+    private static function a1Format(mixed $a1fmt): bool
     {
         $a1fmt = Functions::flattenSingleValue($a1fmt);
         if ($a1fmt === null) {
@@ -34,10 +34,8 @@ class Indirect
 
     /**
      * Convert cellAddress to string, verify not null string.
-     *
-     * @param array|string $cellAddress
      */
-    private static function validateAddress($cellAddress): string
+    private static function validateAddress(array|string|null $cellAddress): string
     {
         $cellAddress = Functions::flattenSingleValue($cellAddress);
         if (!is_string($cellAddress) || !$cellAddress) {
@@ -63,7 +61,7 @@ class Indirect
      *
      * @return array|string An array containing a cell or range of cells, or a string on error
      */
-    public static function INDIRECT($cellAddress, $a1fmt, Cell $cell)
+    public static function INDIRECT($cellAddress, mixed $a1fmt, Cell $cell): string|array
     {
         [$baseCol, $baseRow] = Coordinate::indexesFromString($cell->getCoordinate());
 
@@ -84,13 +82,13 @@ class Indirect
 
         try {
             [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName, $baseRow, $baseCol);
-        } catch (Exception $e) {
+        } catch (Exception) {
             return ExcelError::REF();
         }
 
         if (
-            (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches)) ||
-            (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
+            (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches))
+            || (($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
         ) {
             return ExcelError::REF();
         }
@@ -101,10 +99,10 @@ class Indirect
     /**
      * Extract range values.
      *
-     * @return mixed Array of values in range if range contains more than one element.
+     * @return array Array of values in range if range contains more than one element.
      *                  Otherwise, a single value is returned.
      */
-    private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress)
+    private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress): array
     {
         return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
             ->extractCellRange($cellAddress, $worksheet, false);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
index 76a360b..b187620 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php
@@ -4,7 +4,6 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
-use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
 
 class Lookup
 {
@@ -20,7 +19,7 @@ class Lookup
      *
      * @return mixed The value of the found cell
      */
-    public static function lookup($lookupValue, $lookupVector, $resultVector = null)
+    public static function lookup(mixed $lookupValue, mixed $lookupVector, $resultVector = null): mixed
     {
         if (is_array($lookupValue)) {
             return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $lookupValue, $lookupVector, $resultVector);
@@ -34,12 +33,12 @@ class Lookup
         $lookupColumns = self::columnCount($lookupVector);
         // we correctly orient our results
         if (($lookupRows === 1 && $lookupColumns > 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
-            $lookupVector = LookupRef\Matrix::transpose($lookupVector);
+            $lookupVector = Matrix::transpose($lookupVector);
             $lookupRows = self::rowCount($lookupVector);
             $lookupColumns = self::columnCount($lookupVector);
         }
 
-        $resultVector = self::verifyResultVector($lookupVector, $resultVector);
+        $resultVector = self::verifyResultVector($resultVector ?? $lookupVector);
 
         if ($lookupRows === 2 && !$hasResultVector) {
             $resultVector = array_pop($lookupVector);
@@ -78,18 +77,14 @@ class Lookup
         return $lookupVector;
     }
 
-    private static function verifyResultVector(array $lookupVector, $resultVector)
+    private static function verifyResultVector(array $resultVector): array
     {
-        if ($resultVector === null) {
-            $resultVector = $lookupVector;
-        }
-
         $resultRows = self::rowCount($resultVector);
         $resultColumns = self::columnCount($resultVector);
 
         // we correctly orient our results
         if ($resultRows === 1 && $resultColumns > 1) {
-            $resultVector = LookupRef\Matrix::transpose($resultVector);
+            $resultVector = Matrix::transpose($resultVector);
         }
 
         return $resultVector;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
index 5fe1676..7d21cce 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php
@@ -7,16 +7,14 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 abstract class LookupBase
 {
-    /**
-     * @param mixed $lookup_array
-     */
-    protected static function validateLookupArray($lookup_array): void
+    protected static function validateLookupArray(mixed $lookup_array): void
     {
         if (!is_array($lookup_array)) {
             throw new Exception(ExcelError::REF());
         }
     }
 
+    /** @param float|int|string $index_number */
     protected static function validateIndexLookup(array $lookup_array, $index_number): int
     {
         // index_number must be a number greater than or equal to 1.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php
index 2282bf4..74c313c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php
@@ -8,10 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class LookupRefValidations
 {
-    /**
-     * @param mixed $value
-     */
-    public static function validateInt($value): int
+    public static function validateInt(mixed $value): int
     {
         if (!is_numeric($value)) {
             if (ErrorValue::isError($value)) {
@@ -24,10 +21,7 @@ class LookupRefValidations
         return (int) floor((float) $value);
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function validatePositiveInt($value, bool $allowZero = true): int
+    public static function validatePositiveInt(mixed $value, bool $allowZero = true): int
     {
         $value = self::validateInt($value);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
index d7d15d4..228b464 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php
@@ -23,18 +23,16 @@ class Matrix
      */
     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));
+        return count($values, COUNT_RECURSIVE) > 1
+            && (count($values, COUNT_NORMAL) === 1 || count($values, COUNT_RECURSIVE) === count($values, COUNT_NORMAL));
     }
 
     /**
      * TRANSPOSE.
      *
      * @param array|mixed $matrixData A matrix of values
-     *
-     * @return array
      */
-    public static function transpose($matrixData)
+    public static function transpose($matrixData): array
     {
         $returnMatrix = [];
         if (!is_array($matrixData)) {
@@ -76,14 +74,13 @@ class Matrix
      *         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 = null)
+    public static function index(mixed $matrix, mixed $rowNum = 0, mixed $columnNum = null): mixed
     {
         if (is_array($rowNum) || is_array($columnNum)) {
             return self::evaluateArrayArgumentsSubsetFrom([self::class, __FUNCTION__], 1, $matrix, $rowNum, $columnNum);
         }
 
         $rowNum = $rowNum ?? 0;
-        $originalColumnNum = $columnNum;
         $columnNum = $columnNum ?? 0;
 
         try {
@@ -93,6 +90,17 @@ class Matrix
             return $e->getMessage();
         }
 
+        if (is_array($matrix) && count($matrix) === 1 && $rowNum > 1) {
+            $matrixKey = array_keys($matrix)[0];
+            if (is_array($matrix[$matrixKey])) {
+                $tempMatrix = [];
+                foreach ($matrix[$matrixKey] as $key => $value) {
+                    $tempMatrix[$key] = [$value];
+                }
+                $matrix = $tempMatrix;
+            }
+        }
+
         if (!is_array($matrix) || ($rowNum > count($matrix))) {
             return ExcelError::REF();
         }
@@ -103,9 +111,6 @@ class Matrix
         if ($columnNum > count($columnKeys)) {
             return ExcelError::REF();
         }
-        if ($originalColumnNum === null && 1 < count($columnKeys)) {
-            return ExcelError::REF();
-        }
 
         if ($columnNum === 0) {
             return self::extractRowValue($matrix, $rowKeys, $rowNum);
@@ -114,9 +119,7 @@ class Matrix
         $columnNum = $columnKeys[--$columnNum];
         if ($rowNum === 0) {
             return array_map(
-                function ($value) {
-                    return [$value];
-                },
+                fn ($value): array => [$value],
                 array_column($matrix, $columnNum)
             );
         }
@@ -125,7 +128,7 @@ class Matrix
         return $matrix[$rowNum][$columnNum];
     }
 
-    private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum)
+    private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum): mixed
     {
         if ($rowNum === 0) {
             return $matrix;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
index 02a2558..260ccc3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
@@ -39,9 +39,9 @@ class Offset
      * @param mixed $width The width, in number of columns, that you want the returned reference to be.
      *                         Width must be a positive number.
      *
-     * @return array|int|string An array containing a cell or range of cells, or a string on error
+     * @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 $cell = null)
+    public static function OFFSET(?string $cellAddress = null, mixed $rows = 0, mixed $columns = 0, mixed $height = null, mixed $width = null, ?Cell $cell = null): string|array
     {
         $rows = Functions::flattenSingleValue($rows);
         $columns = Functions::flattenSingleValue($columns);
@@ -91,24 +91,24 @@ class Offset
         return self::extractRequiredCells($worksheet, $cellAddress);
     }
 
-    private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress)
+    private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress): array
     {
         return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
             ->extractCellRange($cellAddress, $worksheet, false);
     }
 
-    private static function extractWorksheet($cellAddress, Cell $cell): array
+    private static function extractWorksheet(?string $cellAddress, Cell $cell): array
     {
-        $cellAddress = self::assessCellAddress($cellAddress, $cell);
+        $cellAddress = self::assessCellAddress($cellAddress ?? '', $cell);
 
         $sheetName = '';
-        if (strpos($cellAddress, '!') !== false) {
+        if (str_contains($cellAddress, '!')) {
             [$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
             $sheetName = trim($sheetName, "'");
         }
 
         $worksheet = ($sheetName !== '')
-            ? $cell->getWorksheet()->getParent()->getSheetByName($sheetName)
+            ? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($sheetName)
             : $cell->getWorksheet();
 
         return [$cellAddress, $worksheet];
@@ -123,7 +123,7 @@ class Offset
         return $cellAddress;
     }
 
-    private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns)
+    private static function adjustEndCellColumnForWidth(string $endCellColumn, mixed $width, int $startCellColumn, mixed $columns): int
     {
         $endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
         if (($width !== null) && (!is_object($width))) {
@@ -135,7 +135,7 @@ class Offset
         return $endCellColumn;
     }
 
-    private static function adustEndCellRowForHeight($height, int $startCellRow, $rows, $endCellRow): int
+    private static function adustEndCellRowForHeight(mixed $height, int $startCellRow, mixed $rows, mixed $endCellRow): int
     {
         if (($height !== null) && (!is_object($height))) {
             $endCellRow = $startCellRow + (int) $height - 1;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
index 8bce07e..ea3ce44 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
@@ -22,7 +22,7 @@ class RowColumnInformation
 
     private static function cellColumn(?Cell $cell): int
     {
-        return ($cell !== null) ? (int) Coordinate::columnIndexFromString($cell->getColumn()) : 1;
+        return ($cell !== null) ? Coordinate::columnIndexFromString($cell->getColumn()) : 1;
     }
 
     /**
@@ -42,7 +42,7 @@ class RowColumnInformation
      *
      * @return int|int[]
      */
-    public static function COLUMN($cellAddress = null, ?Cell $cell = null)
+    public static function COLUMN($cellAddress = null, ?Cell $cell = null): int|array
     {
         if (self::cellAddressNullOrWhitespace($cellAddress)) {
             return self::cellColumn($cell);
@@ -52,7 +52,7 @@ class RowColumnInformation
             foreach ($cellAddress as $columnKey => $value) {
                 $columnKey = (string) preg_replace('/[^a-z]/i', '', $columnKey);
 
-                return (int) Coordinate::columnIndexFromString($columnKey);
+                return Coordinate::columnIndexFromString($columnKey);
             }
 
             return self::cellColumn($cell);
@@ -64,20 +64,22 @@ class RowColumnInformation
             [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
         }
         [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
-        if (strpos($cellAddress, ':') !== false) {
+        $cellAddress ??= '';
+
+        if (str_contains($cellAddress, ':')) {
             [$startAddress, $endAddress] = explode(':', $cellAddress);
             $startAddress = (string) preg_replace('/[^a-z]/i', '', $startAddress);
             $endAddress = (string) preg_replace('/[^a-z]/i', '', $endAddress);
 
             return range(
-                (int) Coordinate::columnIndexFromString($startAddress),
-                (int) Coordinate::columnIndexFromString($endAddress)
+                Coordinate::columnIndexFromString($startAddress),
+                Coordinate::columnIndexFromString($endAddress)
             );
         }
 
         $cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
 
-        return (int) Coordinate::columnIndexFromString($cellAddress);
+        return Coordinate::columnIndexFromString($cellAddress);
     }
 
     /**
@@ -133,9 +135,9 @@ class RowColumnInformation
      *
      * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
      *
-     * @return int|mixed[]|string
+     * @return int|mixed[]
      */
-    public static function ROW($cellAddress = null, ?Cell $cell = null)
+    public static function ROW($cellAddress = null, ?Cell $cell = null): int|array
     {
         if (self::cellAddressNullOrWhitespace($cellAddress)) {
             return self::cellRow($cell);
@@ -157,15 +159,14 @@ class RowColumnInformation
             [,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
         }
         [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
-        if (strpos($cellAddress, ':') !== false) {
+        $cellAddress ??= '';
+        if (str_contains($cellAddress, ':')) {
             [$startAddress, $endAddress] = explode(':', $cellAddress);
-            $startAddress = (string) preg_replace('/\D/', '', $startAddress);
-            $endAddress = (string) preg_replace('/\D/', '', $endAddress);
+            $startAddress = (int) (string) preg_replace('/\D/', '', $startAddress);
+            $endAddress = (int) (string) preg_replace('/\D/', '', $endAddress);
 
             return array_map(
-                function ($value) {
-                    return [$value];
-                },
+                fn ($value): array => [$value],
                 range($startAddress, $endAddress)
             );
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
index 0ac9177..53396c9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
@@ -24,7 +24,7 @@ class Selection
      *
      * @return mixed The selected value
      */
-    public static function choose($chosenEntry, ...$chooseArgs)
+    public static function choose(mixed $chosenEntry, mixed ...$chooseArgs): mixed
     {
         if (is_array($chosenEntry)) {
             return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $chosenEntry, ...$chooseArgs);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
index ff78fbe..9ad47b4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php
@@ -29,7 +29,7 @@ class Sort extends LookupRefValidations
      *
      * @return mixed The sorted values from the sort range
      */
-    public static function sort($sortArray, $sortIndex = 1, $sortOrder = self::ORDER_ASCENDING, $byColumn = false)
+    public static function sort(mixed $sortArray, mixed $sortIndex = 1, mixed $sortOrder = self::ORDER_ASCENDING, mixed $byColumn = false): mixed
     {
         if (!is_array($sortArray)) {
             // Scalars are always returned "as is"
@@ -79,7 +79,7 @@ class Sort extends LookupRefValidations
      *
      * @return mixed The sorted values from the sort range
      */
-    public static function sortBy($sortArray, ...$args)
+    public static function sortBy(mixed $sortArray, mixed ...$args): mixed
     {
         if (!is_array($sortArray)) {
             // Scalars are always returned "as is"
@@ -118,11 +118,7 @@ class Sort extends LookupRefValidations
         return array_values($sortArray);
     }
 
-    /**
-     * @param mixed $sortIndex
-     * @param mixed $sortOrder
-     */
-    private static function validateScalarArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+    private static function validateScalarArgumentsForSort(mixed &$sortIndex, mixed &$sortOrder, int $sortArraySize): void
     {
         if (is_array($sortIndex) || is_array($sortOrder)) {
             throw new Exception(ExcelError::VALUE());
@@ -137,10 +133,7 @@ class Sort extends LookupRefValidations
         $sortOrder = self::validateSortOrder($sortOrder);
     }
 
-    /**
-     * @param mixed $sortVector
-     */
-    private static function validateSortVector($sortVector, int $sortArraySize): array
+    private static function validateSortVector(mixed $sortVector, int $sortArraySize): array
     {
         if (!is_array($sortVector)) {
             throw new Exception(ExcelError::VALUE());
@@ -155,10 +148,7 @@ class Sort extends LookupRefValidations
         return $sortVector;
     }
 
-    /**
-     * @param mixed $sortOrder
-     */
-    private static function validateSortOrder($sortOrder): int
+    private static function validateSortOrder(mixed $sortOrder): int
     {
         $sortOrder = self::validateInt($sortOrder);
         if (($sortOrder == self::ORDER_ASCENDING || $sortOrder === self::ORDER_DESCENDING) === false) {
@@ -168,19 +158,15 @@ class Sort extends LookupRefValidations
         return $sortOrder;
     }
 
-    /**
-     * @param array $sortIndex
-     * @param mixed $sortOrder
-     */
-    private static function validateArrayArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
+    private static function validateArrayArgumentsForSort(array &$sortIndex, mixed &$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))
+            count($sortOrder) === 0 || count($sortOrder) > $sortArraySize
+            || (count($sortOrder) > count($sortIndex))
         ) {
             throw new Exception(ExcelError::VALUE());
         }
@@ -219,7 +205,7 @@ class Sort extends LookupRefValidations
      * @param array[] $sortIndex
      * @param int[] $sortOrder
      */
-    private static function processSortBy(array $sortArray, array $sortIndex, $sortOrder): array
+    private static function processSortBy(array $sortArray, array $sortIndex, array $sortOrder): array
     {
         $sortArguments = [];
         $sortData = [];
@@ -228,7 +214,6 @@ class Sort extends LookupRefValidations
             $sortArguments[] = self::prepareSortVectorValues($sortValues);
             $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
         }
-        $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
 
         $sortVector = self::executeVectorSortQuery($sortData, $sortArguments);
 
@@ -272,7 +257,6 @@ class Sort extends LookupRefValidations
             $sortArguments[] = self::prepareSortVectorValues($sortValues);
             $sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
         }
-        $sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
 
         $sortData = self::executeVectorSortQuery($sortData, $sortArguments);
 
@@ -322,21 +306,4 @@ class Sort extends LookupRefValidations
 //
 //        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;
-    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
index 2ba5128..220be2d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php
@@ -18,7 +18,7 @@ class Unique
      *
      * @return mixed The unique values from the search range
      */
-    public static function unique($lookupVector, $byColumn = false, $exactlyOnce = false)
+    public static function unique(mixed $lookupVector, mixed $byColumn = false, mixed $exactlyOnce = false): mixed
     {
         if (!is_array($lookupVector)) {
             // Scalars are always returned "as is"
@@ -33,10 +33,7 @@ class Unique
             : self::uniqueByRow($lookupVector, $exactlyOnce);
     }
 
-    /**
-     * @return mixed
-     */
-    private static function uniqueByRow(array $lookupVector, bool $exactlyOnce)
+    private static function uniqueByRow(array $lookupVector, bool $exactlyOnce): mixed
     {
         // When not $byColumn, we count whole rows or values, not individual values
         //      so implode each row into a single string value
@@ -70,10 +67,7 @@ class Unique
         return (count($result) === 1) ? array_pop($result) : $result;
     }
 
-    /**
-     * @return mixed
-     */
-    private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce)
+    private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce): mixed
     {
         $flattenedLookupVector = Functions::flattenArray($lookupVector);
 
@@ -104,9 +98,7 @@ class Unique
     {
         $caseInsensitiveCounts = array_count_values(
             array_map(
-                function (string $value) {
-                    return StringHelper::strToUpper($value);
-                },
+                fn (string $value): string => StringHelper::strToUpper($value),
                 $caseSensitiveLookupValues
             )
         );
@@ -133,9 +125,7 @@ class Unique
     {
         return array_filter(
             $values,
-            function ($value) {
-                return $value === 1;
-            }
+            fn ($value): bool => $value === 1
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
index edeb1aa..247074c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
@@ -24,9 +24,9 @@ class VLookup extends LookupBase
      *
      * @return mixed The value of the found cell
      */
-    public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
+    public static function lookup(mixed $lookupValue, mixed $lookupArray, mixed $indexNumber, mixed $notExactMatch = true): mixed
     {
-        if (is_array($lookupValue)) {
+        if (is_array($lookupValue) || is_array($indexNumber)) {
             return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $lookupValue, $lookupArray, $indexNumber, $notExactMatch);
         }
 
@@ -49,7 +49,7 @@ class VLookup extends LookupBase
         $firstColumn = array_shift($columnKeys) ?? 1;
 
         if (!$notExactMatch) {
-            /** @var callable */
+            /** @var callable $callable */
             $callable = [self::class, 'vlookupSort'];
             uasort($lookupArray, $callable);
         }
@@ -82,7 +82,7 @@ class VLookup extends LookupBase
      * @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
+    private static function vLookupSearch(mixed $lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
     {
         $lookupLower = StringHelper::strToLower((string) $lookupValue);
 
@@ -94,9 +94,9 @@ class VLookup extends LookupBase
 
             // break if we have passed possible keys
             if (
-                $notExactMatch &&
-                (($bothNumeric && ($rowData[$column] > $lookupValue)) ||
-                ($bothNotNumeric && ($cellDataLower > $lookupLower)))
+                $notExactMatch
+                && (($bothNumeric && ($rowData[$column] > $lookupValue))
+                || ($bothNotNumeric && ($cellDataLower > $lookupLower)))
             ) {
                 break;
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
deleted file mode 100644
index 1790b21..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
+++ /dev/null
@@ -1,1446 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-/**
- * @deprecated 1.18.0
- */
-class MathTrig
-{
-    /**
-     * ARABIC.
-     *
-     * Converts a Roman numeral to an Arabic numeral.
-     *
-     * Excel Function:
-     *        ARABIC(text)
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\Arabic class instead
-     * @see MathTrig\Arabic::evaluate()
-     *
-     * @param array|string $roman
-     *
-     * @return array|int|string the arabic numberal contrived from the roman numeral
-     */
-    public static function ARABIC($roman)
-    {
-        return MathTrig\Arabic::evaluate($roman);
-    }
-
-    /**
-     * ATAN2.
-     *
-     * This function calculates the arc tangent of the two variables x and y. It is similar to
-     *        calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
-     *        to determine the quadrant of the result.
-     * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
-     *        point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
-     *        -pi and pi, excluding -pi.
-     *
-     * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
-     *        PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
-     *
-     * Excel Function:
-     *        ATAN2(xCoordinate,yCoordinate)
-     *
-     * @deprecated 1.18.0
-     *      Use the atan2 method in the MathTrig\Trig\Tangent class instead
-     * @see MathTrig\Trig\Tangent::atan2()
-     *
-     * @param array|float $xCoordinate the x-coordinate of the point
-     * @param array|float $yCoordinate the y-coordinate of the point
-     *
-     * @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)
-    {
-        return MathTrig\Trig\Tangent::atan2($xCoordinate, $yCoordinate);
-    }
-
-    /**
-     * BASE.
-     *
-     * Converts a number into a text representation with the given radix (base).
-     *
-     * Excel Function:
-     *        BASE(Number, Radix [Min_length])
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\Base class instead
-     * @see MathTrig\Base::evaluate()
-     *
-     * @param float $number
-     * @param float $radix
-     * @param int $minLength
-     *
-     * @return array|string the text representation with the given radix (base)
-     */
-    public static function BASE($number, $radix, $minLength = null)
-    {
-        return MathTrig\Base::evaluate($number, $radix, $minLength);
-    }
-
-    /**
-     * CEILING.
-     *
-     * Returns number rounded up, away from zero, to the nearest multiple of significance.
-     *        For example, if you want to avoid using pennies in your prices and your product is
-     *        priced at $4.42, use the formula =CEILING(4.42,0.05) to round prices up to the
-     *        nearest nickel.
-     *
-     * Excel Function:
-     *        CEILING(number[,significance])
-     *
-     * @deprecated 1.17.0
-     *      Use the ceiling() method in the MathTrig\Ceiling class instead
-     * @see MathTrig\Ceiling::ceiling()
-     *
-     * @param float $number the number you want to round
-     * @param float $significance the multiple to which you want to round
-     *
-     * @return array|float|string Rounded Number, or a string containing an error
-     */
-    public static function CEILING($number, $significance = null)
-    {
-        return MathTrig\Ceiling::ceiling($number, $significance);
-    }
-
-    /**
-     * COMBIN.
-     *
-     * Returns the number of combinations for a given number of items. Use COMBIN to
-     *        determine the total possible number of groups for a given number of items.
-     *
-     * Excel Function:
-     *        COMBIN(numObjs,numInSet)
-     *
-     * @deprecated 1.18.0
-     *      Use the withoutRepetition() method in the MathTrig\Combinations class instead
-     * @see MathTrig\Combinations::withoutRepetition()
-     *
-     * @param array|int $numObjs Number of different objects
-     * @param array|int $numInSet Number of objects in each combination
-     *
-     * @return array|float|int|string Number of combinations, or a string containing an error
-     */
-    public static function COMBIN($numObjs, $numInSet)
-    {
-        return MathTrig\Combinations::withoutRepetition($numObjs, $numInSet);
-    }
-
-    /**
-     * EVEN.
-     *
-     * Returns number rounded up to the nearest even integer.
-     * You can use this function for processing items that come in twos. For example,
-     *        a packing crate accepts rows of one or two items. The crate is full when
-     *        the number of items, rounded up to the nearest two, matches the crate's
-     *        capacity.
-     *
-     * Excel Function:
-     *        EVEN(number)
-     *
-     * @deprecated 1.18.0
-     *      Use the even() method in the MathTrig\Round class instead
-     * @see MathTrig\Round::even()
-     *
-     * @param array|float $number Number to round
-     *
-     * @return array|float|int|string Rounded Number, or a string containing an error
-     */
-    public static function EVEN($number)
-    {
-        return MathTrig\Round::even($number);
-    }
-
-    /**
-     * Helper function for Even.
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate() method in the MathTrig\Helpers class instead
-     * @see MathTrig\Helpers::getEven()
-     */
-    public static function getEven(float $number): int
-    {
-        return (int) MathTrig\Helpers::getEven($number);
-    }
-
-    /**
-     * FACT.
-     *
-     * Returns the factorial of a number.
-     * The factorial of a number is equal to 1*2*3*...* number.
-     *
-     * Excel Function:
-     *        FACT(factVal)
-     *
-     * @deprecated 1.18.0
-     *      Use the fact() method in the MathTrig\Factorial class instead
-     * @see MathTrig\Factorial::fact()
-     *
-     * @param array|float $factVal Factorial Value
-     *
-     * @return array|float|int|string Factorial, or a string containing an error
-     */
-    public static function FACT($factVal)
-    {
-        return MathTrig\Factorial::fact($factVal);
-    }
-
-    /**
-     * FACTDOUBLE.
-     *
-     * Returns the double factorial of a number.
-     *
-     * Excel Function:
-     *        FACTDOUBLE(factVal)
-     *
-     * @deprecated 1.18.0
-     *      Use the factDouble() method in the MathTrig\Factorial class instead
-     * @see MathTrig\Factorial::factDouble()
-     *
-     * @param array|float $factVal Factorial Value
-     *
-     * @return array|float|int|string Double Factorial, or a string containing an error
-     */
-    public static function FACTDOUBLE($factVal)
-    {
-        return MathTrig\Factorial::factDouble($factVal);
-    }
-
-    /**
-     * FLOOR.
-     *
-     * Rounds number down, toward zero, to the nearest multiple of significance.
-     *
-     * Excel Function:
-     *        FLOOR(number[,significance])
-     *
-     * @deprecated 1.17.0
-     *      Use the floor() method in the MathTrig\Floor class instead
-     * @see MathTrig\Floor::floor()
-     *
-     * @param float $number Number to round
-     * @param float $significance Significance
-     *
-     * @return array|float|string Rounded Number, or a string containing an error
-     */
-    public static function FLOOR($number, $significance = null)
-    {
-        return MathTrig\Floor::floor($number, $significance);
-    }
-
-    /**
-     * FLOOR.MATH.
-     *
-     * Round a number down to the nearest integer or to the nearest multiple of significance.
-     *
-     * Excel Function:
-     *        FLOOR.MATH(number[,significance[,mode]])
-     *
-     * @deprecated 1.17.0
-     *      Use the math() method in the MathTrig\Floor class instead
-     * @see MathTrig\Floor::math()
-     *
-     * @param float $number Number to round
-     * @param float $significance Significance
-     * @param int $mode direction to round negative numbers
-     *
-     * @return array|float|string Rounded Number, or a string containing an error
-     */
-    public static function FLOORMATH($number, $significance = null, $mode = 0)
-    {
-        return MathTrig\Floor::math($number, $significance, $mode);
-    }
-
-    /**
-     * FLOOR.PRECISE.
-     *
-     * Rounds number down, toward zero, to the nearest multiple of significance.
-     *
-     * Excel Function:
-     *        FLOOR.PRECISE(number[,significance])
-     *
-     * @deprecated 1.17.0
-     *      Use the precise() method in the MathTrig\Floor class instead
-     * @see MathTrig\Floor::precise()
-     *
-     * @param float $number Number to round
-     * @param float $significance Significance
-     *
-     * @return array|float|string Rounded Number, or a string containing an error
-     */
-    public static function FLOORPRECISE($number, $significance = 1)
-    {
-        return MathTrig\Floor::precise($number, $significance);
-    }
-
-    /**
-     * INT.
-     *
-     * Casts a floating point value to an integer
-     *
-     * Excel Function:
-     *        INT(number)
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the MathTrig\IntClass class instead
-     * @see MathTrig\IntClass::evaluate()
-     *
-     * @param array|float $number Number to cast to an integer
-     *
-     * @return array|int|string Integer value, or a string containing an error
-     */
-    public static function INT($number)
-    {
-        return MathTrig\IntClass::evaluate($number);
-    }
-
-    /**
-     * GCD.
-     *
-     * Returns the greatest common divisor of a series of numbers.
-     * The greatest common divisor is the largest integer that divides both
-     *        number1 and number2 without a remainder.
-     *
-     * Excel Function:
-     *        GCD(number1[,number2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate() method in the MathTrig\Gcd class instead
-     * @see MathTrig\Gcd::evaluate()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return int|mixed|string Greatest Common Divisor, or a string containing an error
-     */
-    public static function GCD(...$args)
-    {
-        return MathTrig\Gcd::evaluate(...$args);
-    }
-
-    /**
-     * LCM.
-     *
-     * Returns the lowest common multiplier of a series of numbers
-     * The least common multiple is the smallest positive integer that is a multiple
-     * of all integer arguments number1, number2, and so on. Use LCM to add fractions
-     * with different denominators.
-     *
-     * Excel Function:
-     *        LCM(number1[,number2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate() method in the MathTrig\Lcm class instead
-     * @see MathTrig\Lcm::evaluate()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return int|string Lowest Common Multiplier, or a string containing an error
-     */
-    public static function LCM(...$args)
-    {
-        return MathTrig\Lcm::evaluate(...$args);
-    }
-
-    /**
-     * LOG_BASE.
-     *
-     * Returns the logarithm of a number to a specified base. The default base is 10.
-     *
-     * Excel Function:
-     *        LOG(number[,base])
-     *
-     * @deprecated 1.18.0
-     *      Use the withBase() method in the MathTrig\Logarithms class instead
-     * @see MathTrig\Logarithms::withBase()
-     *
-     * @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 array|float|string The result, or a string containing an error
-     */
-    public static function logBase($number, $base = 10)
-    {
-        return MathTrig\Logarithms::withBase($number, $base);
-    }
-
-    /**
-     * MDETERM.
-     *
-     * Returns the matrix determinant of an array.
-     *
-     * Excel Function:
-     *        MDETERM(array)
-     *
-     * @deprecated 1.18.0
-     *      Use the determinant() method in the MathTrig\MatrixFunctions class instead
-     * @see MathTrig\MatrixFunctions::determinant()
-     *
-     * @param array $matrixValues A matrix of values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function MDETERM($matrixValues)
-    {
-        return MathTrig\MatrixFunctions::determinant($matrixValues);
-    }
-
-    /**
-     * MINVERSE.
-     *
-     * Returns the inverse matrix for the matrix stored in an array.
-     *
-     * Excel Function:
-     *        MINVERSE(array)
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the MathTrig\MatrixFunctions class instead
-     * @see MathTrig\MatrixFunctions::inverse()
-     *
-     * @param array $matrixValues A matrix of values
-     *
-     * @return array|string The result, or a string containing an error
-     */
-    public static function MINVERSE($matrixValues)
-    {
-        return MathTrig\MatrixFunctions::inverse($matrixValues);
-    }
-
-    /**
-     * MMULT.
-     *
-     * @deprecated 1.18.0
-     *      Use the multiply() method in the MathTrig\MatrixFunctions class instead
-     * @see MathTrig\MatrixFunctions::multiply()
-     *
-     * @param array $matrixData1 A matrix of values
-     * @param array $matrixData2 A matrix of values
-     *
-     * @return array|string The result, or a string containing an error
-     */
-    public static function MMULT($matrixData1, $matrixData2)
-    {
-        return MathTrig\MatrixFunctions::multiply($matrixData1, $matrixData2);
-    }
-
-    /**
-     * MOD.
-     *
-     * @deprecated 1.18.0
-     *      Use the mod() method in the MathTrig\Operations class instead
-     * @see MathTrig\Operations::mod()
-     *
-     * @param int $a Dividend
-     * @param int $b Divisor
-     *
-     * @return array|float|int|string Remainder, or a string containing an error
-     */
-    public static function MOD($a = 1, $b = 1)
-    {
-        return MathTrig\Operations::mod($a, $b);
-    }
-
-    /**
-     * MROUND.
-     *
-     * Rounds a number to the nearest multiple of a specified value
-     *
-     * @deprecated 1.17.0
-     *      Use the multiple() method in the MathTrig\Mround class instead
-     * @see MathTrig\Round::multiple()
-     *
-     * @param float $number Number to round
-     * @param array|int $multiple Multiple to which you want to round $number
-     *
-     * @return array|float|string Rounded Number, or a string containing an error
-     */
-    public static function MROUND($number, $multiple)
-    {
-        return MathTrig\Round::multiple($number, $multiple);
-    }
-
-    /**
-     * MULTINOMIAL.
-     *
-     * Returns the ratio of the factorial of a sum of values to the product of factorials.
-     *
-     * @deprecated 1.18.0
-     *      Use the multinomial method in the MathTrig\Factorial class instead
-     * @see MathTrig\Factorial::multinomial()
-     *
-     * @param mixed[] $args An array of mixed values for the Data Series
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function MULTINOMIAL(...$args)
-    {
-        return MathTrig\Factorial::multinomial(...$args);
-    }
-
-    /**
-     * ODD.
-     *
-     * Returns number rounded up to the nearest odd integer.
-     *
-     * @deprecated 1.18.0
-     *      Use the odd method in the MathTrig\Round class instead
-     * @see MathTrig\Round::odd()
-     *
-     * @param array|float $number Number to round
-     *
-     * @return array|float|int|string Rounded Number, or a string containing an error
-     */
-    public static function ODD($number)
-    {
-        return MathTrig\Round::odd($number);
-    }
-
-    /**
-     * POWER.
-     *
-     * Computes x raised to the power y.
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\Power class instead
-     * @see MathTrig\Operations::power()
-     *
-     * @param float $x
-     * @param float $y
-     *
-     * @return array|float|int|string The result, or a string containing an error
-     */
-    public static function POWER($x = 0, $y = 2)
-    {
-        return MathTrig\Operations::power($x, $y);
-    }
-
-    /**
-     * PRODUCT.
-     *
-     * PRODUCT returns the product of all the values and cells referenced in the argument list.
-     *
-     * @deprecated 1.18.0
-     *      Use the product method in the MathTrig\Operations class instead
-     * @see MathTrig\Operations::product()
-     *
-     * Excel Function:
-     *        PRODUCT(value1[,value2[, ...]])
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function PRODUCT(...$args)
-    {
-        return MathTrig\Operations::product(...$args);
-    }
-
-    /**
-     * QUOTIENT.
-     *
-     * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
-     *        and denominator is the divisor.
-     *
-     * @deprecated 1.18.0
-     *      Use the quotient method in the MathTrig\Operations class instead
-     * @see MathTrig\Operations::quotient()
-     *
-     * Excel Function:
-     *        QUOTIENT(value1[,value2[, ...]])
-     *
-     * @param mixed $numerator
-     * @param mixed $denominator
-     *
-     * @return array|int|string
-     */
-    public static function QUOTIENT($numerator, $denominator)
-    {
-        return MathTrig\Operations::quotient($numerator, $denominator);
-    }
-
-    /**
-     * RAND/RANDBETWEEN.
-     *
-     * @deprecated 1.18.0
-     *      Use the randBetween or randBetween method in the MathTrig\Random class instead
-     * @see MathTrig\Random::randBetween()
-     *
-     * @param int $min Minimal value
-     * @param int $max Maximal value
-     *
-     * @return array|float|int|string Random number
-     */
-    public static function RAND($min = 0, $max = 0)
-    {
-        return MathTrig\Random::randBetween($min, $max);
-    }
-
-    /**
-     * ROMAN.
-     *
-     * Converts a number to Roman numeral
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the MathTrig\Roman class instead
-     * @see MathTrig\Roman::evaluate()
-     *
-     * @param mixed $aValue Number to convert
-     * @param mixed $style Number indicating one of five possible forms
-     *
-     * @return array|string Roman numeral, or a string containing an error
-     */
-    public static function ROMAN($aValue, $style = 0)
-    {
-        return MathTrig\Roman::evaluate($aValue, $style);
-    }
-
-    /**
-     * ROUNDUP.
-     *
-     * Rounds a number up to a specified number of decimal places
-     *
-     * @deprecated 1.17.0
-     *      Use the up() method in the MathTrig\Round class instead
-     * @see MathTrig\Round::up()
-     *
-     * @param array|float $number Number to round
-     * @param array|int $digits Number of digits to which you want to round $number
-     *
-     * @return array|float|string Rounded Number, or a string containing an error
-     */
-    public static function ROUNDUP($number, $digits)
-    {
-        return MathTrig\Round::up($number, $digits);
-    }
-
-    /**
-     * ROUNDDOWN.
-     *
-     * Rounds a number down to a specified number of decimal places
-     *
-     * @deprecated 1.17.0
-     *      Use the down() method in the MathTrig\Round class instead
-     * @see MathTrig\Round::down()
-     *
-     * @param array|float $number Number to round
-     * @param array|int $digits Number of digits to which you want to round $number
-     *
-     * @return array|float|string Rounded Number, or a string containing an error
-     */
-    public static function ROUNDDOWN($number, $digits)
-    {
-        return MathTrig\Round::down($number, $digits);
-    }
-
-    /**
-     * SERIESSUM.
-     *
-     * Returns the sum of a power series
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\SeriesSum class instead
-     * @see MathTrig\SeriesSum::evaluate()
-     *
-     * @param mixed $x Input value
-     * @param mixed $n Initial power
-     * @param mixed $m Step
-     * @param mixed[] $args An array of coefficients for the Data Series
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function SERIESSUM($x, $n, $m, ...$args)
-    {
-        return MathTrig\SeriesSum::evaluate($x, $n, $m, ...$args);
-    }
-
-    /**
-     * SIGN.
-     *
-     * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
-     *        if the number is 0, and -1 if the number is negative.
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\Sign class instead
-     * @see MathTrig\Sign::evaluate()
-     *
-     * @param array|float $number Number to round
-     *
-     * @return array|int|string sign value, or a string containing an error
-     */
-    public static function SIGN($number)
-    {
-        return MathTrig\Sign::evaluate($number);
-    }
-
-    /**
-     * returnSign = returns 0/-1/+1.
-     *
-     * @deprecated 1.18.0
-     *      Use the returnSign method in the MathTrig\Helpers class instead
-     * @see MathTrig\Helpers::returnSign()
-     */
-    public static function returnSign(float $number): int
-    {
-        return MathTrig\Helpers::returnSign($number);
-    }
-
-    /**
-     * SQRTPI.
-     *
-     * Returns the square root of (number * pi).
-     *
-     * @deprecated 1.18.0
-     *      Use the pi method in the MathTrig\Sqrt class instead
-     * @see MathTrig\Sqrt::sqrt()
-     *
-     * @param array|float $number Number
-     *
-     * @return array|float|string Square Root of Number * Pi, or a string containing an error
-     */
-    public static function SQRTPI($number)
-    {
-        return MathTrig\Sqrt::pi($number);
-    }
-
-    /**
-     * SUBTOTAL.
-     *
-     * Returns a subtotal in a list or database.
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\Subtotal class instead
-     * @see MathTrig\Subtotal::evaluate()
-     *
-     * @param int $functionType
-     *            A number 1 to 11 that specifies which function to
-     *                    use in calculating subtotals within a range
-     *                    list
-     *            Numbers 101 to 111 shadow the functions of 1 to 11
-     *                    but ignore any values in the range that are
-     *                    in hidden rows or columns
-     * @param mixed[] $args A mixed data series of values
-     *
-     * @return float|string
-     */
-    public static function SUBTOTAL($functionType, ...$args)
-    {
-        return MathTrig\Subtotal::evaluate($functionType, ...$args);
-    }
-
-    /**
-     * SUM.
-     *
-     * SUM computes the sum of all the values and cells referenced in the argument list.
-     *
-     * @deprecated 1.18.0
-     *      Use the sumErroringStrings method in the MathTrig\Sum class instead
-     * @see MathTrig\Sum::sumErroringStrings()
-     *
-     * Excel Function:
-     *        SUM(value1[,value2[, ...]])
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function SUM(...$args)
-    {
-        return MathTrig\Sum::sumIgnoringStrings(...$args);
-    }
-
-    /**
-     * SUMIF.
-     *
-     * Totals the values of cells that contain numbers within the list of arguments
-     *
-     * Excel Function:
-     *        SUMIF(range, criteria, [sum_range])
-     *
-     * @deprecated 1.17.0
-     *      Use the SUMIF() method in the Statistical\Conditional class instead
-     * @see Statistical\Conditional::SUMIF()
-     *
-     * @param mixed $range Data values
-     * @param string $criteria the criteria that defines which cells will be summed
-     * @param mixed $sumRange
-     *
-     * @return float|string
-     */
-    public static function SUMIF($range, $criteria, $sumRange = [])
-    {
-        return Statistical\Conditional::SUMIF($range, $criteria, $sumRange);
-    }
-
-    /**
-     * SUMIFS.
-     *
-     *    Totals the values of cells that contain numbers within the list of arguments
-     *
-     *    Excel Function:
-     *        SUMIFS(sum_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
-     *
-     * @deprecated 1.17.0
-     *      Use the SUMIFS() method in the Statistical\Conditional class instead
-     * @see Statistical\Conditional::SUMIFS()
-     *
-     * @param mixed $args Data values
-     *
-     * @return null|float|string
-     */
-    public static function SUMIFS(...$args)
-    {
-        return Statistical\Conditional::SUMIFS(...$args);
-    }
-
-    /**
-     * SUMPRODUCT.
-     *
-     * Excel Function:
-     *        SUMPRODUCT(value1[,value2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the product method in the MathTrig\Sum class instead
-     * @see MathTrig\Sum::product()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function SUMPRODUCT(...$args)
-    {
-        return MathTrig\Sum::product(...$args);
-    }
-
-    /**
-     * SUMSQ.
-     *
-     * SUMSQ returns the sum of the squares of the arguments
-     *
-     * @deprecated 1.18.0
-     *      Use the sumSquare method in the MathTrig\SumSquares class instead
-     * @see MathTrig\SumSquares::sumSquare()
-     *
-     * Excel Function:
-     *        SUMSQ(value1[,value2[, ...]])
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function SUMSQ(...$args)
-    {
-        return MathTrig\SumSquares::sumSquare(...$args);
-    }
-
-    /**
-     * SUMX2MY2.
-     *
-     * @deprecated 1.18.0
-     *     Use the sumXSquaredMinusYSquared method in the MathTrig\SumSquares class instead
-     * @see MathTrig\SumSquares::sumXSquaredMinusYSquared()
-     *
-     * @param mixed[] $matrixData1 Matrix #1
-     * @param mixed[] $matrixData2 Matrix #2
-     *
-     * @return float|string
-     */
-    public static function SUMX2MY2($matrixData1, $matrixData2)
-    {
-        return MathTrig\SumSquares::sumXSquaredMinusYSquared($matrixData1, $matrixData2);
-    }
-
-    /**
-     * SUMX2PY2.
-     *
-     * @deprecated 1.18.0
-     *     Use the sumXSquaredPlusYSquared method in the MathTrig\SumSquares class instead
-     * @see MathTrig\SumSquares::sumXSquaredPlusYSquared()
-     *
-     * @param mixed[] $matrixData1 Matrix #1
-     * @param mixed[] $matrixData2 Matrix #2
-     *
-     * @return float|string
-     */
-    public static function SUMX2PY2($matrixData1, $matrixData2)
-    {
-        return MathTrig\SumSquares::sumXSquaredPlusYSquared($matrixData1, $matrixData2);
-    }
-
-    /**
-     * SUMXMY2.
-     *
-     * @deprecated 1.18.0
-     *      Use the sumXMinusYSquared method in the MathTrig\SumSquares class instead
-     * @see MathTrig\SumSquares::sumXMinusYSquared()
-     *
-     * @param mixed[] $matrixData1 Matrix #1
-     * @param mixed[] $matrixData2 Matrix #2
-     *
-     * @return float|string
-     */
-    public static function SUMXMY2($matrixData1, $matrixData2)
-    {
-        return MathTrig\SumSquares::sumXMinusYSquared($matrixData1, $matrixData2);
-    }
-
-    /**
-     * TRUNC.
-     *
-     * Truncates value to the number of fractional digits by number_digits.
-     *
-     * @deprecated 1.17.0
-     *      Use the evaluate() method in the MathTrig\Trunc class instead
-     * @see MathTrig\Trunc::evaluate()
-     *
-     * @param float $value
-     * @param int $digits
-     *
-     * @return array|float|string Truncated value, or a string containing an error
-     */
-    public static function TRUNC($value = 0, $digits = 0)
-    {
-        return MathTrig\Trunc::evaluate($value, $digits);
-    }
-
-    /**
-     * SEC.
-     *
-     * Returns the secant of an angle.
-     *
-     * @deprecated 1.18.0
-     *      Use the sec method in the MathTrig\Trig\Secant class instead
-     * @see MathTrig\Trig\Secant::sec()
-     *
-     * @param array|float $angle Number
-     *
-     * @return array|float|string The secant of the angle
-     */
-    public static function SEC($angle)
-    {
-        return MathTrig\Trig\Secant::sec($angle);
-    }
-
-    /**
-     * SECH.
-     *
-     * Returns the hyperbolic secant of an angle.
-     *
-     * @deprecated 1.18.0
-     *      Use the sech method in the MathTrig\Trig\Secant class instead
-     * @see MathTrig\Trig\Secant::sech()
-     *
-     * @param array|float $angle Number
-     *
-     * @return array|float|string The hyperbolic secant of the angle
-     */
-    public static function SECH($angle)
-    {
-        return MathTrig\Trig\Secant::sech($angle);
-    }
-
-    /**
-     * CSC.
-     *
-     * Returns the cosecant of an angle.
-     *
-     * @deprecated 1.18.0
-     *      Use the csc method in the MathTrig\Trig\Cosecant class instead
-     * @see MathTrig\Trig\Cosecant::csc()
-     *
-     * @param array|float $angle Number
-     *
-     * @return array|float|string The cosecant of the angle
-     */
-    public static function CSC($angle)
-    {
-        return MathTrig\Trig\Cosecant::csc($angle);
-    }
-
-    /**
-     * CSCH.
-     *
-     * Returns the hyperbolic cosecant of an angle.
-     *
-     * @deprecated 1.18.0
-     *      Use the csch method in the MathTrig\Trig\Cosecant class instead
-     * @see MathTrig\Trig\Cosecant::csch()
-     *
-     * @param array|float $angle Number
-     *
-     * @return array|float|string The hyperbolic cosecant of the angle
-     */
-    public static function CSCH($angle)
-    {
-        return MathTrig\Trig\Cosecant::csch($angle);
-    }
-
-    /**
-     * COT.
-     *
-     * Returns the cotangent of an angle.
-     *
-     * @deprecated 1.18.0
-     *      Use the cot method in the MathTrig\Trig\Cotangent class instead
-     * @see MathTrig\Trig\Cotangent::cot()
-     *
-     * @param array|float $angle Number
-     *
-     * @return array|float|string The cotangent of the angle
-     */
-    public static function COT($angle)
-    {
-        return MathTrig\Trig\Cotangent::cot($angle);
-    }
-
-    /**
-     * COTH.
-     *
-     * Returns the hyperbolic cotangent of an angle.
-     *
-     * @deprecated 1.18.0
-     *      Use the coth method in the MathTrig\Trig\Cotangent class instead
-     * @see MathTrig\Trig\Cotangent::coth()
-     *
-     * @param array|float $angle Number
-     *
-     * @return array|float|string The hyperbolic cotangent of the angle
-     */
-    public static function COTH($angle)
-    {
-        return MathTrig\Trig\Cotangent::coth($angle);
-    }
-
-    /**
-     * ACOT.
-     *
-     * Returns the arccotangent of a number.
-     *
-     * @deprecated 1.18.0
-     *      Use the acot method in the MathTrig\Trig\Cotangent class instead
-     * @see MathTrig\Trig\Cotangent::acot()
-     *
-     * @param array|float $number Number
-     *
-     * @return array|float|string The arccotangent of the number
-     */
-    public static function ACOT($number)
-    {
-        return MathTrig\Trig\Cotangent::acot($number);
-    }
-
-    /**
-     * Return NAN or value depending on argument.
-     *
-     * @deprecated 1.18.0
-     *      Use the numberOrNan method in the MathTrig\Helpers class instead
-     * @see MathTrig\Helpers::numberOrNan()
-     *
-     * @param float $result Number
-     *
-     * @return float|string
-     */
-    public static function numberOrNan($result)
-    {
-        return MathTrig\Helpers::numberOrNan($result);
-    }
-
-    /**
-     * ACOTH.
-     *
-     * Returns the hyperbolic arccotangent of a number.
-     *
-     * @deprecated 1.18.0
-     *      Use the acoth method in the MathTrig\Trig\Cotangent class instead
-     * @see MathTrig\Trig\Cotangent::acoth()
-     *
-     * @param array|float $number Number
-     *
-     * @return array|float|string The hyperbolic arccotangent of the number
-     */
-    public static function ACOTH($number)
-    {
-        return MathTrig\Trig\Cotangent::acoth($number);
-    }
-
-    /**
-     * ROUND.
-     *
-     * Returns the result of builtin function round after validating args.
-     *
-     * @deprecated 1.17.0
-     *      Use the round() method in the MathTrig\Round class instead
-     * @see MathTrig\Round::round()
-     *
-     * @param array|mixed $number Should be numeric
-     * @param array|mixed $precision Should be int
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinROUND($number, $precision)
-    {
-        return MathTrig\Round::round($number, $precision);
-    }
-
-    /**
-     * ABS.
-     *
-     * Returns the result of builtin function abs after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\Absolute class instead
-     * @see MathTrig\Absolute::evaluate()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|int|string Rounded number
-     */
-    public static function builtinABS($number)
-    {
-        return MathTrig\Absolute::evaluate($number);
-    }
-
-    /**
-     * ACOS.
-     *
-     * @deprecated 1.18.0
-     *      Use the acos method in the MathTrig\Trig\Cosine class instead
-     * @see MathTrig\Trig\Cosine::acos()
-     *
-     * Returns the result of builtin function acos after validating args.
-     *
-     * @param array|float $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinACOS($number)
-    {
-        return MathTrig\Trig\Cosine::acos($number);
-    }
-
-    /**
-     * ACOSH.
-     *
-     * Returns the result of builtin function acosh after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the acosh method in the MathTrig\Trig\Cosine class instead
-     * @see MathTrig\Trig\Cosine::acosh()
-     *
-     * @param array|float $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinACOSH($number)
-    {
-        return MathTrig\Trig\Cosine::acosh($number);
-    }
-
-    /**
-     * ASIN.
-     *
-     * Returns the result of builtin function asin after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the asin method in the MathTrig\Trig\Sine class instead
-     * @see MathTrig\Trig\Sine::asin()
-     *
-     * @param array|float $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinASIN($number)
-    {
-        return MathTrig\Trig\Sine::asin($number);
-    }
-
-    /**
-     * ASINH.
-     *
-     * Returns the result of builtin function asinh after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the asinh method in the MathTrig\Trig\Sine class instead
-     * @see MathTrig\Trig\Sine::asinh()
-     *
-     * @param array|float $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinASINH($number)
-    {
-        return MathTrig\Trig\Sine::asinh($number);
-    }
-
-    /**
-     * ATAN.
-     *
-     * Returns the result of builtin function atan after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the atan method in the MathTrig\Trig\Tangent class instead
-     * @see MathTrig\Trig\Tangent::atan()
-     *
-     * @param array|float $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinATAN($number)
-    {
-        return MathTrig\Trig\Tangent::atan($number);
-    }
-
-    /**
-     * ATANH.
-     *
-     * Returns the result of builtin function atanh after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the atanh method in the MathTrig\Trig\Tangent class instead
-     * @see MathTrig\Trig\Tangent::atanh()
-     *
-     * @param array|float $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinATANH($number)
-    {
-        return MathTrig\Trig\Tangent::atanh($number);
-    }
-
-    /**
-     * COS.
-     *
-     * Returns the result of builtin function cos after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the cos method in the MathTrig\Trig\Cosine class instead
-     * @see MathTrig\Trig\Cosine::cos()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinCOS($number)
-    {
-        return MathTrig\Trig\Cosine::cos($number);
-    }
-
-    /**
-     * COSH.
-     *
-     * Returns the result of builtin function cos after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the cosh method in the MathTrig\Trig\Cosine class instead
-     * @see MathTrig\Trig\Cosine::cosh()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinCOSH($number)
-    {
-        return MathTrig\Trig\Cosine::cosh($number);
-    }
-
-    /**
-     * DEGREES.
-     *
-     * Returns the result of builtin function rad2deg after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the toDegrees method in the MathTrig\Angle class instead
-     * @see MathTrig\Angle::toDegrees()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinDEGREES($number)
-    {
-        return MathTrig\Angle::toDegrees($number);
-    }
-
-    /**
-     * EXP.
-     *
-     * Returns the result of builtin function exp after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the evaluate method in the MathTrig\Exp class instead
-     * @see MathTrig\Exp::evaluate()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinEXP($number)
-    {
-        return MathTrig\Exp::evaluate($number);
-    }
-
-    /**
-     * LN.
-     *
-     * Returns the result of builtin function log after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the natural method in the MathTrig\Logarithms class instead
-     * @see MathTrig\Logarithms::natural()
-     *
-     * @param mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinLN($number)
-    {
-        return MathTrig\Logarithms::natural($number);
-    }
-
-    /**
-     * LOG10.
-     *
-     * Returns the result of builtin function log after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the natural method in the MathTrig\Logarithms class instead
-     * @see MathTrig\Logarithms::base10()
-     *
-     * @param mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinLOG10($number)
-    {
-        return MathTrig\Logarithms::base10($number);
-    }
-
-    /**
-     * RADIANS.
-     *
-     * Returns the result of builtin function deg2rad after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the toRadians method in the MathTrig\Angle class instead
-     * @see MathTrig\Angle::toRadians()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinRADIANS($number)
-    {
-        return MathTrig\Angle::toRadians($number);
-    }
-
-    /**
-     * SIN.
-     *
-     * Returns the result of builtin function sin after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the sin method in the MathTrig\Trig\Sine class instead
-     * @see MathTrig\Trig\Sine::evaluate()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string sine
-     */
-    public static function builtinSIN($number)
-    {
-        return MathTrig\Trig\Sine::sin($number);
-    }
-
-    /**
-     * SINH.
-     *
-     * Returns the result of builtin function sinh after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the sinh method in the MathTrig\Trig\Sine class instead
-     * @see MathTrig\Trig\Sine::sinh()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinSINH($number)
-    {
-        return MathTrig\Trig\Sine::sinh($number);
-    }
-
-    /**
-     * SQRT.
-     *
-     * Returns the result of builtin function sqrt after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the sqrt method in the MathTrig\Sqrt class instead
-     * @see MathTrig\Sqrt::sqrt()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinSQRT($number)
-    {
-        return MathTrig\Sqrt::sqrt($number);
-    }
-
-    /**
-     * TAN.
-     *
-     * Returns the result of builtin function tan after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the tan method in the MathTrig\Trig\Tangent class instead
-     * @see MathTrig\Trig\Tangent::tan()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinTAN($number)
-    {
-        return MathTrig\Trig\Tangent::tan($number);
-    }
-
-    /**
-     * TANH.
-     *
-     * Returns the result of builtin function sinh after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the tanh method in the MathTrig\Trig\Tangent class instead
-     * @see MathTrig\Trig\Tangent::tanh()
-     *
-     * @param array|mixed $number Should be numeric
-     *
-     * @return array|float|string Rounded number
-     */
-    public static function builtinTANH($number)
-    {
-        return MathTrig\Trig\Tangent::tanh($number);
-    }
-
-    /**
-     * Many functions accept null/false/true argument treated as 0/0/1.
-     *
-     * @deprecated 1.18.0
-     *      Use the validateNumericNullBool method in the MathTrig\Helpers class instead
-     * @see MathTrig\Helpers::validateNumericNullBool()
-     *
-     * @param mixed $number
-     */
-    public static function nullFalseTrueToNumber(&$number): void
-    {
-        $number = Functions::flattenSingleValue($number);
-        if ($number === null) {
-            $number = 0;
-        } elseif (is_bool($number)) {
-            $number = (int) $number;
-        }
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
index f21c6b7..03e6139 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php
@@ -20,7 +20,7 @@ class Absolute
      *         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 evaluate($number)
+    public static function evaluate(mixed $number): array|string|int|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
index cbeec6f..e7de7aa 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php
@@ -20,7 +20,7 @@ class Angle
      *         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 toDegrees($number)
+    public static function toDegrees(mixed $number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
@@ -46,7 +46,7 @@ class Angle
      *         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 toRadians($number)
+    public static function toRadians(mixed $number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
index ee48850..98c3e3d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php
@@ -22,13 +22,8 @@ class Arabic
 
     /**
      * Recursively calculate the arabic value of a roman numeral.
-     *
-     * @param int $sum
-     * @param int $subtract
-     *
-     * @return int
      */
-    private static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
+    private static function calculateArabic(array $roman, int &$sum = 0, int $subtract = 0): int
     {
         $numeral = array_shift($roman);
         if (!isset(self::ROMAN_LOOKUP[$numeral])) {
@@ -50,21 +45,6 @@ class Arabic
         return $sum;
     }
 
-    /**
-     * @param mixed $value
-     */
-    private static function mollifyScrutinizer($value): array
-    {
-        return is_array($value) ? $value : [];
-    }
-
-    private static function strSplit(string $roman): array
-    {
-        $rslt = str_split($roman);
-
-        return self::mollifyScrutinizer($rslt);
-    }
-
     /**
      * ARABIC.
      *
@@ -73,13 +53,13 @@ class Arabic
      * Excel Function:
      *        ARABIC(text)
      *
-     * @param mixed $roman Should be a string, or can be an array of strings
+     * @param string|string[] $roman Should be a string, or can be an array of strings
      *
      * @return array|int|string the arabic numberal contrived from the roman numeral
      *         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 evaluate($roman)
+    public static function evaluate(mixed $roman): array|int|string
     {
         if (is_array($roman)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $roman);
@@ -98,8 +78,8 @@ class Arabic
         }
 
         try {
-            $arabic = self::calculateArabic(self::strSplit($roman));
-        } catch (Exception $e) {
+            $arabic = self::calculateArabic(str_split($roman));
+        } catch (Exception) {
             return ExcelError::VALUE(); // Invalid character detected
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
index 2fec947..7eda72c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php
@@ -29,7 +29,7 @@ class Base
      *         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 evaluate($number, $radix, $minLength = null)
+    public static function evaluate(mixed $number, mixed $radix, mixed $minLength = null): array|string
     {
         if (is_array($number) || is_array($radix) || is_array($minLength)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $radix, $minLength);
@@ -45,10 +45,7 @@ class Base
         return self::calculate($number, $radix, $minLength);
     }
 
-    /**
-     * @param mixed $minLength
-     */
-    private static function calculate(float $number, int $radix, $minLength): string
+    private static function calculate(float $number, int $radix, mixed $minLength): string
     {
         if ($minLength === null || is_numeric($minLength)) {
             if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
index 635f1bb..365ec2e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php
@@ -70,7 +70,7 @@ class Ceiling
      *         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 math($number, $significance = null, $mode = 0)
+    public static function math(mixed $number, mixed $significance = null, $mode = 0): array|string|float
     {
         if (is_array($number) || is_array($significance) || is_array($mode)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
@@ -111,7 +111,7 @@ class Ceiling
      *         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 precise($number, $significance = 1)
+    public static function precise(mixed $number, $significance = 1): array|string|float
     {
         if (is_array($number) || is_array($significance)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
@@ -137,15 +137,13 @@ class Ceiling
      */
     private static function ceilingMathTest(float $significance, float $number, int $mode): bool
     {
-        return ((float) $significance < 0) || ((float) $number < 0 && !empty($mode));
+        return ($significance < 0) || ($number < 0 && !empty($mode));
     }
 
     /**
      * Avoid Scrutinizer problems concerning complexity.
-     *
-     * @return float|string
      */
-    private static function argumentsOk(float $number, float $significance)
+    private static function argumentsOk(float $number, float $significance): float|string
     {
         if (empty($number * $significance)) {
             return 0.0;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
index 5a652da..99eb05a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php
@@ -21,11 +21,11 @@ class Combinations
      * @param mixed $numObjs Number of different objects, or can be an array of numbers
      * @param mixed $numInSet Number of objects in each combination, or can be an array of numbers
      *
-     * @return array|float|int|string Number of combinations, or a string containing an error
+     * @return array|float|string Number of combinations, or a string containing an error
      *         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 withoutRepetition($numObjs, $numInSet)
+    public static function withoutRepetition(mixed $numObjs, mixed $numInSet): array|string|float
     {
         if (is_array($numObjs) || is_array($numInSet)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
@@ -40,7 +40,14 @@ class Combinations
             return $e->getMessage();
         }
 
-        return round(Factorial::fact($numObjs) / Factorial::fact($numObjs - $numInSet)) / Factorial::fact($numInSet);
+        /** @var float */
+        $quotient = Factorial::fact($numObjs);
+        /** @var float */
+        $divisor1 = Factorial::fact($numObjs - $numInSet);
+        /** @var float */
+        $divisor2 = Factorial::fact($numInSet);
+
+        return round($quotient / ($divisor1 * $divisor2));
     }
 
     /**
@@ -59,7 +66,7 @@ class Combinations
      *         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 withRepetition($numObjs, $numInSet)
+    public static function withRepetition(mixed $numObjs, mixed $numInSet): array|int|string|float
     {
         if (is_array($numObjs) || is_array($numInSet)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
@@ -84,8 +91,13 @@ class Combinations
             return $e->getMessage();
         }
 
-        return round(
-            Factorial::fact($numObjs + $numInSet - 1) / Factorial::fact($numObjs - 1)
-        ) / Factorial::fact($numInSet);
+        /** @var float */
+        $quotient = Factorial::fact($numObjs + $numInSet - 1);
+        /** @var float */
+        $divisor1 = Factorial::fact($numObjs - 1);
+        /** @var float */
+        $divisor2 = Factorial::fact($numInSet);
+
+        return round($quotient / ($divisor1 * $divisor2));
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
index f65c2c1..ea67d5f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php
@@ -20,7 +20,7 @@ class Exp
      *         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 evaluate($number)
+    public static function evaluate(mixed $number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
index b6883e2..7bbd4d8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php
@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
 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\Statistical;
 
 class Factorial
@@ -26,7 +27,7 @@ class Factorial
      *         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 fact($factVal)
+    public static function fact($factVal): array|string|float|int
     {
         if (is_array($factVal)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $factVal);
@@ -68,7 +69,7 @@ class Factorial
      *         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 factDouble($factVal)
+    public static function factDouble($factVal): array|string|float|int
     {
         if (is_array($factVal)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $factVal);
@@ -98,9 +99,9 @@ class Factorial
      *
      * @param mixed[] $args An array of mixed values for the Data Series
      *
-     * @return float|string The result, or a string containing an error
+     * @return float|int|string The result, or a string containing an error
      */
-    public static function multinomial(...$args)
+    public static function multinomial(...$args): string|int|float
     {
         $summer = 0;
         $divisor = 1;
@@ -120,6 +121,6 @@ class Factorial
 
         $summer = self::fact($summer);
 
-        return $summer / $divisor;
+        return is_numeric($summer) ? ($summer / $divisor) : ExcelError::VALUE();
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
index 2199dda..83cf051 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php
@@ -36,7 +36,7 @@ class Floor
      *         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 floor($number, $significance = null)
+    public static function floor(mixed $number, mixed $significance = null)
     {
         if (is_array($number) || is_array($significance)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
@@ -75,7 +75,7 @@ class Floor
      *         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 math($number, $significance = null, $mode = 0)
+    public static function math(mixed $number, mixed $significance = null, mixed $mode = 0)
     {
         if (is_array($number) || is_array($significance) || is_array($mode)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
@@ -127,10 +127,8 @@ class Floor
 
     /**
      * Avoid Scrutinizer problems concerning complexity.
-     *
-     * @return float|string
      */
-    private static function argumentsOkPrecise(float $number, float $significance)
+    private static function argumentsOkPrecise(float $number, float $significance): string|float
     {
         if ($significance == 0.0) {
             return ExcelError::DIV0();
@@ -147,7 +145,7 @@ class Floor
      *
      * @return float|string Rounded Number, or a string containing an error
      */
-    private static function argsOk(float $number, float $significance, int $mode)
+    private static function argsOk(float $number, float $significance, int $mode): string|float
     {
         if (!$significance) {
             return ExcelError::DIV0();
@@ -172,10 +170,8 @@ class Floor
 
     /**
      * Avoid Scrutinizer problems concerning complexity.
-     *
-     * @return float|string
      */
-    private static function argumentsOk(float $number, float $significance)
+    private static function argumentsOk(float $number, float $significance): string|float
     {
         if ($significance == 0.0) {
             return ExcelError::DIV0();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
index f703599..f2aedb6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php
@@ -17,13 +17,8 @@ class Gcd
      *
      * Excel Function:
      *        GCD(number1[,number2[, ...]])
-     *
-     * @param float|int $a
-     * @param float|int $b
-     *
-     * @return float|int
      */
-    private static function evaluateGCD($a, $b)
+    private static function evaluateGCD(float|int $a, float|int $b): float|int
     {
         return $b ? self::evaluateGCD($b, $a % $b) : $a;
     }
@@ -42,7 +37,7 @@ class Gcd
      *
      * @return float|int|string Greatest Common Divisor, or a string containing an error
      */
-    public static function evaluate(...$args)
+    public static function evaluate(mixed ...$args)
     {
         try {
             $arrayArgs = [];
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
index f34f159..57e05b1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php
@@ -13,19 +13,15 @@ class Helpers
      *
      * @return float|string quotient or DIV0 if denominator is too small
      */
-    public static function verySmallDenominator(float $numerator, float $denominator)
+    public static function verySmallDenominator(float $numerator, float $denominator): string|float
     {
         return (abs($denominator) < 1.0E-12) ? ExcelError::DIV0() : ($numerator / $denominator);
     }
 
     /**
      * Many functions accept null/false/true argument treated as 0/0/1.
-     *
-     * @param mixed $number
-     *
-     * @return float|int
      */
-    public static function validateNumericNullBool($number)
+    public static function validateNumericNullBool(mixed $number): int|float
     {
         $number = Functions::flattenSingleValue($number);
         if ($number === null) {
@@ -43,13 +39,8 @@ class Helpers
 
     /**
      * Validate numeric, but allow substitute for null.
-     *
-     * @param mixed $number
-     * @param null|float|int $substitute
-     *
-     * @return float|int
      */
-    public static function validateNumericNullSubstitution($number, $substitute)
+    public static function validateNumericNullSubstitution(mixed $number, null|float|int $substitute): float|int
     {
         $number = Functions::flattenSingleValue($number);
         if ($number === null && $substitute !== null) {
@@ -64,10 +55,8 @@ class Helpers
 
     /**
      * Confirm number >= 0.
-     *
-     * @param float|int $number
      */
-    public static function validateNotNegative($number, ?string $except = null): void
+    public static function validateNotNegative(float|int $number, ?string $except = null): void
     {
         if ($number >= 0) {
             return;
@@ -78,10 +67,8 @@ class Helpers
 
     /**
      * Confirm number > 0.
-     *
-     * @param float|int $number
      */
-    public static function validatePositive($number, ?string $except = null): void
+    public static function validatePositive(float|int $number, ?string $except = null): void
     {
         if ($number > 0) {
             return;
@@ -92,10 +79,8 @@ class Helpers
 
     /**
      * Confirm number != 0.
-     *
-     * @param float|int $number
      */
-    public static function validateNotZero($number): void
+    public static function validateNotZero(float|int $number): void
     {
         if ($number) {
             return;
@@ -118,12 +103,8 @@ class Helpers
 
     /**
      * Return NAN or value depending on argument.
-     *
-     * @param float $result Number
-     *
-     * @return float|string
      */
-    public static function numberOrNan($result)
+    public static function numberOrNan(float $result): float|string
     {
         return is_nan($result) ? ExcelError::NAN() : $result;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
index f7f7764..76bbced 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php
@@ -19,11 +19,11 @@ class IntClass
      *
      * @param array|float $number Number to cast to an integer, or can be an array of numbers
      *
-     * @return array|string Integer value, or a string containing an error
+     * @return array|int|string Integer value, or a string containing an error
      *         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 evaluate($number)
+    public static function evaluate($number): array|string|int
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
index 3b23c1d..979b6df 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php
@@ -49,7 +49,7 @@ class Lcm
      *
      * @return int|string Lowest Common Multiplier, or a string containing an error
      */
-    public static function evaluate(...$args)
+    public static function evaluate(mixed ...$args): int|string
     {
         try {
             $arrayArgs = [];
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
index 7b07f09..3de0a2b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php
@@ -26,7 +26,7 @@ class Logarithms
      *         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 withBase($number, $base = 10)
+    public static function withBase(mixed $number, mixed $base = 10): array|string|float
     {
         if (is_array($number) || is_array($base)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $base);
@@ -56,7 +56,7 @@ class Logarithms
      *         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 base10($number)
+    public static function base10(mixed $number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
@@ -84,7 +84,7 @@ class Logarithms
      *         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 natural($number)
+    public static function natural(mixed $number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
index 5a5125a..fad0108 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php
@@ -16,7 +16,7 @@ class MatrixFunctions
      *
      * @param mixed $matrixValues A matrix of values
      */
-    private static function getMatrix($matrixValues): Matrix
+    private static function getMatrix(mixed $matrixValues): Matrix
     {
         $matrixData = [];
         if (!is_array($matrixValues)) {
@@ -57,7 +57,7 @@ class MatrixFunctions
      *
      * @return array|string The resulting array, or a string containing an error
      */
-    public static function sequence($rows = 1, $columns = 1, $start = 1, $step = 1)
+    public static function sequence(mixed $rows = 1, mixed $columns = 1, mixed $start = 1, mixed $step = 1): string|array
     {
         try {
             $rows = (int) Helpers::validateNumericNullSubstitution($rows, 1);
@@ -95,13 +95,13 @@ class MatrixFunctions
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function determinant($matrixValues)
+    public static function determinant(mixed $matrixValues)
     {
         try {
             $matrix = self::getMatrix($matrixValues);
 
             return $matrix->determinant();
-        } catch (MatrixException $ex) {
+        } catch (MatrixException) {
             return ExcelError::VALUE();
         } catch (Exception $e) {
             return $e->getMessage();
@@ -120,15 +120,15 @@ class MatrixFunctions
      *
      * @return array|string The result, or a string containing an error
      */
-    public static function inverse($matrixValues)
+    public static function inverse(mixed $matrixValues): array|string
     {
         try {
             $matrix = self::getMatrix($matrixValues);
 
             return $matrix->inverse()->toArray();
-        } catch (MatrixDiv0Exception $e) {
+        } catch (MatrixDiv0Exception) {
             return ExcelError::NAN();
-        } catch (MatrixException $e) {
+        } catch (MatrixException) {
             return ExcelError::VALUE();
         } catch (Exception $e) {
             return $e->getMessage();
@@ -143,14 +143,14 @@ class MatrixFunctions
      *
      * @return array|string The result, or a string containing an error
      */
-    public static function multiply($matrixData1, $matrixData2)
+    public static function multiply(mixed $matrixData1, mixed $matrixData2): array|string
     {
         try {
             $matrixA = self::getMatrix($matrixData1);
             $matrixB = self::getMatrix($matrixData2);
 
             return $matrixA->multiply($matrixB)->toArray();
-        } catch (MatrixException $ex) {
+        } catch (MatrixException) {
             return ExcelError::VALUE();
         } catch (Exception $e) {
             return $e->getMessage();
@@ -164,7 +164,7 @@ class MatrixFunctions
      *
      * @return array|string The result, or a string containing an error
      */
-    public static function identity($dimension)
+    public static function identity(mixed $dimension)
     {
         try {
             $dimension = (int) Helpers::validateNumericNullBool($dimension);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
index 0625845..0eca549 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php
@@ -19,11 +19,11 @@ class Operations
      * @param mixed $divisor Divisor
      *                      Or can be an array of values
      *
-     * @return array|float|int|string Remainder, or a string containing an error
+     * @return array|float|string Remainder, 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 mod($dividend, $divisor)
+    public static function mod(mixed $dividend, mixed $divisor): array|string|float
     {
         if (is_array($dividend) || is_array($divisor)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $dividend, $divisor);
@@ -52,16 +52,14 @@ class Operations
      *
      * Computes x raised to the power y.
      *
-     * @param array|float|int $x
-     *                      Or can be an array of values
-     * @param array|float|int $y
-     *                      Or can be an array of values
+     * @param null|array|bool|float|int|string $x Or can be an array of values
+     * @param null|array|bool|float|int|string $y Or can be an array of values
      *
      * @return array|float|int|string The 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 power($x, $y)
+    public static function power(null|array|bool|float|int|string $x, null|array|bool|float|int|string $y): array|float|int|string
     {
         if (is_array($x) || is_array($y)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $y);
@@ -97,16 +95,12 @@ class Operations
      *        PRODUCT(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function product(...$args)
+    public static function product(mixed ...$args): string|float
     {
         $args = array_filter(
             Functions::flattenArray($args),
-            function ($value) {
-                return $value !== null;
-            }
+            fn ($value): bool => $value !== null
         );
 
         // Return value
@@ -139,11 +133,10 @@ class Operations
      * @param mixed $denominator Expect float|int
      *                      Or can be an array of values
      *
-     * @return array|int|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 quotient($numerator, $denominator)
+    public static function quotient(mixed $numerator, mixed $denominator): array|string|int
     {
         if (is_array($numerator) || is_array($denominator)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $numerator, $denominator);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
index 22cad2c..5d35167 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
@@ -13,9 +13,9 @@ class Random
     /**
      * RAND.
      *
-     * @return float Random number
+     * @return float|int Random number
      */
-    public static function rand()
+    public static function rand(): int|float
     {
         return mt_rand(0, 10000000) / 10000000;
     }
@@ -28,11 +28,11 @@ class Random
      * @param mixed $max Maximal value
      *                      Or can be an array of values
      *
-     * @return array|float|int|string Random number
+     * @return array|int|string Random number
      *         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 randBetween($min, $max)
+    public static function randBetween(mixed $min, mixed $max): array|string|int
     {
         if (is_array($min) || is_array($max)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $min, $max);
@@ -67,7 +67,7 @@ class Random
      *
      * @return array|string The resulting array, or a string containing an error
      */
-    public static function randArray($rows = 1, $columns = 1, $min = 0, $max = 1, $wholeNumber = false)
+    public static function randArray(mixed $rows = 1, mixed $columns = 1, mixed $min = 0, mixed $max = 1, bool $wholeNumber = false): string|array
     {
         try {
             $rows = (int) Helpers::validateNumericNullSubstitution($rows, 1);
@@ -86,7 +86,7 @@ class Random
 
         return array_chunk(
             array_map(
-                function () use ($min, $max, $wholeNumber) {
+                function () use ($min, $max, $wholeNumber): int|float {
                     return $wholeNumber
                         ? mt_rand((int) $min, (int) $max)
                         : (mt_rand() / mt_getrandmax()) * ($max - $min) + $min;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
index 0541548..7c6f8e7 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
@@ -825,7 +825,7 @@ class Roman
      *         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 evaluate($aValue, $style = 0)
+    public static function evaluate(mixed $aValue, mixed $style = 0): array|string
     {
         if (is_array($aValue) || is_array($style)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $aValue, $style);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
index 776f7eb..d2aa1c0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
@@ -22,7 +22,7 @@ class Round
      *         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 round($number, $precision)
+    public static function round(mixed $number, mixed $precision): array|string|float
     {
         if (is_array($number) || is_array($precision)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $precision);
@@ -50,7 +50,7 @@ class Round
      *         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 up($number, $digits)
+    public static function up($number, $digits): array|string|float
     {
         if (is_array($number) || is_array($digits)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
@@ -67,11 +67,22 @@ class Round
             return 0.0;
         }
 
+        $digitsPlus1 = $digits + 1;
         if ($number < 0.0) {
-            return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+            if ($digitsPlus1 < 0) {
+                return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+            }
+            $result = sprintf("%.{$digitsPlus1}F", $number - 0.5 * 0.1 ** $digits);
+
+            return round((float) $result, $digits, PHP_ROUND_HALF_DOWN);
         }
 
-        return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+        if ($digitsPlus1 < 0) {
+            return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
+        }
+        $result = sprintf("%.{$digitsPlus1}F", $number + 0.5 * 0.1 ** $digits);
+
+        return round((float) $result, $digits, PHP_ROUND_HALF_DOWN);
     }
 
     /**
@@ -86,7 +97,7 @@ class Round
      *         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 down($number, $digits)
+    public static function down($number, $digits): array|string|float
     {
         if (is_array($number) || is_array($digits)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
@@ -103,11 +114,23 @@ class Round
             return 0.0;
         }
 
+        $digitsPlus1 = $digits + 1;
         if ($number < 0.0) {
-            return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+            if ($digitsPlus1 < 0) {
+                return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+            }
+            $result = sprintf("%.{$digitsPlus1}F", $number + 0.5 * 0.1 ** $digits);
+
+            return round((float) $result, $digits, PHP_ROUND_HALF_UP);
         }
 
-        return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+        if ($digitsPlus1 < 0) {
+            return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
+        }
+
+        $result = sprintf("%.{$digitsPlus1}F", $number - 0.5 * 0.1 ** $digits);
+
+        return round((float) $result, $digits, PHP_ROUND_HALF_UP);
     }
 
     /**
@@ -118,11 +141,11 @@ class Round
      * @param mixed $number Expect float. Number to round, or can be an array of numbers
      * @param mixed $multiple Expect int. Multiple to which you want to round, or can be an array of numbers.
      *
-     * @return array|float|string Rounded Number, or a string containing an error
+     * @return array|float|int|string Rounded Number, or a string containing an error
      *         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 multiple($number, $multiple)
+    public static function multiple(mixed $number, mixed $multiple): array|string|int|float
     {
         if (is_array($number) || is_array($multiple)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $multiple);
@@ -165,7 +188,7 @@ class Round
      *         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 even($number)
+    public static function even($number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
@@ -187,11 +210,11 @@ class Round
      *
      * @param array|float $number Number to round, or can be an array of numbers
      *
-     * @return array|float|string Rounded Number, or a string containing an error
+     * @return array|float|int|string Rounded Number, or a string containing an error
      *         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 odd($number)
+    public static function odd($number): array|string|int|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
index ecce359..bb10090 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
@@ -20,9 +20,9 @@ class SeriesSum
      * @param mixed $m Step
      * @param mixed[] $args An array of coefficients for the Data Series
      *
-     * @return array|float|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 evaluate($x, $n, $m, ...$args)
+    public static function evaluate(mixed $x, mixed $n, mixed $m, ...$args): array|string|float|int
     {
         if (is_array($x) || is_array($n) || is_array($m)) {
             return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 3, $x, $n, $m, ...$args);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
index e40e1f6..86a5509 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
@@ -21,7 +21,7 @@ class Sign
      *         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 evaluate($number)
+    public static function evaluate($number): array|string|int
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
index bb9f15f..18289f7 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
@@ -20,7 +20,7 @@ class Sqrt
      *         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 sqrt($number)
+    public static function sqrt(mixed $number)
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
@@ -46,7 +46,7 @@ class Sqrt
      *         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 pi($number)
+    public static function pi($number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
index 6d8f472..cfced9e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
@@ -9,11 +9,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
 
 class Subtotal
 {
-    /**
-     * @param mixed $cellReference
-     * @param mixed $args
-     */
-    protected static function filterHiddenArgs($cellReference, $args): array
+    protected static function filterHiddenArgs(mixed $cellReference, mixed $args): array
     {
         return array_filter(
             $args,
@@ -30,15 +26,11 @@ class Subtotal
         );
     }
 
-    /**
-     * @param mixed $cellReference
-     * @param mixed $args
-     */
-    protected static function filterFormulaArgs($cellReference, $args): array
+    protected static function filterFormulaArgs(mixed $cellReference, mixed $args): array
     {
         return array_filter(
             $args,
-            function ($index) use ($cellReference) {
+            function ($index) use ($cellReference): bool {
                 $explodeArray = explode('.', $index);
                 $row = $explodeArray[1] ?? '';
                 $column = $explodeArray[2] ?? '';
@@ -60,6 +52,9 @@ class Subtotal
         );
     }
 
+    /**
+     * @var array<int, callable>
+     */
     private const CALL_FUNCTIONS = [
         1 => [Statistical\Averages::class, 'average'], // 1 and 101
         [Statistical\Counts::class, 'COUNT'], // 2 and 102
@@ -87,10 +82,8 @@ class Subtotal
      *                    but ignore any values in the range that are
      *                    in hidden rows
      * @param mixed[] $args A mixed data series of values
-     *
-     * @return float|string
      */
-    public static function evaluate($functionType, ...$args)
+    public static function evaluate(mixed $functionType, ...$args): float|int|string
     {
         $cellReference = array_pop($args);
         $bArgs = Functions::flattenArrayIndexed($args);
@@ -124,7 +117,6 @@ class Subtotal
 
         $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
         if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) {
-            /** @var callable */
             $call = self::CALL_FUNCTIONS[$subtotal];
 
             return call_user_func_array($call, $aArgs);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
index 1a797c8..f939d9e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
@@ -5,7 +5,6 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
-use PhpOffice\PhpSpreadsheet\Calculation\Information\Value;
 
 class Sum
 {
@@ -18,10 +17,8 @@ class Sum
      *        SUM(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function sumIgnoringStrings(...$args)
+    public static function sumIgnoringStrings(mixed ...$args): float|int|string
     {
         $returnValue = 0;
 
@@ -47,27 +44,22 @@ class Sum
      *        SUM(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function sumErroringStrings(...$args)
+    public static function sumErroringStrings(mixed ...$args): float|int|string|array
     {
         $returnValue = 0;
         // Loop through the arguments
         $aArgs = Functions::flattenArrayIndexed($args);
         foreach ($aArgs as $k => $arg) {
             // Is it a numeric value?
-            if (is_numeric($arg) || empty($arg)) {
-                if (is_string($arg)) {
-                    $arg = (int) $arg;
-                }
+            if (is_numeric($arg)) {
                 $returnValue += $arg;
             } elseif (is_bool($arg)) {
                 $returnValue += (int) $arg;
             } elseif (ErrorValue::isError($arg)) {
                 return $arg;
-            // ignore non-numerics from cell, but fail as literals (except null)
             } elseif ($arg !== null && !Functions::isCellValue($k)) {
+                // ignore non-numerics from cell, but fail as literals (except null)
                 return ExcelError::VALUE();
             }
         }
@@ -83,9 +75,9 @@ class Sum
      *
      * @param mixed ...$args Data values
      *
-     * @return float|string The result, or a string containing an error
+     * @return float|int|string The result, or a string containing an error
      */
-    public static function product(...$args)
+    public static function product(mixed ...$args): string|int|float
     {
         $arrayList = $args;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
index 34b397c..b2e9cea 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
@@ -17,10 +17,8 @@ class SumSquares
      *        SUMSQ(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function sumSquare(...$args)
+    public static function sumSquare(mixed ...$args): string|int|float
     {
         try {
             $returnValue = 0;
@@ -49,10 +47,8 @@ class SumSquares
 
     /**
      * These functions accept only numeric arguments, not even strings which are numeric.
-     *
-     * @param mixed $item
      */
-    private static function numericNotString($item): bool
+    private static function numericNotString(mixed $item): bool
     {
         return is_numeric($item) && !is_string($item);
     }
@@ -62,10 +58,8 @@ class SumSquares
      *
      * @param mixed[] $matrixData1 Matrix #1
      * @param mixed[] $matrixData2 Matrix #2
-     *
-     * @return float|string
      */
-    public static function sumXSquaredMinusYSquared($matrixData1, $matrixData2)
+    public static function sumXSquaredMinusYSquared(array $matrixData1, array $matrixData2): string|int|float
     {
         try {
             $array1 = Functions::flattenArray($matrixData1);
@@ -90,10 +84,8 @@ class SumSquares
      *
      * @param mixed[] $matrixData1 Matrix #1
      * @param mixed[] $matrixData2 Matrix #2
-     *
-     * @return float|string
      */
-    public static function sumXSquaredPlusYSquared($matrixData1, $matrixData2)
+    public static function sumXSquaredPlusYSquared(array $matrixData1, array $matrixData2): string|int|float
     {
         try {
             $array1 = Functions::flattenArray($matrixData1);
@@ -118,10 +110,8 @@ class SumSquares
      *
      * @param mixed[] $matrixData1 Matrix #1
      * @param mixed[] $matrixData2 Matrix #2
-     *
-     * @return float|string
      */
-    public static function sumXMinusYSquared($matrixData1, $matrixData2)
+    public static function sumXMinusYSquared(array $matrixData1, array $matrixData2): string|int|float
     {
         try {
             $array1 = Functions::flattenArray($matrixData1);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
index c06f04d..733e3d6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php
@@ -21,7 +21,7 @@ class Cosine
      *         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 cos($number)
+    public static function cos(mixed $number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
@@ -47,7 +47,7 @@ class Cosine
      *         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 cosh($number)
+    public static function cosh(mixed $number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
index eeedef9..861159a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php
@@ -73,7 +73,7 @@ class Cotangent
      *         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 acot($number)
+    public static function acot($number): array|string|float
     {
         if (is_array($number)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
index 6af568c..924466e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
@@ -21,7 +21,7 @@ class Sine
      *         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 sin($angle)
+    public static function sin(mixed $angle): array|string|float
     {
         if (is_array($angle)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
@@ -47,7 +47,7 @@ class Sine
      *         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 sinh($angle)
+    public static function sinh(mixed $angle): array|string|float
     {
         if (is_array($angle)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
index 9e77021..9d6775f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
@@ -22,7 +22,7 @@ class Tangent
      *         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 tan($angle)
+    public static function tan(mixed $angle)
     {
         if (is_array($angle)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
@@ -48,7 +48,7 @@ class Tangent
      *         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 tanh($angle)
+    public static function tanh(mixed $angle): array|string|float
     {
         if (is_array($angle)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
@@ -134,12 +134,11 @@ class Tangent
      * @param mixed $xCoordinate should be float, the x-coordinate of the point, or can be an array of numbers
      * @param mixed $yCoordinate should be float, the y-coordinate of the point, or can be an array of numbers
      *
-     * @return array|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
      *         If an array of numbers is passed as one of the arguments, then the returned result will also be an array
      *            with the same dimensions
      */
-    public static function atan2($xCoordinate, $yCoordinate)
+    public static function atan2(mixed $xCoordinate, mixed $yCoordinate): array|string|float
     {
         if (is_array($xCoordinate) || is_array($yCoordinate)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $xCoordinate, $yCoordinate);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
index 943e209..f36f14d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
@@ -13,17 +13,20 @@ class Trunc
      * TRUNC.
      *
      * Truncates value to the number of fractional digits by number_digits.
+     * This will probably not be the precise result in the unlikely
+     * event that the number of digits to the left of the decimal
+     * plus the number of digits to the right exceeds PHP_FLOAT_DIG
+     * (or possibly that value minus 1).
+     * Excel is unlikely to do any better.
      *
-     * @param array|float $value
-     *                      Or can be an array of values
-     * @param array|int $digits
-     *                      Or can be an array of values
+     * @param array|float $value Or can be an array of values
+     * @param array|int $digits Or can be an array of values
      *
      * @return array|float|string Truncated value, 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 evaluate($value = 0, $digits = 0)
+    public static function evaluate(array|float|string|null $value = 0, array|int|string $digits = 0): array|float|string
     {
         if (is_array($value) || is_array($digits)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $digits);
@@ -36,15 +39,27 @@ class Trunc
             return $e->getMessage();
         }
 
-        $digits = floor($digits);
-
-        // Truncate
-        $adjust = 10 ** $digits;
-
-        if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
+        if ($value == 0) {
             return $value;
         }
 
-        return ((int) ($value * $adjust)) / $adjust;
+        if ($value >= 0) {
+            $minusSign = '';
+        } else {
+            $minusSign = '-';
+            $value = -$value;
+        }
+        $digits = (int) floor($digits);
+        if ($digits < 0) {
+            $result = (float) (substr(sprintf('%.0F', $value), 0, $digits) . str_repeat('0', -$digits));
+
+            return ($minusSign === '') ? $result : -$result;
+        }
+        $decimals = (floor($value) == (int) $value) ? (PHP_FLOAT_DIG - strlen((string) (int) $value)) : $digits;
+        $resultString = ($decimals < 0) ? sprintf('%F', $value) : sprintf('%.' . $decimals . 'F', $value);
+        $regExp = '/([.]\\d{' . $digits . '})\\d+$/';
+        $result = $minusSign . (preg_replace($regExp, '$1', $resultString) ?? $resultString);
+
+        return (float) $result;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
deleted file mode 100644
index 6758f47..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
+++ /dev/null
@@ -1,1741 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Conditional;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Confidence;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Counts;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Maximum;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Minimum;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Permutations;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends;
-use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances;
-
-/**
- * @deprecated 1.18.0
- *
- * @codeCoverageIgnore
- */
-class Statistical
-{
-    const LOG_GAMMA_X_MAX_VALUE = 2.55e305;
-    const EPS = 2.22e-16;
-    const MAX_VALUE = 1.2e308;
-    const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
-
-    /**
-     * AVEDEV.
-     *
-     * Returns the average of the absolute deviations of data points from their mean.
-     * AVEDEV is a measure of the variability in a data set.
-     *
-     * Excel Function:
-     *        AVEDEV(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the averageDeviations() method in the Statistical\Averages class instead
-     * @see Statistical\Averages::averageDeviations()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function AVEDEV(...$args)
-    {
-        return Averages::averageDeviations(...$args);
-    }
-
-    /**
-     * AVERAGE.
-     *
-     * Returns the average (arithmetic mean) of the arguments
-     *
-     * Excel Function:
-     *        AVERAGE(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the average() method in the Statistical\Averages class instead
-     * @see Statistical\Averages::average()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function AVERAGE(...$args)
-    {
-        return Averages::average(...$args);
-    }
-
-    /**
-     * AVERAGEA.
-     *
-     * Returns the average of its arguments, including numbers, text, and logical values
-     *
-     * Excel Function:
-     *        AVERAGEA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the averageA() method in the Statistical\Averages class instead
-     * @see Statistical\Averages::averageA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function AVERAGEA(...$args)
-    {
-        return Averages::averageA(...$args);
-    }
-
-    /**
-     * AVERAGEIF.
-     *
-     * Returns the average value from a range of cells that contain numbers within the list of arguments
-     *
-     * Excel Function:
-     *        AVERAGEIF(value1[,value2[, ...]],condition)
-     *
-     * @deprecated 1.17.0
-     *      Use the AVERAGEIF() method in the Statistical\Conditional class instead
-     * @see Statistical\Conditional::AVERAGEIF()
-     *
-     * @param mixed $range Data values
-     * @param string $condition the criteria that defines which cells will be checked
-     * @param mixed[] $averageRange Data values
-     *
-     * @return null|float|string
-     */
-    public static function AVERAGEIF($range, $condition, $averageRange = [])
-    {
-        return Conditional::AVERAGEIF($range, $condition, $averageRange);
-    }
-
-    /**
-     * BETADIST.
-     *
-     * Returns the beta distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\Beta class instead
-     * @see Statistical\Distributions\Beta::distribution()
-     *
-     * @param float $value Value at which you want to evaluate the distribution
-     * @param float $alpha Parameter to the distribution
-     * @param float $beta Parameter to the distribution
-     * @param mixed $rMin
-     * @param mixed $rMax
-     *
-     * @return array|float|string
-     */
-    public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
-    {
-        return Statistical\Distributions\Beta::distribution($value, $alpha, $beta, $rMin, $rMax);
-    }
-
-    /**
-     * BETAINV.
-     *
-     * Returns the inverse of the Beta distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\Beta class instead
-     * @see Statistical\Distributions\Beta::inverse()
-     *
-     * @param float $probability Probability at which you want to evaluate the distribution
-     * @param float $alpha Parameter to the distribution
-     * @param float $beta Parameter to the distribution
-     * @param float $rMin Minimum value
-     * @param float $rMax Maximum value
-     *
-     * @return array|float|string
-     */
-    public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
-    {
-        return Statistical\Distributions\Beta::inverse($probability, $alpha, $beta, $rMin, $rMax);
-    }
-
-    /**
-     * BINOMDIST.
-     *
-     * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
-     *        a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
-     *        when trials are independent, and when the probability of success is constant throughout the
-     *        experiment. For example, BINOMDIST can calculate the probability that two of the next three
-     *        babies born are male.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\Binomial class instead
-     * @see Statistical\Distributions\Binomial::distribution()
-     *
-     * @param mixed $value Number of successes in trials
-     * @param mixed $trials Number of trials
-     * @param mixed $probability Probability of success on each trial
-     * @param mixed $cumulative
-     *
-     * @return array|float|string
-     */
-    public static function BINOMDIST($value, $trials, $probability, $cumulative)
-    {
-        return Statistical\Distributions\Binomial::distribution($value, $trials, $probability, $cumulative);
-    }
-
-    /**
-     * CHIDIST.
-     *
-     * Returns the one-tailed probability of the chi-squared distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the distributionRightTail() method in the Statistical\Distributions\ChiSquared class instead
-     * @see Statistical\Distributions\ChiSquared::distributionRightTail()
-     *
-     * @param float $value Value for the function
-     * @param float $degrees degrees of freedom
-     *
-     * @return array|float|string
-     */
-    public static function CHIDIST($value, $degrees)
-    {
-        return Statistical\Distributions\ChiSquared::distributionRightTail($value, $degrees);
-    }
-
-    /**
-     * CHIINV.
-     *
-     * Returns the one-tailed probability of the chi-squared distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the inverseRightTail() method in the Statistical\Distributions\ChiSquared class instead
-     * @see Statistical\Distributions\ChiSquared::inverseRightTail()
-     *
-     * @param float $probability Probability for the function
-     * @param float $degrees degrees of freedom
-     *
-     * @return array|float|string
-     */
-    public static function CHIINV($probability, $degrees)
-    {
-        return Statistical\Distributions\ChiSquared::inverseRightTail($probability, $degrees);
-    }
-
-    /**
-     * CONFIDENCE.
-     *
-     * Returns the confidence interval for a population mean
-     *
-     * @deprecated 1.18.0
-     *      Use the CONFIDENCE() method in the Statistical\Confidence class instead
-     * @see Statistical\Confidence::CONFIDENCE()
-     *
-     * @param float $alpha
-     * @param float $stdDev Standard Deviation
-     * @param float $size
-     *
-     * @return array|float|string
-     */
-    public static function CONFIDENCE($alpha, $stdDev, $size)
-    {
-        return Confidence::CONFIDENCE($alpha, $stdDev, $size);
-    }
-
-    /**
-     * CORREL.
-     *
-     * Returns covariance, the average of the products of deviations for each data point pair.
-     *
-     * @deprecated 1.18.0
-     *      Use the CORREL() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::CORREL()
-     *
-     * @param mixed $yValues array of mixed Data Series Y
-     * @param null|mixed $xValues array of mixed Data Series X
-     *
-     * @return float|string
-     */
-    public static function CORREL($yValues, $xValues = null)
-    {
-        return Trends::CORREL($xValues, $yValues);
-    }
-
-    /**
-     * COUNT.
-     *
-     * Counts the number of cells that contain numbers within the list of arguments
-     *
-     * Excel Function:
-     *        COUNT(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the COUNT() method in the Statistical\Counts class instead
-     * @see Statistical\Counts::COUNT()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return int
-     */
-    public static function COUNT(...$args)
-    {
-        return Counts::COUNT(...$args);
-    }
-
-    /**
-     * COUNTA.
-     *
-     * Counts the number of cells that are not empty within the list of arguments
-     *
-     * Excel Function:
-     *        COUNTA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the COUNTA() method in the Statistical\Counts class instead
-     * @see Statistical\Counts::COUNTA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return int
-     */
-    public static function COUNTA(...$args)
-    {
-        return Counts::COUNTA(...$args);
-    }
-
-    /**
-     * COUNTBLANK.
-     *
-     * Counts the number of empty cells within the list of arguments
-     *
-     * Excel Function:
-     *        COUNTBLANK(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the COUNTBLANK() method in the Statistical\Counts class instead
-     * @see Statistical\Counts::COUNTBLANK()
-     *
-     * @param mixed $range Data values
-     *
-     * @return int
-     */
-    public static function COUNTBLANK($range)
-    {
-        return Counts::COUNTBLANK($range);
-    }
-
-    /**
-     * COUNTIF.
-     *
-     * Counts the number of cells that contain numbers within the list of arguments
-     *
-     * Excel Function:
-     *        COUNTIF(range,condition)
-     *
-     * @deprecated 1.17.0
-     *      Use the COUNTIF() method in the Statistical\Conditional class instead
-     * @see Statistical\Conditional::COUNTIF()
-     *
-     * @param mixed $range Data values
-     * @param string $condition the criteria that defines which cells will be counted
-     *
-     * @return int|string
-     */
-    public static function COUNTIF($range, $condition)
-    {
-        return Conditional::COUNTIF($range, $condition);
-    }
-
-    /**
-     * COUNTIFS.
-     *
-     * Counts the number of cells that contain numbers within the list of arguments
-     *
-     * Excel Function:
-     *        COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
-     *
-     * @deprecated 1.17.0
-     *      Use the COUNTIFS() method in the Statistical\Conditional class instead
-     * @see Statistical\Conditional::COUNTIFS()
-     *
-     * @param mixed $args Pairs of Ranges and Criteria
-     *
-     * @return int|string
-     */
-    public static function COUNTIFS(...$args)
-    {
-        return Conditional::COUNTIFS(...$args);
-    }
-
-    /**
-     * COVAR.
-     *
-     * Returns covariance, the average of the products of deviations for each data point pair.
-     *
-     * @deprecated 1.18.0
-     *      Use the COVAR() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::COVAR()
-     *
-     * @param mixed $yValues array of mixed Data Series Y
-     * @param mixed $xValues array of mixed Data Series X
-     *
-     * @return float|string
-     */
-    public static function COVAR($yValues, $xValues)
-    {
-        return Trends::COVAR($yValues, $xValues);
-    }
-
-    /**
-     * CRITBINOM.
-     *
-     * Returns the smallest value for which the cumulative binomial distribution is greater
-     *        than or equal to a criterion value
-     *
-     * See https://support.microsoft.com/en-us/help/828117/ for details of the algorithm used
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\Binomial class instead
-     * @see Statistical\Distributions\Binomial::inverse()
-     *
-     * @param float $trials number of Bernoulli trials
-     * @param float $probability probability of a success on each trial
-     * @param float $alpha criterion value
-     *
-     * @return array|int|string
-     */
-    public static function CRITBINOM($trials, $probability, $alpha)
-    {
-        return Statistical\Distributions\Binomial::inverse($trials, $probability, $alpha);
-    }
-
-    /**
-     * DEVSQ.
-     *
-     * Returns the sum of squares of deviations of data points from their sample mean.
-     *
-     * Excel Function:
-     *        DEVSQ(value1[,value2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the sumSquares() method in the Statistical\Deviations class instead
-     * @see Statistical\Deviations::sumSquares()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function DEVSQ(...$args)
-    {
-        return Statistical\Deviations::sumSquares(...$args);
-    }
-
-    /**
-     * EXPONDIST.
-     *
-     *    Returns the exponential distribution. Use EXPONDIST to model the time between events,
-     *        such as how long an automated bank teller takes to deliver cash. For example, you can
-     *        use EXPONDIST to determine the probability that the process takes at most 1 minute.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\Exponential class instead
-     * @see Statistical\Distributions\Exponential::distribution()
-     *
-     * @param float $value Value of the function
-     * @param float $lambda The parameter value
-     * @param bool $cumulative
-     *
-     * @return array|float|string
-     */
-    public static function EXPONDIST($value, $lambda, $cumulative)
-    {
-        return Statistical\Distributions\Exponential::distribution($value, $lambda, $cumulative);
-    }
-
-    /**
-     * F.DIST.
-     *
-     *    Returns the F probability distribution.
-     *    You can use this function to determine whether two data sets have different degrees of diversity.
-     *    For example, you can examine the test scores of men and women entering high school, and determine
-     *        if the variability in the females is different from that found in the males.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\F class instead
-     * @see Statistical\Distributions\F::distribution()
-     *
-     * @param float $value Value of the function
-     * @param int $u The numerator degrees of freedom
-     * @param int $v The denominator degrees of freedom
-     * @param bool $cumulative If cumulative is TRUE, F.DIST returns the cumulative distribution function;
-     *                         if FALSE, it returns the probability density function.
-     *
-     * @return array|float|string
-     */
-    public static function FDIST2($value, $u, $v, $cumulative)
-    {
-        return Statistical\Distributions\F::distribution($value, $u, $v, $cumulative);
-    }
-
-    /**
-     * FISHER.
-     *
-     * Returns the Fisher transformation at x. This transformation produces a function that
-     *        is normally distributed rather than skewed. Use this function to perform hypothesis
-     *        testing on the correlation coefficient.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\Fisher class instead
-     * @see Statistical\Distributions\Fisher::distribution()
-     *
-     * @param float $value
-     *
-     * @return array|float|string
-     */
-    public static function FISHER($value)
-    {
-        return Statistical\Distributions\Fisher::distribution($value);
-    }
-
-    /**
-     * FISHERINV.
-     *
-     * Returns the inverse of the Fisher transformation. Use this transformation when
-     *        analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
-     *        FISHERINV(y) = x.
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\Fisher class instead
-     * @see Statistical\Distributions\Fisher::inverse()
-     *
-     * @param float $value
-     *
-     * @return array|float|string
-     */
-    public static function FISHERINV($value)
-    {
-        return Statistical\Distributions\Fisher::inverse($value);
-    }
-
-    /**
-     * FORECAST.
-     *
-     * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
-     *
-     * @deprecated 1.18.0
-     *      Use the FORECAST() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::FORECAST()
-     *
-     * @param float $xValue Value of X for which we want to find Y
-     * @param mixed $yValues array of mixed Data Series Y
-     * @param mixed $xValues of mixed Data Series X
-     *
-     * @return array|bool|float|string
-     */
-    public static function FORECAST($xValue, $yValues, $xValues)
-    {
-        return Trends::FORECAST($xValue, $yValues, $xValues);
-    }
-
-    /**
-     * GAMMA.
-     *
-     * Returns the gamma function value.
-     *
-     * @deprecated 1.18.0
-     *      Use the gamma() method in the Statistical\Distributions\Gamma class instead
-     * @see Statistical\Distributions\Gamma::gamma()
-     *
-     * @param float $value
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function GAMMAFunction($value)
-    {
-        return Statistical\Distributions\Gamma::gamma($value);
-    }
-
-    /**
-     * GAMMADIST.
-     *
-     * Returns the gamma distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\Gamma class instead
-     * @see Statistical\Distributions\Gamma::distribution()
-     *
-     * @param float $value Value at which you want to evaluate the distribution
-     * @param float $a Parameter to the distribution
-     * @param float $b Parameter to the distribution
-     * @param bool $cumulative
-     *
-     * @return array|float|string
-     */
-    public static function GAMMADIST($value, $a, $b, $cumulative)
-    {
-        return Statistical\Distributions\Gamma::distribution($value, $a, $b, $cumulative);
-    }
-
-    /**
-     * GAMMAINV.
-     *
-     * Returns the inverse of the Gamma distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\Gamma class instead
-     * @see Statistical\Distributions\Gamma::inverse()
-     *
-     * @param float $probability Probability at which you want to evaluate the distribution
-     * @param float $alpha Parameter to the distribution
-     * @param float $beta Parameter to the distribution
-     *
-     * @return array|float|string
-     */
-    public static function GAMMAINV($probability, $alpha, $beta)
-    {
-        return Statistical\Distributions\Gamma::inverse($probability, $alpha, $beta);
-    }
-
-    /**
-     * GAMMALN.
-     *
-     * Returns the natural logarithm of the gamma function.
-     *
-     * @deprecated 1.18.0
-     *      Use the ln() method in the Statistical\Distributions\Gamma class instead
-     * @see Statistical\Distributions\Gamma::ln()
-     *
-     * @param float $value
-     *
-     * @return array|float|string
-     */
-    public static function GAMMALN($value)
-    {
-        return Statistical\Distributions\Gamma::ln($value);
-    }
-
-    /**
-     * GAUSS.
-     *
-     * Calculates the probability that a member of a standard normal population will fall between
-     *     the mean and z standard deviations from the mean.
-     *
-     * @deprecated 1.18.0
-     *      Use the gauss() method in the Statistical\Distributions\StandardNormal class instead
-     * @see Statistical\Distributions\StandardNormal::gauss()
-     *
-     * @param float $value
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function GAUSS($value)
-    {
-        return Statistical\Distributions\StandardNormal::gauss($value);
-    }
-
-    /**
-     * GEOMEAN.
-     *
-     * Returns the geometric mean of an array or range of positive data. For example, you
-     *        can use GEOMEAN to calculate average growth rate given compound interest with
-     *        variable rates.
-     *
-     * Excel Function:
-     *        GEOMEAN(value1[,value2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the geometric() method in the Statistical\Averages\Mean class instead
-     * @see Statistical\Averages\Mean::geometric()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function GEOMEAN(...$args)
-    {
-        return Statistical\Averages\Mean::geometric(...$args);
-    }
-
-    /**
-     * GROWTH.
-     *
-     * Returns values along a predicted exponential Trend
-     *
-     * @deprecated 1.18.0
-     *      Use the GROWTH() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::GROWTH()
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param mixed[] $xValues Data Series X
-     * @param mixed[] $newValues Values of X for which we want to find Y
-     * @param bool $const a logical value specifying whether to force the intersect to equal 0
-     *
-     * @return float[]
-     */
-    public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
-    {
-        return Trends::GROWTH($yValues, $xValues, $newValues, $const);
-    }
-
-    /**
-     * HARMEAN.
-     *
-     * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
-     *        arithmetic mean of reciprocals.
-     *
-     * Excel Function:
-     *        HARMEAN(value1[,value2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the harmonic() method in the Statistical\Averages\Mean class instead
-     * @see Statistical\Averages\Mean::harmonic()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function HARMEAN(...$args)
-    {
-        return Statistical\Averages\Mean::harmonic(...$args);
-    }
-
-    /**
-     * HYPGEOMDIST.
-     *
-     * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
-     * sample successes, given the sample size, population successes, and population size.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\HyperGeometric class instead
-     * @see Statistical\Distributions\HyperGeometric::distribution()
-     *
-     * @param mixed $sampleSuccesses Number of successes in the sample
-     * @param mixed $sampleNumber Size of the sample
-     * @param mixed $populationSuccesses Number of successes in the population
-     * @param mixed $populationNumber Population size
-     *
-     * @return array|float|string
-     */
-    public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
-    {
-        return Statistical\Distributions\HyperGeometric::distribution(
-            $sampleSuccesses,
-            $sampleNumber,
-            $populationSuccesses,
-            $populationNumber
-        );
-    }
-
-    /**
-     * INTERCEPT.
-     *
-     * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
-     *
-     * @deprecated 1.18.0
-     *      Use the INTERCEPT() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::INTERCEPT()
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param mixed[] $xValues Data Series X
-     *
-     * @return float|string
-     */
-    public static function INTERCEPT($yValues, $xValues)
-    {
-        return Trends::INTERCEPT($yValues, $xValues);
-    }
-
-    /**
-     * KURT.
-     *
-     * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
-     * or flatness of a distribution compared with the normal distribution. Positive
-     * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
-     * relatively flat distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the kurtosis() method in the Statistical\Deviations class instead
-     * @see Statistical\Deviations::kurtosis()
-     *
-     * @param array ...$args Data Series
-     *
-     * @return float|string
-     */
-    public static function KURT(...$args)
-    {
-        return Statistical\Deviations::kurtosis(...$args);
-    }
-
-    /**
-     * LARGE.
-     *
-     * Returns the nth largest value in a data set. You can use this function to
-     *        select a value based on its relative standing.
-     *
-     * Excel Function:
-     *        LARGE(value1[,value2[, ...]],entry)
-     *
-     * @deprecated 1.18.0
-     *      Use the large() method in the Statistical\Size class instead
-     * @see Statistical\Size::large()
-     *
-     * @param mixed $args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function LARGE(...$args)
-    {
-        return Statistical\Size::large(...$args);
-    }
-
-    /**
-     * LINEST.
-     *
-     * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
-     *        and then returns an array that describes the line.
-     *
-     * @deprecated 1.18.0
-     *      Use the LINEST() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::LINEST()
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param null|mixed[] $xValues Data Series X
-     * @param bool $const a logical value specifying whether to force the intersect to equal 0
-     * @param bool $stats a logical value specifying whether to return additional regression statistics
-     *
-     * @return array|int|string The result, or a string containing an error
-     */
-    public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
-    {
-        return Trends::LINEST($yValues, $xValues, $const, $stats);
-    }
-
-    /**
-     * LOGEST.
-     *
-     * Calculates an exponential curve that best fits the X and Y data series,
-     *        and then returns an array that describes the line.
-     *
-     * @deprecated 1.18.0
-     *      Use the LOGEST() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::LOGEST()
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param null|mixed[] $xValues Data Series X
-     * @param bool $const a logical value specifying whether to force the intersect to equal 0
-     * @param bool $stats a logical value specifying whether to return additional regression statistics
-     *
-     * @return array|int|string The result, or a string containing an error
-     */
-    public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
-    {
-        return Trends::LOGEST($yValues, $xValues, $const, $stats);
-    }
-
-    /**
-     * LOGINV.
-     *
-     * Returns the inverse of the normal cumulative distribution
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\LogNormal class instead
-     * @see Statistical\Distributions\LogNormal::inverse()
-     *
-     * @param float $probability
-     * @param float $mean
-     * @param float $stdDev
-     *
-     * @return array|float|string The result, or a string containing an error
-     *
-     * @TODO    Try implementing P J Acklam's refinement algorithm for greater
-     *            accuracy if I can get my head round the mathematics
-     *            (as described at) http://home.online.no/~pjacklam/notes/invnorm/
-     */
-    public static function LOGINV($probability, $mean, $stdDev)
-    {
-        return Statistical\Distributions\LogNormal::inverse($probability, $mean, $stdDev);
-    }
-
-    /**
-     * LOGNORMDIST.
-     *
-     * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
-     * with parameters mean and standard_dev.
-     *
-     * @deprecated 1.18.0
-     *      Use the cumulative() method in the Statistical\Distributions\LogNormal class instead
-     * @see Statistical\Distributions\LogNormal::cumulative()
-     *
-     * @param float $value
-     * @param float $mean
-     * @param float $stdDev
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function LOGNORMDIST($value, $mean, $stdDev)
-    {
-        return Statistical\Distributions\LogNormal::cumulative($value, $mean, $stdDev);
-    }
-
-    /**
-     * LOGNORM.DIST.
-     *
-     * Returns the lognormal distribution of x, where ln(x) is normally distributed
-     * with parameters mean and standard_dev.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\LogNormal class instead
-     * @see Statistical\Distributions\LogNormal::distribution()
-     *
-     * @param float $value
-     * @param float $mean
-     * @param float $stdDev
-     * @param bool $cumulative
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function LOGNORMDIST2($value, $mean, $stdDev, $cumulative = false)
-    {
-        return Statistical\Distributions\LogNormal::distribution($value, $mean, $stdDev, $cumulative);
-    }
-
-    /**
-     * MAX.
-     *
-     * MAX returns the value of the element of the values passed that has the highest value,
-     *        with negative numbers considered smaller than positive numbers.
-     *
-     * Excel Function:
-     *        max(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the MAX() method in the Statistical\Maximum class instead
-     * @see Statistical\Maximum::max()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float
-     */
-    public static function MAX(...$args)
-    {
-        return Maximum::max(...$args);
-    }
-
-    /**
-     * MAXA.
-     *
-     * Returns the greatest value in a list of arguments, including numbers, text, and logical values
-     *
-     * Excel Function:
-     *        maxA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the MAXA() method in the Statistical\Maximum class instead
-     * @see Statistical\Maximum::maxA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float
-     */
-    public static function MAXA(...$args)
-    {
-        return Maximum::maxA(...$args);
-    }
-
-    /**
-     * MAXIFS.
-     *
-     * Counts the maximum value within a range of cells that contain numbers within the list of arguments
-     *
-     * Excel Function:
-     *        MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
-     *
-     * @deprecated 1.17.0
-     *      Use the MAXIFS() method in the Statistical\Conditional class instead
-     * @see Statistical\Conditional::MAXIFS()
-     *
-     * @param mixed $args Data range and criterias
-     *
-     * @return float
-     */
-    public static function MAXIFS(...$args)
-    {
-        return Conditional::MAXIFS(...$args);
-    }
-
-    /**
-     * MEDIAN.
-     *
-     * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
-     *
-     * Excel Function:
-     *        MEDIAN(value1[,value2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the median() method in the Statistical\Averages class instead
-     * @see Statistical\Averages::median()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function MEDIAN(...$args)
-    {
-        return Statistical\Averages::median(...$args);
-    }
-
-    /**
-     * MIN.
-     *
-     * MIN returns the value of the element of the values passed that has the smallest value,
-     *        with negative numbers considered smaller than positive numbers.
-     *
-     * Excel Function:
-     *        MIN(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the min() method in the Statistical\Minimum class instead
-     * @see Statistical\Minimum::min()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float
-     */
-    public static function MIN(...$args)
-    {
-        return Minimum::min(...$args);
-    }
-
-    /**
-     * MINA.
-     *
-     * Returns the smallest value in a list of arguments, including numbers, text, and logical values
-     *
-     * Excel Function:
-     *        MINA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the minA() method in the Statistical\Minimum class instead
-     * @see Statistical\Minimum::minA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float
-     */
-    public static function MINA(...$args)
-    {
-        return Minimum::minA(...$args);
-    }
-
-    /**
-     * MINIFS.
-     *
-     * Returns the minimum value within a range of cells that contain numbers within the list of arguments
-     *
-     * Excel Function:
-     *        MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
-     *
-     * @deprecated 1.17.0
-     *      Use the MINIFS() method in the Statistical\Conditional class instead
-     * @see Statistical\Conditional::MINIFS()
-     *
-     * @param mixed $args Data range and criterias
-     *
-     * @return float
-     */
-    public static function MINIFS(...$args)
-    {
-        return Conditional::MINIFS(...$args);
-    }
-
-    /**
-     * MODE.
-     *
-     * Returns the most frequently occurring, or repetitive, value in an array or range of data
-     *
-     * Excel Function:
-     *        MODE(value1[,value2[, ...]])
-     *
-     * @deprecated 1.18.0
-     *      Use the mode() method in the Statistical\Averages class instead
-     * @see Statistical\Averages::mode()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function MODE(...$args)
-    {
-        return Statistical\Averages::mode(...$args);
-    }
-
-    /**
-     * NEGBINOMDIST.
-     *
-     * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
-     *        there will be number_f failures before the number_s-th success, when the constant
-     *        probability of a success is probability_s. This function is similar to the binomial
-     *        distribution, except that the number of successes is fixed, and the number of trials is
-     *        variable. Like the binomial, trials are assumed to be independent.
-     *
-     * @deprecated 1.18.0
-     *      Use the negative() method in the Statistical\Distributions\Binomial class instead
-     * @see Statistical\Distributions\Binomial::negative()
-     *
-     * @param mixed $failures Number of Failures
-     * @param mixed $successes Threshold number of Successes
-     * @param mixed $probability Probability of success on each trial
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function NEGBINOMDIST($failures, $successes, $probability)
-    {
-        return Statistical\Distributions\Binomial::negative($failures, $successes, $probability);
-    }
-
-    /**
-     * NORMDIST.
-     *
-     * Returns the normal distribution for the specified mean and standard deviation. This
-     * function has a very wide range of applications in statistics, including hypothesis
-     * testing.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\Normal class instead
-     * @see Statistical\Distributions\Normal::distribution()
-     *
-     * @param mixed $value
-     * @param mixed $mean Mean Value
-     * @param mixed $stdDev Standard Deviation
-     * @param mixed $cumulative
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function NORMDIST($value, $mean, $stdDev, $cumulative)
-    {
-        return Statistical\Distributions\Normal::distribution($value, $mean, $stdDev, $cumulative);
-    }
-
-    /**
-     * NORMINV.
-     *
-     * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\Normal class instead
-     * @see Statistical\Distributions\Normal::inverse()
-     *
-     * @param mixed $probability
-     * @param mixed $mean Mean Value
-     * @param mixed $stdDev Standard Deviation
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function NORMINV($probability, $mean, $stdDev)
-    {
-        return Statistical\Distributions\Normal::inverse($probability, $mean, $stdDev);
-    }
-
-    /**
-     * NORMSDIST.
-     *
-     * Returns the standard normal cumulative distribution function. The distribution has
-     * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
-     * table of standard normal curve areas.
-     *
-     * @deprecated 1.18.0
-     *      Use the cumulative() method in the Statistical\Distributions\StandardNormal class instead
-     * @see Statistical\Distributions\StandardNormal::cumulative()
-     *
-     * @param mixed $value
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function NORMSDIST($value)
-    {
-        return Statistical\Distributions\StandardNormal::cumulative($value);
-    }
-
-    /**
-     * NORM.S.DIST.
-     *
-     * Returns the standard normal cumulative distribution function. The distribution has
-     * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
-     * table of standard normal curve areas.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\StandardNormal class instead
-     * @see Statistical\Distributions\StandardNormal::distribution()
-     *
-     * @param mixed $value
-     * @param mixed $cumulative
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function NORMSDIST2($value, $cumulative)
-    {
-        return Statistical\Distributions\StandardNormal::distribution($value, $cumulative);
-    }
-
-    /**
-     * NORMSINV.
-     *
-     * Returns the inverse of the standard normal cumulative distribution
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\StandardNormal class instead
-     * @see Statistical\Distributions\StandardNormal::inverse()
-     *
-     * @param mixed $value
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function NORMSINV($value)
-    {
-        return Statistical\Distributions\StandardNormal::inverse($value);
-    }
-
-    /**
-     * PERCENTILE.
-     *
-     * Returns the nth percentile of values in a range..
-     *
-     * Excel Function:
-     *        PERCENTILE(value1[,value2[, ...]],entry)
-     *
-     * @deprecated 1.18.0
-     * Use the PERCENTILE() method in the Statistical\Percentiles class instead
-     * @see Statistical\Percentiles::PERCENTILE()
-     *
-     * @param mixed $args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function PERCENTILE(...$args)
-    {
-        return Statistical\Percentiles::PERCENTILE(...$args);
-    }
-
-    /**
-     * PERCENTRANK.
-     *
-     * Returns the rank of a value in a data set as a percentage of the data set.
-     * Note that the returned rank is simply rounded to the appropriate significant digits,
-     *      rather than floored (as MS Excel), so value 3 for a value set of  1, 2, 3, 4 will return
-     *      0.667 rather than 0.666
-     *
-     * @deprecated 1.18.0
-     * Use the PERCENTRANK() method in the Statistical\Percentiles class instead
-     * @see Statistical\Percentiles::PERCENTRANK()
-     *
-     * @param mixed $valueSet An array of, or a reference to, a list of numbers
-     * @param mixed $value the number whose rank you want to find
-     * @param mixed $significance the number of significant digits for the returned percentage value
-     *
-     * @return float|string (string if result is an error)
-     */
-    public static function PERCENTRANK($valueSet, $value, $significance = 3)
-    {
-        return Statistical\Percentiles::PERCENTRANK($valueSet, $value, $significance);
-    }
-
-    /**
-     * PERMUT.
-     *
-     * Returns the number of permutations for a given number of objects that can be
-     *        selected from number objects. A permutation is any set or subset of objects or
-     *        events where internal order is significant. Permutations are different from
-     *        combinations, for which the internal order is not significant. Use this function
-     *        for lottery-style probability calculations.
-     *
-     * @deprecated 1.17.0
-     * Use the PERMUT() method in the Statistical\Permutations class instead
-     * @see Statistical\Permutations::PERMUT()
-     *
-     * @param int $numObjs Number of different objects
-     * @param int $numInSet Number of objects in each permutation
-     *
-     * @return array|float|int|string Number of permutations, or a string containing an error
-     */
-    public static function PERMUT($numObjs, $numInSet)
-    {
-        return Permutations::PERMUT($numObjs, $numInSet);
-    }
-
-    /**
-     * POISSON.
-     *
-     * Returns the Poisson distribution. A common application of the Poisson distribution
-     * is predicting the number of events over a specific time, such as the number of
-     * cars arriving at a toll plaza in 1 minute.
-     *
-     * @deprecated 1.18.0
-     * Use the distribution() method in the Statistical\Distributions\Poisson class instead
-     * @see Statistical\Distributions\Poisson::distribution()
-     *
-     * @param mixed $value
-     * @param mixed $mean Mean Value
-     * @param mixed $cumulative
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function POISSON($value, $mean, $cumulative)
-    {
-        return Statistical\Distributions\Poisson::distribution($value, $mean, $cumulative);
-    }
-
-    /**
-     * QUARTILE.
-     *
-     * Returns the quartile of a data set.
-     *
-     * Excel Function:
-     *        QUARTILE(value1[,value2[, ...]],entry)
-     *
-     * @deprecated 1.18.0
-     * Use the QUARTILE() method in the Statistical\Percentiles class instead
-     * @see Statistical\Percentiles::QUARTILE()
-     *
-     * @param mixed $args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function QUARTILE(...$args)
-    {
-        return Statistical\Percentiles::QUARTILE(...$args);
-    }
-
-    /**
-     * RANK.
-     *
-     * Returns the rank of a number in a list of numbers.
-     *
-     * @deprecated 1.18.0
-     * Use the RANK() method in the Statistical\Percentiles class instead
-     * @see Statistical\Percentiles::RANK()
-     *
-     * @param mixed $value the number whose rank you want to find
-     * @param mixed $valueSet An array of, or a reference to, a list of numbers
-     * @param mixed $order Order to sort the values in the value set
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function RANK($value, $valueSet, $order = 0)
-    {
-        return Statistical\Percentiles::RANK($value, $valueSet, $order);
-    }
-
-    /**
-     * RSQ.
-     *
-     * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
-     *
-     * @deprecated 1.18.0
-     *      Use the RSQ() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::RSQ()
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param mixed[] $xValues Data Series X
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function RSQ($yValues, $xValues)
-    {
-        return Trends::RSQ($yValues, $xValues);
-    }
-
-    /**
-     * SKEW.
-     *
-     * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
-     * of a distribution around its mean. Positive skewness indicates a distribution with an
-     * asymmetric tail extending toward more positive values. Negative skewness indicates a
-     * distribution with an asymmetric tail extending toward more negative values.
-     *
-     * @deprecated 1.18.0
-     *      Use the skew() method in the Statistical\Deviations class instead
-     * @see Statistical\Deviations::skew()
-     *
-     * @param array ...$args Data Series
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function SKEW(...$args)
-    {
-        return Statistical\Deviations::skew(...$args);
-    }
-
-    /**
-     * SLOPE.
-     *
-     * Returns the slope of the linear regression line through data points in known_y's and known_x's.
-     *
-     * @deprecated 1.18.0
-     *      Use the SLOPE() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::SLOPE()
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param mixed[] $xValues Data Series X
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function SLOPE($yValues, $xValues)
-    {
-        return Trends::SLOPE($yValues, $xValues);
-    }
-
-    /**
-     * SMALL.
-     *
-     * Returns the nth smallest value in a data set. You can use this function to
-     *        select a value based on its relative standing.
-     *
-     * Excel Function:
-     *        SMALL(value1[,value2[, ...]],entry)
-     *
-     * @deprecated 1.18.0
-     *      Use the small() method in the Statistical\Size class instead
-     * @see Statistical\Size::small()
-     *
-     * @param mixed $args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function SMALL(...$args)
-    {
-        return Statistical\Size::small(...$args);
-    }
-
-    /**
-     * STANDARDIZE.
-     *
-     * Returns a normalized value from a distribution characterized by mean and standard_dev.
-     *
-     * @deprecated 1.18.0
-     *      Use the execute() method in the Statistical\Standardize class instead
-     * @see Statistical\Standardize::execute()
-     *
-     * @param float $value Value to normalize
-     * @param float $mean Mean Value
-     * @param float $stdDev Standard Deviation
-     *
-     * @return array|float|string Standardized value, or a string containing an error
-     */
-    public static function STANDARDIZE($value, $mean, $stdDev)
-    {
-        return Statistical\Standardize::execute($value, $mean, $stdDev);
-    }
-
-    /**
-     * STDEV.
-     *
-     * Estimates standard deviation based on a sample. The standard deviation is a measure of how
-     *        widely values are dispersed from the average value (the mean).
-     *
-     * Excel Function:
-     *        STDEV(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the STDEV() method in the Statistical\StandardDeviations class instead
-     * @see Statistical\StandardDeviations::STDEV()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string The result, or a string containing an error
-     */
-    public static function STDEV(...$args)
-    {
-        return StandardDeviations::STDEV(...$args);
-    }
-
-    /**
-     * STDEVA.
-     *
-     * Estimates standard deviation based on a sample, including numbers, text, and logical values
-     *
-     * Excel Function:
-     *        STDEVA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the STDEVA() method in the Statistical\StandardDeviations class instead
-     * @see Statistical\StandardDeviations::STDEVA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function STDEVA(...$args)
-    {
-        return StandardDeviations::STDEVA(...$args);
-    }
-
-    /**
-     * STDEVP.
-     *
-     * Calculates standard deviation based on the entire population
-     *
-     * Excel Function:
-     *        STDEVP(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the STDEVP() method in the Statistical\StandardDeviations class instead
-     * @see Statistical\StandardDeviations::STDEVP()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function STDEVP(...$args)
-    {
-        return StandardDeviations::STDEVP(...$args);
-    }
-
-    /**
-     * STDEVPA.
-     *
-     * Calculates standard deviation based on the entire population, including numbers, text, and logical values
-     *
-     * Excel Function:
-     *        STDEVPA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the STDEVPA() method in the Statistical\StandardDeviations class instead
-     * @see Statistical\StandardDeviations::STDEVPA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string
-     */
-    public static function STDEVPA(...$args)
-    {
-        return StandardDeviations::STDEVPA(...$args);
-    }
-
-    /**
-     * STEYX.
-     *
-     * @deprecated 1.18.0
-     *      Use the STEYX() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::STEYX()
-     *
-     * Returns the standard error of the predicted y-value for each x in the regression.
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param mixed[] $xValues Data Series X
-     *
-     * @return float|string
-     */
-    public static function STEYX($yValues, $xValues)
-    {
-        return Trends::STEYX($yValues, $xValues);
-    }
-
-    /**
-     * TDIST.
-     *
-     * Returns the probability of Student's T distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\StudentT class instead
-     * @see Statistical\Distributions\StudentT::distribution()
-     *
-     * @param float $value Value for the function
-     * @param float $degrees degrees of freedom
-     * @param float $tails number of tails (1 or 2)
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function TDIST($value, $degrees, $tails)
-    {
-        return Statistical\Distributions\StudentT::distribution($value, $degrees, $tails);
-    }
-
-    /**
-     * TINV.
-     *
-     * Returns the one-tailed probability of the Student-T distribution.
-     *
-     * @deprecated 1.18.0
-     *      Use the inverse() method in the Statistical\Distributions\StudentT class instead
-     * @see Statistical\Distributions\StudentT::inverse()
-     *
-     * @param float $probability Probability for the function
-     * @param float $degrees degrees of freedom
-     *
-     * @return array|float|string The result, or a string containing an error
-     */
-    public static function TINV($probability, $degrees)
-    {
-        return Statistical\Distributions\StudentT::inverse($probability, $degrees);
-    }
-
-    /**
-     * TREND.
-     *
-     * Returns values along a linear Trend
-     *
-     * @deprecated 1.18.0
-     *      Use the TREND() method in the Statistical\Trends class instead
-     * @see Statistical\Trends::TREND()
-     *
-     * @param mixed[] $yValues Data Series Y
-     * @param mixed[] $xValues Data Series X
-     * @param mixed[] $newValues Values of X for which we want to find Y
-     * @param bool $const a logical value specifying whether to force the intersect to equal 0
-     *
-     * @return float[]
-     */
-    public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
-    {
-        return Trends::TREND($yValues, $xValues, $newValues, $const);
-    }
-
-    /**
-     * TRIMMEAN.
-     *
-     * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
-     *        taken by excluding a percentage of data points from the top and bottom tails
-     *        of a data set.
-     *
-     * Excel Function:
-     *        TRIMEAN(value1[,value2[, ...]], $discard)
-     *
-     * @deprecated 1.18.0
-     *      Use the trim() method in the Statistical\Averages\Mean class instead
-     * @see Statistical\Averages\Mean::trim()
-     *
-     * @param mixed $args Data values
-     *
-     * @return float|string
-     */
-    public static function TRIMMEAN(...$args)
-    {
-        return Statistical\Averages\Mean::trim(...$args);
-    }
-
-    /**
-     * VARFunc.
-     *
-     * Estimates variance based on a sample.
-     *
-     * Excel Function:
-     *        VAR(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the VAR() method in the Statistical\Variances class instead
-     * @see Statistical\Variances::VAR()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string (string if result is an error)
-     */
-    public static function VARFunc(...$args)
-    {
-        return Variances::VAR(...$args);
-    }
-
-    /**
-     * VARA.
-     *
-     * Estimates variance based on a sample, including numbers, text, and logical values
-     *
-     * Excel Function:
-     *        VARA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the VARA() method in the Statistical\Variances class instead
-     * @see Statistical\Variances::VARA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string (string if result is an error)
-     */
-    public static function VARA(...$args)
-    {
-        return Variances::VARA(...$args);
-    }
-
-    /**
-     * VARP.
-     *
-     * Calculates variance based on the entire population
-     *
-     * Excel Function:
-     *        VARP(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the VARP() method in the Statistical\Variances class instead
-     * @see Statistical\Variances::VARP()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string (string if result is an error)
-     */
-    public static function VARP(...$args)
-    {
-        return Variances::VARP(...$args);
-    }
-
-    /**
-     * VARPA.
-     *
-     * Calculates variance based on the entire population, including numbers, text, and logical values
-     *
-     * Excel Function:
-     *        VARPA(value1[,value2[, ...]])
-     *
-     * @deprecated 1.17.0
-     *      Use the VARPA() method in the Statistical\Variances class instead
-     * @see Statistical\Variances::VARPA()
-     *
-     * @param mixed ...$args Data values
-     *
-     * @return float|string (string if result is an error)
-     */
-    public static function VARPA(...$args)
-    {
-        return Variances::VARPA(...$args);
-    }
-
-    /**
-     * WEIBULL.
-     *
-     * Returns the Weibull distribution. Use this distribution in reliability
-     * analysis, such as calculating a device's mean time to failure.
-     *
-     * @deprecated 1.18.0
-     *      Use the distribution() method in the Statistical\Distributions\Weibull class instead
-     * @see Statistical\Distributions\Weibull::distribution()
-     *
-     * @param float $value
-     * @param float $alpha Alpha Parameter
-     * @param float $beta Beta Parameter
-     * @param bool $cumulative
-     *
-     * @return array|float|string (string if result is an error)
-     */
-    public static function WEIBULL($value, $alpha, $beta, $cumulative)
-    {
-        return Statistical\Distributions\Weibull::distribution($value, $alpha, $beta, $cumulative);
-    }
-
-    /**
-     * ZTEST.
-     *
-     * Returns the one-tailed P-value of a z-test.
-     *
-     * For a given hypothesized population mean, x, Z.TEST returns the probability that the sample mean would be
-     *     greater than the average of observations in the data set (array) — that is, the observed sample mean.
-     *
-     * @deprecated 1.18.0
-     *      Use the zTest() method in the Statistical\Distributions\StandardNormal class instead
-     * @see Statistical\Distributions\StandardNormal::zTest()
-     *
-     * @param float $dataSet
-     * @param float $m0 Alpha Parameter
-     * @param float $sigma Beta Parameter
-     *
-     * @return array|float|string (string if result is an error)
-     */
-    public static function ZTEST($dataSet, $m0, $sigma = null)
-    {
-        return Statistical\Distributions\StandardNormal::zTest($dataSet, $m0, $sigma);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
index 10933f4..a10b278 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php
@@ -10,13 +10,8 @@ abstract class AggregateBase
      * MS Excel does not count Booleans if passed as cell values, but they are counted if passed as literals.
      * OpenOffice Calc always counts Booleans.
      * Gnumeric never counts Booleans.
-     *
-     * @param mixed $arg
-     * @param mixed $k
-     *
-     * @return int|mixed
      */
-    protected static function testAcceptedBoolean($arg, $k)
+    protected static function testAcceptedBoolean(mixed $arg, mixed $k): mixed
     {
         if (!is_bool($arg)) {
             return $arg;
@@ -41,13 +36,7 @@ abstract class AggregateBase
         return $arg;
     }
 
-    /**
-     * @param mixed $arg
-     * @param mixed $k
-     *
-     * @return bool
-     */
-    protected static function isAcceptedCountable($arg, $k, bool $countNull = false)
+    protected static function isAcceptedCountable(mixed $arg, mixed $k, bool $countNull = false): bool
     {
         if ($countNull && $arg === null && !Functions::isCellValue($k) && Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_GNUMERIC) {
             return true;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages.php
index f2b5b2a..998e14d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages.php
@@ -20,7 +20,7 @@ class Averages extends AggregateBase
      *
      * @return float|string (string if result is an error)
      */
-    public static function averageDeviations(...$args)
+    public static function averageDeviations(mixed ...$args): string|float
     {
         $aArgs = Functions::flattenArrayIndexed($args);
 
@@ -67,9 +67,9 @@ class Averages extends AggregateBase
      *
      * @param mixed ...$args Data values
      *
-     * @return float|string (string if result is an error)
+     * @return float|int|string (string if result is an error)
      */
-    public static function average(...$args)
+    public static function average(mixed ...$args): string|int|float
     {
         $returnValue = $aCount = 0;
 
@@ -106,9 +106,9 @@ class Averages extends AggregateBase
      *
      * @param mixed ...$args Data values
      *
-     * @return float|string (string if result is an error)
+     * @return float|int|string (string if result is an error)
      */
-    public static function averageA(...$args)
+    public static function averageA(mixed ...$args): string|int|float
     {
         $returnValue = null;
 
@@ -147,7 +147,7 @@ class Averages extends AggregateBase
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function median(...$args)
+    public static function median(mixed ...$args): float|string
     {
         $aArgs = Functions::flattenArray($args);
 
@@ -181,7 +181,7 @@ class Averages extends AggregateBase
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function mode(...$args)
+    public static function mode(mixed ...$args): float|string
     {
         $returnValue = ExcelError::NA();
 
@@ -196,22 +196,22 @@ class Averages extends AggregateBase
         return $returnValue;
     }
 
-    protected static function filterArguments($args)
+    protected static function filterArguments(array $args): array
     {
         return array_filter(
             $args,
-            function ($value) {
+            function ($value): bool {
                 // Is it a numeric value?
-                return  is_numeric($value) && (!is_string($value));
+                return is_numeric($value) && (!is_string($value));
             }
         );
     }
 
-    //
-    //    Special variant of array_count_values that isn't limited to strings and integers,
-    //        but can work with floating point numbers as values
-    //
-    private static function modeCalc($data)
+    /**
+     * Special variant of array_count_values that isn't limited to strings and integers,
+     * but can work with floating point numbers as values.
+     */
+    private static function modeCalc(array $data): float|string
     {
         $frequencyArray = [];
         $index = 0;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
index 001c91e..2051675 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php
@@ -22,10 +22,8 @@ class Mean
      *        GEOMEAN(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function geometric(...$args)
+    public static function geometric(mixed ...$args): float|int|string
     {
         $aArgs = Functions::flattenArray($args);
 
@@ -50,10 +48,8 @@ class Mean
      *        HARMEAN(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function harmonic(...$args)
+    public static function harmonic(mixed ...$args): string|float|int
     {
         // Loop through arguments
         $aArgs = Functions::flattenArray($args);
@@ -93,10 +89,8 @@ class Mean
      *        TRIMEAN(value1[,value2[, ...]], $discard)
      *
      * @param mixed $args Data values
-     *
-     * @return float|string
      */
-    public static function trim(...$args)
+    public static function trim(mixed ...$args): float|string
     {
         $aArgs = Functions::flattenArray($args);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
index 5d40943..ae98c60 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Conditional.php
@@ -25,12 +25,10 @@ class Conditional
      *        AVERAGEIF(range,condition[, average_range])
      *
      * @param mixed $range Data values
-     * @param string $condition the criteria that defines which cells will be checked
+     * @param null|array|string $condition the criteria that defines which cells will be checked
      * @param mixed $averageRange Data values
-     *
-     * @return null|float|string
      */
-    public static function AVERAGEIF($range, $condition, $averageRange = [])
+    public static function AVERAGEIF(mixed $range, null|array|string $condition, mixed $averageRange = []): null|int|float|string
     {
         if (!is_array($range) || !is_array($averageRange) || array_key_exists(0, $range) || array_key_exists(0, $averageRange)) {
             throw new CalcException('Must specify range of cells, not any kind of literal');
@@ -50,10 +48,8 @@ class Conditional
      *        AVERAGEIFS(average_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
      *
      * @param mixed $args Pairs of Ranges and Criteria
-     *
-     * @return null|float|string
      */
-    public static function AVERAGEIFS(...$args)
+    public static function AVERAGEIFS(mixed ...$args): null|int|float|string
     {
         if (empty($args)) {
             return 0.0;
@@ -81,18 +77,14 @@ class Conditional
      *        COUNTIF(range,condition)
      *
      * @param mixed[] $range Data values
-     * @param string $condition the criteria that defines which cells will be counted
-     *
-     * @return int|string
+     * @param null|array|string $condition the criteria that defines which cells will be counted
      */
-    public static function COUNTIF($range, $condition)
+    public static function COUNTIF(array $range, null|array|string $condition): string|int
     {
         // Filter out any empty values that shouldn't be included in a COUNT
         $range = array_filter(
             Functions::flattenArray($range),
-            function ($value) {
-                return $value !== null && $value !== '';
-            }
+            fn ($value): bool => $value !== null && $value !== ''
         );
 
         $range = array_merge([[self::CONDITION_COLUMN_NAME]], array_chunk($range, 1));
@@ -110,10 +102,8 @@ class Conditional
      *        COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
      *
      * @param mixed $args Pairs of Ranges and Criteria
-     *
-     * @return int|string
      */
-    public static function COUNTIFS(...$args)
+    public static function COUNTIFS(mixed ...$args): int|string
     {
         if (empty($args)) {
             return 0;
@@ -136,10 +126,8 @@ class Conditional
      *        MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
      *
      * @param mixed $args Pairs of Ranges and Criteria
-     *
-     * @return null|float|string
      */
-    public static function MAXIFS(...$args)
+    public static function MAXIFS(mixed ...$args): null|float|string
     {
         if (empty($args)) {
             return 0.0;
@@ -160,10 +148,8 @@ class Conditional
      *        MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
      *
      * @param mixed $args Pairs of Ranges and Criteria
-     *
-     * @return null|float|string
      */
-    public static function MINIFS(...$args)
+    public static function MINIFS(mixed ...$args): null|float|string
     {
         if (empty($args)) {
             return 0.0;
@@ -183,13 +169,9 @@ class Conditional
      * Excel Function:
      *        SUMIF(range, criteria, [sum_range])
      *
-     * @param mixed $range Data values
-     * @param mixed $sumRange
-     * @param mixed $condition
-     *
-     * @return float|string
+     * @param array $range Data values
      */
-    public static function SUMIF($range, $condition, $sumRange = [])
+    public static function SUMIF(array $range, mixed $condition, array $sumRange = []): null|float|string
     {
         $database = self::databaseFromRangeAndValue($range, $sumRange);
         $condition = [[self::CONDITION_COLUMN_NAME, self::VALUE_COLUMN_NAME], [$condition, null]];
@@ -206,10 +188,8 @@ class Conditional
      *        SUMIFS(average_range, criteria_range1, criteria1, [criteria_range2, criteria2]…)
      *
      * @param mixed $args Pairs of Ranges and Criteria
-     *
-     * @return null|float|string
      */
-    public static function SUMIFS(...$args)
+    public static function SUMIFS(mixed ...$args): null|float|string
     {
         if (empty($args)) {
             return 0.0;
@@ -223,30 +203,30 @@ class Conditional
         return DSum::evaluate($database, self::VALUE_COLUMN_NAME, $conditions);
     }
 
+    /** @param array $args */
     private static function buildConditionSet(...$args): array
     {
         $conditions = self::buildConditions(1, ...$args);
 
-        // Scrutinizer thinks first parameter of array_map can't be null. It is wrong.
-        return array_map(/** @scrutinizer ignore-type */ null, ...$conditions);
+        return array_map(null, ...$conditions);
     }
 
+    /** @param array $args */
     private static function buildConditionSetForValueRange(...$args): array
     {
         $conditions = self::buildConditions(2, ...$args);
 
         if (count($conditions) === 1) {
             return array_map(
-                function ($value) {
-                    return [$value];
-                },
+                fn ($value): array => [$value],
                 $conditions[0]
             );
         }
 
-        return array_map(/** @scrutinizer ignore-type */ null, ...$conditions);
+        return array_map(null, ...$conditions);
     }
 
+    /** @param array $args */
     private static function buildConditions(int $startOffset, ...$args): array
     {
         $conditions = [];
@@ -261,6 +241,7 @@ class Conditional
         return $conditions;
     }
 
+    /** @param array $args */
     private static function buildDatabase(...$args): array
     {
         $database = [];
@@ -268,6 +249,7 @@ class Conditional
         return self::buildDataSet(0, $database, ...$args);
     }
 
+    /** @param array $args */
     private static function buildDatabaseWithValueRange(...$args): array
     {
         $database = [];
@@ -279,6 +261,7 @@ class Conditional
         return self::buildDataSet(1, $database, ...$args);
     }
 
+    /** @param array $args */
     private static function buildDataSet(int $startOffset, array $database, ...$args): array
     {
         $pairCount = 1;
@@ -291,7 +274,7 @@ class Conditional
             ++$pairCount;
         }
 
-        return array_map(/** @scrutinizer ignore-type */ null, ...$database);
+        return array_map(null, ...$database);
     }
 
     private static function databaseFromRangeAndValue(array $range, array $valueRange = []): array
@@ -303,7 +286,7 @@ class Conditional
             $valueRange = $range;
         }
 
-        $database = array_map(/** @scrutinizer ignore-type */ null, array_merge([self::CONDITION_COLUMN_NAME], $range), array_merge([self::VALUE_COLUMN_NAME], $valueRange));
+        $database = array_map(null, array_merge([self::CONDITION_COLUMN_NAME], $range), array_merge([self::VALUE_COLUMN_NAME], $valueRange));
 
         return $database;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php
index ec2ce34..492438a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php
@@ -23,11 +23,10 @@ class Confidence
      * @param mixed $size As an integer
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 CONFIDENCE($alpha, $stdDev, $size)
+    public static function CONFIDENCE(mixed $alpha, mixed $stdDev, mixed $size)
     {
         if (is_array($alpha) || is_array($stdDev) || is_array($size)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $alpha, $stdDev, $size);
@@ -44,7 +43,7 @@ class Confidence
         if (($alpha <= 0) || ($alpha >= 1) || ($stdDev <= 0) || ($size < 1)) {
             return ExcelError::NAN();
         }
-        /** @var float */
+        /** @var float $temp */
         $temp = Distributions\StandardNormal::inverse(1 - $alpha / 2);
 
         return Functions::scalar($temp * $stdDev / sqrt($size));
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
index 6792730..20ed634 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Counts.php
@@ -16,10 +16,8 @@ class Counts extends AggregateBase
      *        COUNT(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return int
      */
-    public static function COUNT(...$args)
+    public static function COUNT(mixed ...$args): int
     {
         $returnValue = 0;
 
@@ -47,10 +45,8 @@ class Counts extends AggregateBase
      *        COUNTA(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return int
      */
-    public static function COUNTA(...$args)
+    public static function COUNTA(mixed ...$args): int
     {
         $returnValue = 0;
 
@@ -75,10 +71,8 @@ class Counts extends AggregateBase
      *        COUNTBLANK(value1[,value2[, ...]])
      *
      * @param mixed $range Data values
-     *
-     * @return int
      */
-    public static function COUNTBLANK($range)
+    public static function COUNTBLANK(mixed $range): int
     {
         if ($range === null) {
             return 1;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
index 6b1db3a..77c0d38 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php
@@ -16,10 +16,8 @@ class Deviations
      *        DEVSQ(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function sumSquares(...$args)
+    public static function sumSquares(mixed ...$args): string|float
     {
         $aArgs = Functions::flattenArrayIndexed($args);
 
@@ -34,9 +32,9 @@ class Deviations
         foreach ($aArgs as $k => $arg) {
             // Is it a numeric value?
             if (
-                (is_bool($arg)) &&
-                ((!Functions::isCellValue($k)) ||
-                    (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
+                (is_bool($arg))
+                && ((!Functions::isCellValue($k))
+                    || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
             ) {
                 $arg = (int) $arg;
             }
@@ -58,17 +56,15 @@ class Deviations
      * relatively flat distribution.
      *
      * @param array ...$args Data Series
-     *
-     * @return float|string
      */
-    public static function kurtosis(...$args)
+    public static function kurtosis(...$args): string|int|float
     {
         $aArgs = Functions::flattenArrayIndexed($args);
         $mean = Averages::average($aArgs);
         if (!is_numeric($mean)) {
             return ExcelError::DIV0();
         }
-        $stdDev = StandardDeviations::STDEV($aArgs);
+        $stdDev = (float) StandardDeviations::STDEV($aArgs);
 
         if ($stdDev > 0) {
             $count = $summer = 0;
@@ -85,9 +81,9 @@ class Deviations
             }
 
             if ($count > 3) {
-                return $summer * ($count * ($count + 1) /
-                        (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 /
-                        (($count - 2) * ($count - 3)));
+                return $summer * ($count * ($count + 1)
+                        / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2
+                        / (($count - 2) * ($count - 3)));
             }
         }
 
@@ -106,7 +102,7 @@ class Deviations
      *
      * @return float|int|string The result, or a string containing an error
      */
-    public static function skew(...$args)
+    public static function skew(...$args): string|int|float
     {
         $aArgs = Functions::flattenArrayIndexed($args);
         $mean = Averages::average($aArgs);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
index 8b41ac8..16817ce 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php
@@ -33,11 +33,10 @@ class Beta
      * @param mixed $rMax as an float
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distribution($value, $alpha, $beta, $rMin = 0.0, $rMax = 1.0)
+    public static function distribution(mixed $value, mixed $alpha, mixed $beta, mixed $rMin = 0.0, mixed $rMax = 1.0): array|string|float
     {
         if (is_array($value) || is_array($alpha) || is_array($beta) || is_array($rMin) || is_array($rMax)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $alpha, $beta, $rMin, $rMax);
@@ -87,11 +86,10 @@ class Beta
      * @param mixed $rMax Maximum value as a float
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 inverse($probability, $alpha, $beta, $rMin = 0.0, $rMax = 1.0)
+    public static function inverse(mixed $probability, mixed $alpha, mixed $beta, mixed $rMin = 0.0, mixed $rMax = 1.0): array|string|float
     {
         if (is_array($probability) || is_array($alpha) || is_array($beta) || is_array($rMin) || is_array($rMax)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta, $rMin, $rMax);
@@ -122,10 +120,7 @@ class Beta
         return self::calculateInverse($probability, $alpha, $beta, $rMin, $rMax);
     }
 
-    /**
-     * @return float|string
-     */
-    private static function calculateInverse(float $probability, float $alpha, float $beta, float $rMin, float $rMax)
+    private static function calculateInverse(float $probability, float $alpha, float $beta, float $rMin, float $rMax): string|float
     {
         $a = 0;
         $b = 2;
@@ -184,11 +179,12 @@ class Beta
     }
 
     // Function cache for logBeta function
-    private static $logBetaCacheP = 0.0;
 
-    private static $logBetaCacheQ = 0.0;
+    private static float $logBetaCacheP = 0.0;
 
-    private static $logBetaCacheResult = 0.0;
+    private static float $logBetaCacheQ = 0.0;
+
+    private static float $logBetaCacheResult = 0.0;
 
     /**
      * The natural logarithm of the beta function.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
index 02b53e8..2ce3fd5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php
@@ -30,11 +30,10 @@ class Binomial
      * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distribution($value, $trials, $probability, $cumulative)
+    public static function distribution(mixed $value, mixed $trials, mixed $probability, mixed $cumulative)
     {
         if (is_array($value) || is_array($trials) || is_array($probability) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $trials, $probability, $cumulative);
@@ -56,7 +55,7 @@ class Binomial
         if ($cumulative) {
             return self::calculateCumulativeBinomial($value, $trials, $probability);
         }
-        /** @var float */
+        /** @var float $comb */
         $comb = Combinations::withoutRepetition($trials, $value);
 
         return $comb * $probability ** $value
@@ -79,11 +78,10 @@ class Binomial
      *                           If null, then this will indicate the same as the number of Successes
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 range($trials, $probability, $successes, $limit = null)
+    public static function range(mixed $trials, mixed $probability, mixed $successes, mixed $limit = null): array|string|float|int
     {
         if (is_array($trials) || is_array($probability) || is_array($successes) || is_array($limit)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $successes, $limit);
@@ -109,7 +107,7 @@ class Binomial
 
         $summer = 0;
         for ($i = $successes; $i <= $limit; ++$i) {
-            /** @var float */
+            /** @var float $comb */
             $comb = Combinations::withoutRepetition($trials, $i);
             $summer += $comb * $probability ** $i
                 * (1 - $probability) ** ($trials - $i);
@@ -141,7 +139,7 @@ class Binomial
      * TODO Add support for the cumulative flag not present for NEGBINOMDIST, but introduced for NEGBINOM.DIST
      *      The cumulative default should be false to reflect the behaviour of NEGBINOMDIST
      */
-    public static function negative($failures, $successes, $probability)
+    public static function negative(mixed $failures, mixed $successes, mixed $probability): array|string|float
     {
         if (is_array($failures) || is_array($successes) || is_array($probability)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $failures, $successes, $probability);
@@ -163,7 +161,7 @@ class Binomial
                 return ExcelError::NAN();
             }
         }
-        /** @var float */
+        /** @var float $comb */
         $comb = Combinations::withoutRepetition($failures + $successes - 1, $successes - 1);
 
         return $comb
@@ -183,11 +181,10 @@ class Binomial
      * @param mixed $alpha criterion value as a float
      *                      Or can be an array of values
      *
-     * @return array|int|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 inverse($trials, $probability, $alpha)
+    public static function inverse(mixed $trials, mixed $probability, mixed $alpha): array|string|int
     {
         if (is_array($trials) || is_array($probability) || is_array($alpha)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $alpha);
@@ -219,14 +216,11 @@ class Binomial
         return $successes;
     }
 
-    /**
-     * @return float|int
-     */
-    private static function calculateCumulativeBinomial(int $value, int $trials, float $probability)
+    private static function calculateCumulativeBinomial(int $value, int $trials, float $probability): float|int
     {
         $summer = 0;
         for ($i = 0; $i <= $value; ++$i) {
-            /** @var float */
+            /** @var float $comb */
             $comb = Combinations::withoutRepetition($trials, $i);
             $summer += $comb * $probability ** $i
                 * (1 - $probability) ** ($trials - $i);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
index c874336..49b0dfc 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php
@@ -11,8 +11,6 @@ class ChiSquared
 {
     use ArrayEnabled;
 
-    private const MAX_ITERATIONS = 256;
-
     private const EPS = 2.22e-16;
 
     /**
@@ -25,11 +23,10 @@ class ChiSquared
      * @param mixed $degrees Integer degrees of freedom
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distributionRightTail($value, $degrees)
+    public static function distributionRightTail(mixed $value, mixed $degrees): array|string|int|float
     {
         if (is_array($value) || is_array($degrees)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees);
@@ -68,11 +65,10 @@ class ChiSquared
      * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distributionLeftTail($value, $degrees, $cumulative)
+    public static function distributionLeftTail(mixed $value, mixed $degrees, mixed $cumulative): array|string|int|float
     {
         if (is_array($value) || is_array($degrees) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees, $cumulative);
@@ -98,11 +94,13 @@ class ChiSquared
         }
 
         if ($cumulative === true) {
-            return 1 - self::distributionRightTail($value, $degrees);
+            $temp = self::distributionRightTail($value, $degrees);
+
+            return 1 - (is_numeric($temp) ? $temp : 0);
         }
 
-        return ($value ** (($degrees / 2) - 1) * exp(-$value / 2)) /
-            ((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
+        return ($value ** (($degrees / 2) - 1) * exp(-$value / 2))
+            / ((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
     }
 
     /**
@@ -115,11 +113,10 @@ class ChiSquared
      * @param mixed $degrees Integer degrees of freedom
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 inverseRightTail($probability, $degrees)
+    public static function inverseRightTail(mixed $probability, mixed $degrees)
     {
         if (is_array($probability) || is_array($degrees)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
@@ -136,7 +133,7 @@ class ChiSquared
             return ExcelError::NAN();
         }
 
-        $callback = function ($value) use ($degrees) {
+        $callback = function ($value) use ($degrees): float {
             return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2)
                     / Gamma::gammaValue($degrees / 2));
         };
@@ -156,11 +153,10 @@ class ChiSquared
      * @param mixed $degrees Integer degrees of freedom
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 inverseLeftTail($probability, $degrees)
+    public static function inverseLeftTail(mixed $probability, mixed $degrees): array|string|float
     {
         if (is_array($probability) || is_array($degrees)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
@@ -189,15 +185,13 @@ class ChiSquared
      *
      * @param mixed $actual an array of observed frequencies
      * @param mixed $expected an array of expected frequencies
-     *
-     * @return float|string
      */
-    public static function test($actual, $expected)
+    public static function test(mixed $actual, mixed $expected): float|string
     {
         $rows = count($actual);
         $actual = Functions::flattenArray($actual);
         $expected = Functions::flattenArray($expected);
-        $columns = count($actual) / $rows;
+        $columns = intdiv(count($actual), $rows);
 
         $countActuals = count($actual);
         $countExpected = count($expected);
@@ -261,12 +255,12 @@ class ChiSquared
         return $chi2;
     }
 
-    private static function pchisq($chi2, $degrees)
+    private static function pchisq(float $chi2, int $degrees): float
     {
         return self::gammp($degrees, 0.5 * $chi2);
     }
 
-    private static function gammp($n, $x)
+    private static function gammp(int $n, float $x): float
     {
         if ($x < 0.5 * $n + 1) {
             return self::gser($n, $x);
@@ -279,9 +273,9 @@ class ChiSquared
     // series representation. Algorithm from numerical recipe.
     // Assume that n is a positive integer and x>0, won't check arguments.
     // Relative error controlled by the eps parameter
-    private static function gser($n, $x)
+    private static function gser(int $n, float $x): float
     {
-        /** @var float */
+        /** @var float $gln */
         $gln = Gamma::ln($n / 2);
         $a = 0.5 * $n;
         $ap = $a;
@@ -303,9 +297,9 @@ class ChiSquared
     // its continued fraction representation. Algorithm from numerical recipe.
     // Assume that n is a postive integer and x>0, won't check arguments.
     // Relative error controlled by the eps parameter
-    private static function gcf($n, $x)
+    private static function gcf(int $n, float $x): float
     {
-        /** @var float */
+        /** @var float $gln */
         $gln = Gamma::ln($n / 2);
         $a = 0.5 * $n;
         $b = $x + 1 - $a;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
index bd45a22..61c62f6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php
@@ -8,10 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StatisticalValidations;
 
 class DistributionValidations extends StatisticalValidations
 {
-    /**
-     * @param mixed $probability
-     */
-    public static function validateProbability($probability): float
+    public static function validateProbability(mixed $probability): float
     {
         $probability = self::validateFloat($probability);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
index a03671c..5526473 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php
@@ -24,11 +24,10 @@ class Exponential
      * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distribution($value, $lambda, $cumulative)
+    public static function distribution(mixed $value, mixed $lambda, mixed $cumulative): array|string|float
     {
         if (is_array($value) || is_array($lambda) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $lambda, $cumulative);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
index ff413b6..aa7a19d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php
@@ -27,11 +27,10 @@ class F
      * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distribution($value, $u, $v, $cumulative)
+    public static function distribution(mixed $value, mixed $u, mixed $v, mixed $cumulative): array|string|float
     {
         if (is_array($value) || is_array($u) || is_array($v) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $u, $v, $cumulative);
@@ -56,9 +55,9 @@ class F
             return Beta::incompleteBeta($adjustedValue, $u / 2, $v / 2);
         }
 
-        return (Gamma::gammaValue(($v + $u) / 2) /
-                (Gamma::gammaValue($u / 2) * Gamma::gammaValue($v / 2))) *
-            (($u / $v) ** ($u / 2)) *
-            (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2)));
+        return (Gamma::gammaValue(($v + $u) / 2)
+                / (Gamma::gammaValue($u / 2) * Gamma::gammaValue($v / 2)))
+            * (($u / $v) ** ($u / 2))
+            * (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2)));
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
index df9906e..9ad10db 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php
@@ -20,11 +20,10 @@ class Fisher
      * @param mixed $value Float value for which we want the probability
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distribution($value)
+    public static function distribution(mixed $value): array|string|float
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -53,11 +52,10 @@ class Fisher
      * @param mixed $probability Float probability at which you want to evaluate the distribution
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 inverse($probability)
+    public static function inverse(mixed $probability): array|string|float
     {
         if (is_array($probability)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $probability);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
index 216f234..babe937 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php
@@ -22,7 +22,7 @@ class Gamma extends GammaBase
      *         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 gamma($value)
+    public static function gamma(mixed $value): array|string|float
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -55,11 +55,10 @@ class Gamma extends GammaBase
      * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distribution($value, $a, $b, $cumulative)
+    public static function distribution(mixed $value, mixed $a, mixed $b, mixed $cumulative)
     {
         if (is_array($value) || is_array($a) || is_array($b) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $a, $b, $cumulative);
@@ -93,11 +92,10 @@ class Gamma extends GammaBase
      * @param mixed $beta Parameter to the distribution as a float
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 inverse($probability, $alpha, $beta)
+    public static function inverse(mixed $probability, mixed $alpha, mixed $beta)
     {
         if (is_array($probability) || is_array($alpha) || is_array($beta)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta);
@@ -126,11 +124,10 @@ class Gamma extends GammaBase
      * @param mixed $value Float Value at which you want to evaluate the distribution
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 ln($value)
+    public static function ln(mixed $value): array|string|float
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
index 8e6386e..6ce99d8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php
@@ -17,7 +17,7 @@ abstract class GammaBase
 
     private const MAX_ITERATIONS = 256;
 
-    protected static function calculateDistribution(float $value, float $a, float $b, bool $cumulative)
+    protected static function calculateDistribution(float $value, float $a, float $b, bool $cumulative): float
     {
         if ($cumulative) {
             return self::incompleteGamma($a, $value / $b) / self::gammaValue($a);
@@ -26,6 +26,7 @@ abstract class GammaBase
         return (1 / ($b ** $a * self::gammaValue($a))) * $value ** ($a - 1) * exp(0 - ($value / $b));
     }
 
+    /** @return float|string */
     protected static function calculateInverse(float $probability, float $alpha, float $beta)
     {
         $xLo = 0;
@@ -38,6 +39,9 @@ abstract class GammaBase
         while ((abs($dx) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
             // Apply Newton-Raphson step
             $result = self::calculateDistribution($x, $alpha, $beta, true);
+            if (!is_float($result)) {
+                return ExcelError::NA();
+            }
             $error = $result - $probability;
 
             if ($error == 0.0) {
@@ -50,6 +54,9 @@ abstract class GammaBase
 
             $pdf = self::calculateDistribution($x, $alpha, $beta, false);
             // Avoid division by zero
+            if (!is_float($pdf)) {
+                return ExcelError::NA();
+            }
             if ($pdf !== 0.0) {
                 $dx = $error / $pdf;
                 $xNew = $x - $dx;
@@ -121,7 +128,7 @@ abstract class GammaBase
         return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x));
     }
 
-    private const  LG_D1 = -0.5772156649015328605195174;
+    private const LG_D1 = -0.5772156649015328605195174;
 
     private const LG_D2 = 0.4227843350984671393993777;
 
@@ -209,9 +216,10 @@ abstract class GammaBase
     private const PNT68 = 0.6796875;
 
     // Function cache for logGamma
-    private static $logGammaCacheResult = 0.0;
 
-    private static $logGammaCacheX = 0.0;
+    private static float $logGammaCacheResult = 0.0;
+
+    private static float $logGammaCacheX = 0.0;
 
     /**
      * logGamma function.
@@ -292,7 +300,7 @@ abstract class GammaBase
         return $res;
     }
 
-    private static function logGamma1(float $y)
+    private static function logGamma1(float $y): float
     {
         // ---------------------
         //    EPS .LT. X .LE. 1.5
@@ -325,7 +333,7 @@ abstract class GammaBase
         return $corr + $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
     }
 
-    private static function logGamma2(float $y)
+    private static function logGamma2(float $y): float
     {
         // ---------------------
         //    1.5 .LT. X .LE. 4.0
@@ -341,7 +349,7 @@ abstract class GammaBase
         return $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
     }
 
-    protected static function logGamma3(float $y)
+    protected static function logGamma3(float $y): float
     {
         // ----------------------
         //    4.0 .LT. X .LE. 12.0
@@ -357,7 +365,7 @@ abstract class GammaBase
         return self::LG_D4 + $xm4 * ($xnum / $xden);
     }
 
-    protected static function logGamma4(float $y)
+    protected static function logGamma4(float $y): float
     {
         // ---------------------------------
         //    Evaluate for argument .GE. 12.0
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
index b3ad69d..345ea81 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php
@@ -26,15 +26,14 @@ class HyperGeometric
      * @param mixed $populationNumber Integer population size
      *                      Or can be an array of values
      *
-     * @return array|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @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 distribution($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
+    public static function distribution(mixed $sampleSuccesses, mixed $sampleNumber, mixed $populationSuccesses, mixed $populationNumber): array|string|float
     {
         if (
-            is_array($sampleSuccesses) || is_array($sampleNumber) ||
-            is_array($populationSuccesses) || is_array($populationNumber)
+            is_array($sampleSuccesses) || is_array($sampleNumber)
+            || is_array($populationSuccesses) || is_array($populationNumber)
         ) {
             return self::evaluateArrayArguments(
                 [self::class, __FUNCTION__],
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
index d572d23..50f02e4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php
@@ -27,7 +27,7 @@ class LogNormal
      *         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 cumulative($value, $mean, $stdDev)
+    public static function cumulative(mixed $value, mixed $mean, mixed $stdDev)
     {
         if (is_array($value) || is_array($mean) || is_array($stdDev)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev);
@@ -67,7 +67,7 @@ class LogNormal
      *         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 distribution($value, $mean, $stdDev, $cumulative = false)
+    public static function distribution(mixed $value, mixed $mean, mixed $stdDev, mixed $cumulative = false)
     {
         if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative);
@@ -90,8 +90,8 @@ class LogNormal
             return StandardNormal::distribution((log($value) - $mean) / $stdDev, true);
         }
 
-        return (1 / (sqrt(2 * M_PI) * $stdDev * $value)) *
-            exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2)));
+        return (1 / (sqrt(2 * M_PI) * $stdDev * $value))
+            * exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2)));
     }
 
     /**
@@ -114,7 +114,7 @@ class LogNormal
      *            accuracy if I can get my head round the mathematics
      *            (as described at) http://home.online.no/~pjacklam/notes/invnorm/
      */
-    public static function inverse($probability, $mean, $stdDev)
+    public static function inverse(mixed $probability, mixed $mean, mixed $stdDev): array|string|float
     {
         if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
@@ -131,7 +131,7 @@ class LogNormal
         if ($stdDev <= 0) {
             return ExcelError::NAN();
         }
-        /** @var float */
+        /** @var float $inverse */
         $inverse = StandardNormal::inverse($probability);
 
         return exp($mean + $stdDev * $inverse);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
index b994864..647c0c4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php
@@ -9,6 +9,7 @@ class NewtonRaphson
 {
     private const MAX_ITERATIONS = 256;
 
+    /** @var callable */
     protected $callback;
 
     public function __construct(callable $callback)
@@ -16,7 +17,7 @@ class NewtonRaphson
         $this->callback = $callback;
     }
 
-    public function execute(float $probability)
+    public function execute(float $probability): string|int|float
     {
         $xLo = 100;
         $xHi = 0;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
index 67533c4..8d08d57 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php
@@ -33,7 +33,7 @@ class Normal
      *         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 distribution($value, $mean, $stdDev, $cumulative)
+    public static function distribution(mixed $value, mixed $mean, mixed $stdDev, mixed $cumulative): array|string|float
     {
         if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative);
@@ -75,7 +75,7 @@ class Normal
      *         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 inverse($probability, $mean, $stdDev)
+    public static function inverse(mixed $probability, mixed $mean, mixed $stdDev): array|string|float
     {
         if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
@@ -104,7 +104,7 @@ class Normal
      *    email                : nickersonm@yahoo.com
      *
      */
-    private static function inverseNcdf($p)
+    private static function inverseNcdf(float $p): float
     {
         //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
         //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
@@ -160,21 +160,21 @@ class Normal
             //    Rational approximation for lower region.
             $q = sqrt(-2 * log($p));
 
-            return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
-                (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+            return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6])
+                / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
         } elseif ($p_high < $p && $p < 1) {
             //    Rational approximation for upper region.
             $q = sqrt(-2 * log(1 - $p));
 
-            return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
-                (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
+            return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6])
+                / (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
         }
 
         //    Rational approximation for central region.
         $q = $p - 0.5;
         $r = $q * $q;
 
-        return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
-                ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
+        return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q
+                / ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
index 041c34a..931568e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
@@ -29,7 +29,7 @@ class Poisson
      *         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 distribution($value, $mean, $cumulative)
+    public static function distribution(mixed $value, mixed $mean, mixed $cumulative): array|string|float
     {
         if (is_array($value) || is_array($mean) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $cumulative);
@@ -51,14 +51,14 @@ class Poisson
             $summer = 0;
             $floor = floor($value);
             for ($i = 0; $i <= $floor; ++$i) {
-                /** @var float */
+                /** @var float $fact */
                 $fact = MathTrig\Factorial::fact($i);
                 $summer += $mean ** $i / $fact;
             }
 
             return exp(0 - $mean) * $summer;
         }
-        /** @var float */
+        /** @var float $fact */
         $fact = MathTrig\Factorial::fact($value);
 
         return (exp(0 - $mean) * $mean ** $value) / $fact;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
index a655fa7..cb2c646 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
@@ -30,7 +30,7 @@ class StandardNormal
      *         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 cumulative($value)
+    public static function cumulative(mixed $value)
     {
         return Normal::distribution($value, 0, 1, true);
     }
@@ -55,7 +55,7 @@ class StandardNormal
      *         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 distribution($value, $cumulative)
+    public static function distribution(mixed $value, mixed $cumulative)
     {
         return Normal::distribution($value, 0, 1, $cumulative);
     }
@@ -76,7 +76,7 @@ class StandardNormal
      *         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 inverse($value)
+    public static function inverse(mixed $value)
     {
         return Normal::inverse($value, 0, 1);
     }
@@ -87,14 +87,13 @@ class StandardNormal
      * Calculates the probability that a member of a standard normal population will fall between
      *     the mean and z standard deviations from the mean.
      *
-     * @param mixed $value
-     *                      Or can be an array of values
+     * @param mixed $value Or can be an array of values
      *
      * @return array|float|string The 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 gauss($value)
+    public static function gauss(mixed $value): array|string|float
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -103,7 +102,7 @@ class StandardNormal
         if (!is_numeric($value)) {
             return ExcelError::VALUE();
         }
-        /** @var float */
+        /** @var float $dist */
         $dist = self::distribution($value, true);
 
         return $dist - 0.5;
@@ -128,7 +127,7 @@ class StandardNormal
      *         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 zTest($dataSet, $m0, $sigma = null)
+    public static function zTest(mixed $dataSet, mixed $m0, mixed $sigma = null)
     {
         if (is_array($m0) || is_array($sigma)) {
             return self::evaluateArrayArgumentsSubsetFrom([self::class, __FUNCTION__], 1, $dataSet, $m0, $sigma);
@@ -141,11 +140,19 @@ class StandardNormal
         }
 
         if ($sigma === null) {
-            /** @var float */
+            /** @var float $sigma */
             $sigma = StandardDeviations::STDEV($dataSet);
         }
         $n = count($dataSet);
 
-        return 1 - self::cumulative((Averages::average($dataSet) - $m0) / ($sigma / sqrt($n)));
+        $sub1 = Averages::average($dataSet);
+
+        if (!is_numeric($sub1)) {
+            return $sub1;
+        }
+
+        $temp = self::cumulative(($sub1 - $m0) / ($sigma / sqrt($n)));
+
+        return 1 - (is_numeric($temp) ? $temp : 0);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php
index 8afc5c4..5ec6489 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php
@@ -11,8 +11,6 @@ class StudentT
 {
     use ArrayEnabled;
 
-    private const MAX_ITERATIONS = 256;
-
     /**
      * TDIST.
      *
@@ -29,7 +27,7 @@ class StudentT
      *         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 distribution($value, $degrees, $tails)
+    public static function distribution(mixed $value, mixed $degrees, mixed $tails)
     {
         if (is_array($value) || is_array($degrees) || is_array($tails)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees, $tails);
@@ -64,7 +62,7 @@ class StudentT
      *         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 inverse($probability, $degrees)
+    public static function inverse(mixed $probability, mixed $degrees)
     {
         if (is_array($probability) || is_array($degrees)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
@@ -81,19 +79,14 @@ class StudentT
             return ExcelError::NAN();
         }
 
-        $callback = function ($value) use ($degrees) {
-            return self::distribution($value, $degrees, 2);
-        };
+        $callback = fn ($value) => self::distribution($value, $degrees, 2);
 
         $newtonRaphson = new NewtonRaphson($callback);
 
         return $newtonRaphson->execute($probability);
     }
 
-    /**
-     * @return float
-     */
-    private static function calculateDistribution(float $value, int $degrees, int $tails)
+    private static function calculateDistribution(float $value, int $degrees, int $tails): float
     {
         //    tdist, which finds the probability that corresponds to a given value
         //    of t with k degrees of freedom. This algorithm is translated from a
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
index 51392c4..2f20b62 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
@@ -29,7 +29,7 @@ class Weibull
      *         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 distribution($value, $alpha, $beta, $cumulative)
+    public static function distribution(mixed $value, mixed $alpha, mixed $beta, mixed $cumulative): array|string|float
     {
         if (is_array($value) || is_array($alpha) || is_array($beta) || is_array($cumulative)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $alpha, $beta, $cumulative);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
index bd17b06..a722fbd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php
@@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
 
 abstract class MaxMinBase
 {
-    protected static function datatypeAdjustmentAllowStrings($value)
+    protected static function datatypeAdjustmentAllowStrings(int|float|string|bool $value): int|float
     {
         if (is_bool($value)) {
             return (int) $value;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Maximum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Maximum.php
index 3f8436f..47a98e6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Maximum.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Maximum.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
 
 class Maximum extends MaxMinBase
 {
@@ -16,16 +17,19 @@ class Maximum extends MaxMinBase
      *        MAX(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float
      */
-    public static function max(...$args)
+    public static function max(mixed ...$args): float|int|string
     {
         $returnValue = null;
 
         // Loop through arguments
         $aArgs = Functions::flattenArray($args);
         foreach ($aArgs as $arg) {
+            if (ErrorValue::isError($arg)) {
+                $returnValue = $arg;
+
+                break;
+            }
             // Is it a numeric value?
             if ((is_numeric($arg)) && (!is_string($arg))) {
                 if (($returnValue === null) || ($arg > $returnValue)) {
@@ -50,16 +54,19 @@ class Maximum extends MaxMinBase
      *        MAXA(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float
      */
-    public static function maxA(...$args)
+    public static function maxA(mixed ...$args): float|int|string
     {
         $returnValue = null;
 
         // Loop through arguments
         $aArgs = Functions::flattenArray($args);
         foreach ($aArgs as $arg) {
+            if (ErrorValue::isError($arg)) {
+                $returnValue = $arg;
+
+                break;
+            }
             // Is it a numeric value?
             if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
                 $arg = self::datatypeAdjustmentAllowStrings($arg);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
index 596aad7..fcb77c6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
 
 class Minimum extends MaxMinBase
 {
@@ -16,16 +17,19 @@ class Minimum extends MaxMinBase
      *        MIN(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float
      */
-    public static function min(...$args)
+    public static function min(mixed ...$args): float|int|string
     {
         $returnValue = null;
 
         // Loop through arguments
         $aArgs = Functions::flattenArray($args);
         foreach ($aArgs as $arg) {
+            if (ErrorValue::isError($arg)) {
+                $returnValue = $arg;
+
+                break;
+            }
             // Is it a numeric value?
             if ((is_numeric($arg)) && (!is_string($arg))) {
                 if (($returnValue === null) || ($arg < $returnValue)) {
@@ -50,16 +54,19 @@ class Minimum extends MaxMinBase
      *        MINA(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float
      */
-    public static function minA(...$args)
+    public static function minA(mixed ...$args): float|int|string
     {
         $returnValue = null;
 
         // Loop through arguments
         $aArgs = Functions::flattenArray($args);
         foreach ($aArgs as $arg) {
+            if (ErrorValue::isError($arg)) {
+                $returnValue = $arg;
+
+                break;
+            }
             // Is it a numeric value?
             if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
                 $arg = self::datatypeAdjustmentAllowStrings($arg);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
index 9973823..e274f9c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php
@@ -24,7 +24,7 @@ class Percentiles
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function PERCENTILE(...$args)
+    public static function PERCENTILE(mixed ...$args)
     {
         $aArgs = Functions::flattenArray($args);
 
@@ -74,7 +74,7 @@ class Percentiles
      *
      * @return float|string (string if result is an error)
      */
-    public static function PERCENTRANK($valueSet, $value, $significance = 3)
+    public static function PERCENTRANK(mixed $valueSet, mixed $value, mixed $significance = 3): string|float
     {
         $valueSet = Functions::flattenArray($valueSet);
         $value = Functions::flattenSingleValue($value);
@@ -110,7 +110,7 @@ class Percentiles
             $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
         }
 
-        return round($pos / $valueAdjustor, $significance);
+        return round(((float) $pos) / $valueAdjustor, $significance);
     }
 
     /**
@@ -125,7 +125,7 @@ class Percentiles
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function QUARTILE(...$args)
+    public static function QUARTILE(mixed ...$args)
     {
         $aArgs = Functions::flattenArray($args);
         $entry = array_pop($aArgs);
@@ -156,7 +156,7 @@ class Percentiles
      *
      * @return float|string The result, or a string containing an error (0 = Descending, 1 = Ascending)
      */
-    public static function RANK($value, $valueSet, $order = self::RANK_SORT_DESCENDING)
+    public static function RANK(mixed $value, mixed $valueSet, mixed $order = self::RANK_SORT_DESCENDING)
     {
         $value = Functions::flattenSingleValue($value);
         $valueSet = Functions::flattenArray($valueSet);
@@ -184,23 +184,19 @@ class Percentiles
         return ++$pos;
     }
 
-    protected static function percentileFilterValues(array $dataSet)
+    protected static function percentileFilterValues(array $dataSet): array
     {
         return array_filter(
             $dataSet,
-            function ($value): bool {
-                return is_numeric($value) && !is_string($value);
-            }
+            fn ($value): bool => is_numeric($value) && !is_string($value)
         );
     }
 
-    protected static function rankFilterValues(array $dataSet)
+    protected static function rankFilterValues(array $dataSet): array
     {
         return array_filter(
             $dataSet,
-            function ($value): bool {
-                return is_numeric($value);
-            }
+            fn ($value): bool => is_numeric($value)
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
index 5d9d304..06e3b79 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
@@ -30,7 +30,7 @@ class Permutations
      *         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 PERMUT($numObjs, $numInSet)
+    public static function PERMUT(mixed $numObjs, mixed $numInSet)
     {
         if (is_array($numObjs) || is_array($numInSet)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
@@ -46,7 +46,17 @@ class Permutations
         if ($numObjs < $numInSet) {
             return ExcelError::NAN();
         }
-        $result = round(MathTrig\Factorial::fact($numObjs) / MathTrig\Factorial::fact($numObjs - $numInSet));
+        /** @var float|int|string */
+        $result1 = MathTrig\Factorial::fact($numObjs);
+        if (is_string($result1)) {
+            return $result1;
+        }
+        /** @var float|int|string */
+        $result2 = MathTrig\Factorial::fact($numObjs - $numInSet);
+        if (is_string($result2)) {
+            return $result2;
+        }
+        $result = round($result1 / $result2);
 
         return IntOrFloat::evaluate($result);
     }
@@ -66,7 +76,7 @@ class Permutations
      *         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 PERMUTATIONA($numObjs, $numInSet)
+    public static function PERMUTATIONA(mixed $numObjs, mixed $numInSet)
     {
         if (is_array($numObjs) || is_array($numInSet)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
index 2eef5fc..71594bd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
@@ -20,7 +20,7 @@ class Size
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function large(...$args)
+    public static function large(mixed ...$args)
     {
         $aArgs = Functions::flattenArray($args);
         $entry = array_pop($aArgs);
@@ -55,7 +55,7 @@ class Size
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function small(...$args)
+    public static function small(mixed ...$args)
     {
         $aArgs = Functions::flattenArray($args);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
index af27120..3d4ea30 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
@@ -17,7 +17,7 @@ class StandardDeviations
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function STDEV(...$args)
+    public static function STDEV(mixed ...$args)
     {
         $result = Variances::VAR(...$args);
         if (!is_numeric($result)) {
@@ -36,10 +36,8 @@ class StandardDeviations
      *        STDEVA(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function STDEVA(...$args)
+    public static function STDEVA(mixed ...$args): float|string
     {
         $result = Variances::VARA(...$args);
         if (!is_numeric($result)) {
@@ -58,10 +56,8 @@ class StandardDeviations
      *        STDEVP(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function STDEVP(...$args)
+    public static function STDEVP(mixed ...$args): float|string
     {
         $result = Variances::VARP(...$args);
         if (!is_numeric($result)) {
@@ -80,10 +76,8 @@ class StandardDeviations
      *        STDEVPA(value1[,value2[, ...]])
      *
      * @param mixed ...$args Data values
-     *
-     * @return float|string
      */
-    public static function STDEVPA(...$args)
+    public static function STDEVPA(mixed ...$args): float|string
     {
         $result = Variances::VARPA(...$args);
         if (!is_numeric($result)) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php
index 51b0a51..b8e7350 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php
@@ -26,7 +26,7 @@ class Standardize extends StatisticalValidations
      *         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 execute($value, $mean, $stdDev)
+    public static function execute($value, $mean, $stdDev): array|string|float
     {
         if (is_array($value) || is_array($mean) || is_array($stdDev)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
index e23a52c..59ad7ee 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
@@ -7,10 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
 class StatisticalValidations
 {
-    /**
-     * @param mixed $value
-     */
-    public static function validateFloat($value): float
+    public static function validateFloat(mixed $value): float
     {
         if (!is_numeric($value)) {
             throw new Exception(ExcelError::VALUE());
@@ -19,10 +16,7 @@ class StatisticalValidations
         return (float) $value;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function validateInt($value): int
+    public static function validateInt(mixed $value): int
     {
         if (!is_numeric($value)) {
             throw new Exception(ExcelError::VALUE());
@@ -31,10 +25,7 @@ class StatisticalValidations
         return (int) floor((float) $value);
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function validateBool($value): bool
+    public static function validateBool(mixed $value): bool
     {
         if (!is_bool($value) && !is_numeric($value)) {
             throw new Exception(ExcelError::VALUE());
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php
index af73519..365001f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php
@@ -21,7 +21,11 @@ class Trends
         }
     }
 
-    private static function checkTrendArrays(&$array1, &$array2): void
+    /**
+     * @param mixed $array1 should be array, but scalar is made into one
+     * @param mixed $array2 should be array, but scalar is made into one
+     */
+    private static function checkTrendArrays(mixed &$array1, mixed &$array2): void
     {
         if (!is_array($array1)) {
             $array1 = [$array1];
@@ -60,10 +64,8 @@ class Trends
      *
      * @param mixed $yValues array of mixed Data Series Y
      * @param null|mixed $xValues array of mixed Data Series X
-     *
-     * @return float|string
      */
-    public static function CORREL($yValues, $xValues = null)
+    public static function CORREL(mixed $yValues, $xValues = null): float|string
     {
         if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
             return ExcelError::VALUE();
@@ -86,12 +88,10 @@ class Trends
      *
      * Returns covariance, the average of the products of deviations for each data point pair.
      *
-     * @param mixed $yValues array of mixed Data Series Y
-     * @param mixed $xValues array of mixed Data Series X
-     *
-     * @return float|string
+     * @param mixed[] $yValues array of mixed Data Series Y
+     * @param mixed[] $xValues array of mixed Data Series X
      */
-    public static function COVAR($yValues, $xValues)
+    public static function COVAR(array $yValues, array $xValues): float|string
     {
         try {
             self::checkTrendArrays($yValues, $xValues);
@@ -113,14 +113,13 @@ class Trends
      *
      * @param mixed $xValue Float value of X for which we want to find Y
      *                      Or can be an array of values
-     * @param mixed $yValues array of mixed Data Series Y
-     * @param mixed $xValues of mixed Data Series X
+     * @param mixed[] $yValues array of mixed Data Series Y
+     * @param mixed[] $xValues array of mixed Data Series X
      *
-     * @return array|bool|float|string
-     *         If an array of numbers is passed as an argument, then the returned result will also be an array
+     * @return array|bool|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 FORECAST($xValue, $yValues, $xValues)
+    public static function FORECAST(mixed $xValue, array $yValues, array $xValues)
     {
         if (is_array($xValue)) {
             return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $xValue, $yValues, $xValues);
@@ -149,9 +148,9 @@ class Trends
      * @param mixed[] $newValues Values of X for which we want to find Y
      * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
      *
-     * @return float[]
+     * @return array<int, array<int, array<int, float>>>
      */
-    public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
+    public static function GROWTH(array $yValues, array $xValues = [], array $newValues = [], mixed $const = true): array
     {
         $yValues = Functions::flattenArray($yValues);
         $xValues = Functions::flattenArray($xValues);
@@ -178,10 +177,8 @@ class Trends
      *
      * @param mixed[] $yValues Data Series Y
      * @param mixed[] $xValues Data Series X
-     *
-     * @return float|string
      */
-    public static function INTERCEPT($yValues, $xValues)
+    public static function INTERCEPT(array $yValues, array $xValues): float|string
     {
         try {
             self::checkTrendArrays($yValues, $xValues);
@@ -206,9 +203,9 @@ class Trends
      * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
      * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
      *
-     * @return array|int|string The result, or a string containing an error
+     * @return array|string The result, or a string containing an error
      */
-    public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
+    public static function LINEST(array $yValues, ?array $xValues = null, mixed $const = true, mixed $stats = false): string|array
     {
         $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
         $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
@@ -267,9 +264,9 @@ class Trends
      * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
      * @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
      *
-     * @return array|int|string The result, or a string containing an error
+     * @return array|string The result, or a string containing an error
      */
-    public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
+    public static function LOGEST(array $yValues, ?array $xValues = null, mixed $const = true, mixed $stats = false): string|array
     {
         $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
         $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
@@ -334,7 +331,7 @@ class Trends
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function RSQ($yValues, $xValues)
+    public static function RSQ(array $yValues, array $xValues)
     {
         try {
             self::checkTrendArrays($yValues, $xValues);
@@ -358,7 +355,7 @@ class Trends
      *
      * @return float|string The result, or a string containing an error
      */
-    public static function SLOPE($yValues, $xValues)
+    public static function SLOPE(array $yValues, array $xValues)
     {
         try {
             self::checkTrendArrays($yValues, $xValues);
@@ -379,10 +376,8 @@ class Trends
      *
      * @param mixed[] $yValues Data Series Y
      * @param mixed[] $xValues Data Series X
-     *
-     * @return float|string
      */
-    public static function STEYX($yValues, $xValues)
+    public static function STEYX(array $yValues, array $xValues): float|string
     {
         try {
             self::checkTrendArrays($yValues, $xValues);
@@ -406,9 +401,9 @@ class Trends
      * @param mixed[] $newValues Values of X for which we want to find Y
      * @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
      *
-     * @return float[]
+     * @return array<int, array<int, array<int, float>>>
      */
-    public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
+    public static function TREND(array $yValues, array $xValues = [], array $newValues = [], mixed $const = true): array
     {
         $yValues = Functions::flattenArray($yValues);
         $xValues = Functions::flattenArray($xValues);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
index e533467..d5646cf 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
@@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 
 abstract class VarianceBase
 {
-    protected static function datatypeAdjustmentAllowStrings($value)
+    protected static function datatypeAdjustmentAllowStrings(int|float|string|bool $value): int|float
     {
         if (is_bool($value)) {
             return (int) $value;
@@ -17,7 +17,7 @@ abstract class VarianceBase
         return $value;
     }
 
-    protected static function datatypeAdjustmentBooleans($value)
+    protected static function datatypeAdjustmentBooleans(mixed $value): mixed
     {
         if (is_bool($value) && (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE)) {
             return (int) $value;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php
index 938e671..3256e85 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php
@@ -19,7 +19,7 @@ class Variances extends VarianceBase
      *
      * @return float|string (string if result is an error)
      */
-    public static function VAR(...$args)
+    public static function VAR(mixed ...$args): float|string
     {
         $returnValue = ExcelError::DIV0();
 
@@ -61,7 +61,7 @@ class Variances extends VarianceBase
      *
      * @return float|string (string if result is an error)
      */
-    public static function VARA(...$args)
+    public static function VARA(mixed ...$args): string|float
     {
         $returnValue = ExcelError::DIV0();
 
@@ -107,7 +107,7 @@ class Variances extends VarianceBase
      *
      * @return float|string (string if result is an error)
      */
-    public static function VARP(...$args)
+    public static function VARP(mixed ...$args): float|string
     {
         // Return value
         $returnValue = ExcelError::DIV0();
@@ -150,7 +150,7 @@ class Variances extends VarianceBase
      *
      * @return float|string (string if result is an error)
      */
-    public static function VARPA(...$args)
+    public static function VARPA(mixed ...$args): string|float
     {
         $returnValue = ExcelError::DIV0();
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
deleted file mode 100644
index 97f4629..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
+++ /dev/null
@@ -1,448 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-use DateTimeInterface;
-
-/**
- * @deprecated 1.18.0
- */
-class TextData
-{
-    /**
-     * CHARACTER.
-     *
-     * @deprecated 1.18.0
-     *      Use the character() method in the TextData\CharacterConvert class instead
-     * @see TextData\CharacterConvert::character()
-     *
-     * @param string $character Value
-     *
-     * @return array|string
-     */
-    public static function CHARACTER($character)
-    {
-        return TextData\CharacterConvert::character($character);
-    }
-
-    /**
-     * TRIMNONPRINTABLE.
-     *
-     * @deprecated 1.18.0
-     *      Use the nonPrintable() method in the TextData\Trim class instead
-     * @see TextData\Trim::nonPrintable()
-     *
-     * @param mixed $stringValue Value to check
-     *
-     * @return null|array|string
-     */
-    public static function TRIMNONPRINTABLE($stringValue = '')
-    {
-        return TextData\Trim::nonPrintable($stringValue);
-    }
-
-    /**
-     * TRIMSPACES.
-     *
-     * @deprecated 1.18.0
-     *      Use the spaces() method in the TextData\Trim class instead
-     * @see TextData\Trim::spaces()
-     *
-     * @param mixed $stringValue Value to check
-     *
-     * @return array|string
-     */
-    public static function TRIMSPACES($stringValue = '')
-    {
-        return TextData\Trim::spaces($stringValue);
-    }
-
-    /**
-     * ASCIICODE.
-     *
-     * @deprecated 1.18.0
-     *      Use the code() method in the TextData\CharacterConvert class instead
-     * @see TextData\CharacterConvert::code()
-     *
-     * @param array|string $characters Value
-     *
-     * @return array|int|string A string if arguments are invalid
-     */
-    public static function ASCIICODE($characters)
-    {
-        return TextData\CharacterConvert::code($characters);
-    }
-
-    /**
-     * CONCATENATE.
-     *
-     * @deprecated 1.18.0
-     *      Use the CONCATENATE() method in the TextData\Concatenate class instead
-     * @see TextData\Concatenate::CONCATENATE()
-     *
-     * @return string
-     */
-    public static function CONCATENATE(...$args)
-    {
-        return TextData\Concatenate::CONCATENATE(...$args);
-    }
-
-    /**
-     * 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)..
-     *
-     * @deprecated 1.18.0
-     *      Use the DOLLAR() method in the TextData\Format class instead
-     * @see TextData\Format::DOLLAR()
-     *
-     * @param float $value The value to format
-     * @param int $decimals The number of digits to display to the right of the decimal point.
-     *                                    If decimals is negative, number is rounded to the left of the decimal point.
-     *                                    If you omit decimals, it is assumed to be 2
-     *
-     * @return array|string
-     */
-    public static function DOLLAR($value = 0, $decimals = 2)
-    {
-        return TextData\Format::DOLLAR($value, $decimals);
-    }
-
-    /**
-     * FIND.
-     *
-     * @deprecated 1.18.0
-     *      Use the sensitive() method in the TextData\Search class instead
-     * @see TextData\Search::sensitive()
-     *
-     * @param array|string $needle The string to look for
-     * @param array|string $haystack The string in which to look
-     * @param array|int $offset Offset within $haystack
-     *
-     * @return array|int|string
-     */
-    public static function SEARCHSENSITIVE($needle, $haystack, $offset = 1)
-    {
-        return TextData\Search::sensitive($needle, $haystack, $offset);
-    }
-
-    /**
-     * SEARCH.
-     *
-     * @deprecated 1.18.0
-     *      Use the insensitive() method in the TextData\Search class instead
-     * @see TextData\Search::insensitive()
-     *
-     * @param array|string $needle The string to look for
-     * @param array|string $haystack The string in which to look
-     * @param array|int $offset Offset within $haystack
-     *
-     * @return array|int|string
-     */
-    public static function SEARCHINSENSITIVE($needle, $haystack, $offset = 1)
-    {
-        return TextData\Search::insensitive($needle, $haystack, $offset);
-    }
-
-    /**
-     * FIXEDFORMAT.
-     *
-     * @deprecated 1.18.0
-     *      Use the FIXEDFORMAT() method in the TextData\Format class instead
-     * @see TextData\Format::FIXEDFORMAT()
-     *
-     * @param mixed $value Value to check
-     * @param int $decimals
-     * @param bool $no_commas
-     *
-     * @return array|string
-     */
-    public static function FIXEDFORMAT($value, $decimals = 2, $no_commas = false)
-    {
-        return TextData\Format::FIXEDFORMAT($value, $decimals, $no_commas);
-    }
-
-    /**
-     * LEFT.
-     *
-     * @deprecated 1.18.0
-     *      Use the left() method in the TextData\Extract class instead
-     * @see TextData\Extract::left()
-     *
-     * @param array|string $value Value
-     * @param array|int $chars Number of characters
-     *
-     * @return array|string
-     */
-    public static function LEFT($value = '', $chars = 1)
-    {
-        return TextData\Extract::left($value, $chars);
-    }
-
-    /**
-     * MID.
-     *
-     * @deprecated 1.18.0
-     *      Use the mid() method in the TextData\Extract class instead
-     * @see TextData\Extract::mid()
-     *
-     * @param array|string $value Value
-     * @param array|int $start Start character
-     * @param array|int $chars Number of characters
-     *
-     * @return array|string
-     */
-    public static function MID($value = '', $start = 1, $chars = null)
-    {
-        return TextData\Extract::mid($value, $start, $chars);
-    }
-
-    /**
-     * RIGHT.
-     *
-     * @deprecated 1.18.0
-     *      Use the right() method in the TextData\Extract class instead
-     * @see TextData\Extract::right()
-     *
-     * @param array|string $value Value
-     * @param array|int $chars Number of characters
-     *
-     * @return array|string
-     */
-    public static function RIGHT($value = '', $chars = 1)
-    {
-        return TextData\Extract::right($value, $chars);
-    }
-
-    /**
-     * STRINGLENGTH.
-     *
-     * @deprecated 1.18.0
-     *      Use the length() method in the TextData\Text class instead
-     * @see TextData\Text::length()
-     *
-     * @param string $value Value
-     *
-     * @return array|int
-     */
-    public static function STRINGLENGTH($value = '')
-    {
-        return TextData\Text::length($value);
-    }
-
-    /**
-     * LOWERCASE.
-     *
-     * Converts a string value to lower case.
-     *
-     * @deprecated 1.18.0
-     *      Use the lower() method in the TextData\CaseConvert class instead
-     * @see TextData\CaseConvert::lower()
-     *
-     * @param array|string $mixedCaseString
-     *
-     * @return array|string
-     */
-    public static function LOWERCASE($mixedCaseString)
-    {
-        return TextData\CaseConvert::lower($mixedCaseString);
-    }
-
-    /**
-     * UPPERCASE.
-     *
-     * Converts a string value to upper case.
-     *
-     * @deprecated 1.18.0
-     *      Use the upper() method in the TextData\CaseConvert class instead
-     * @see TextData\CaseConvert::upper()
-     *
-     * @param string $mixedCaseString
-     *
-     * @return array|string
-     */
-    public static function UPPERCASE($mixedCaseString)
-    {
-        return TextData\CaseConvert::upper($mixedCaseString);
-    }
-
-    /**
-     * PROPERCASE.
-     *
-     * Converts a string value to proper/title case.
-     *
-     * @deprecated 1.18.0
-     *      Use the proper() method in the TextData\CaseConvert class instead
-     * @see TextData\CaseConvert::proper()
-     *
-     * @param array|string $mixedCaseString
-     *
-     * @return array|string
-     */
-    public static function PROPERCASE($mixedCaseString)
-    {
-        return TextData\CaseConvert::proper($mixedCaseString);
-    }
-
-    /**
-     * REPLACE.
-     *
-     * @deprecated 1.18.0
-     *      Use the replace() method in the TextData\Replace class instead
-     * @see TextData\Replace::replace()
-     *
-     * @param string $oldText String to modify
-     * @param int $start Start character
-     * @param int $chars Number of characters
-     * @param string $newText String to replace in defined position
-     *
-     * @return array|string
-     */
-    public static function REPLACE($oldText, $start, $chars, $newText)
-    {
-        return TextData\Replace::replace($oldText, $start, $chars, $newText);
-    }
-
-    /**
-     * SUBSTITUTE.
-     *
-     * @deprecated 1.18.0
-     *      Use the substitute() method in the TextData\Replace class instead
-     * @see TextData\Replace::substitute()
-     *
-     * @param string $text Value
-     * @param string $fromText From Value
-     * @param string $toText To Value
-     * @param int $instance Instance Number
-     *
-     * @return array|string
-     */
-    public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0)
-    {
-        return TextData\Replace::substitute($text, $fromText, $toText, $instance);
-    }
-
-    /**
-     * RETURNSTRING.
-     *
-     * @deprecated 1.18.0
-     *      Use the test() method in the TextData\Text class instead
-     * @see TextData\Text::test()
-     *
-     * @param mixed $testValue Value to check
-     *
-     * @return null|array|string
-     */
-    public static function RETURNSTRING($testValue = '')
-    {
-        return TextData\Text::test($testValue);
-    }
-
-    /**
-     * TEXTFORMAT.
-     *
-     * @deprecated 1.18.0
-     *      Use the TEXTFORMAT() method in the TextData\Format class instead
-     * @see TextData\Format::TEXTFORMAT()
-     *
-     * @param mixed $value Value to check
-     * @param string $format Format mask to use
-     *
-     * @return array|string
-     */
-    public static function TEXTFORMAT($value, $format)
-    {
-        return TextData\Format::TEXTFORMAT($value, $format);
-    }
-
-    /**
-     * VALUE.
-     *
-     * @deprecated 1.18.0
-     *      Use the VALUE() method in the TextData\Format class instead
-     * @see TextData\Format::VALUE()
-     *
-     * @param mixed $value Value to check
-     *
-     * @return array|DateTimeInterface|float|int|string A string if arguments are invalid
-     */
-    public static function VALUE($value = '')
-    {
-        return TextData\Format::VALUE($value);
-    }
-
-    /**
-     * NUMBERVALUE.
-     *
-     * @deprecated 1.18.0
-     *      Use the NUMBERVALUE() method in the TextData\Format class instead
-     * @see TextData\Format::NUMBERVALUE()
-     *
-     * @param mixed $value Value to check
-     * @param string $decimalSeparator decimal separator, defaults to locale defined value
-     * @param string $groupSeparator group/thosands separator, defaults to locale defined value
-     *
-     * @return array|float|string
-     */
-    public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
-    {
-        return TextData\Format::NUMBERVALUE($value, $decimalSeparator, $groupSeparator);
-    }
-
-    /**
-     * Compares two text strings and returns TRUE if they are exactly the same, FALSE otherwise.
-     * EXACT is case-sensitive but ignores formatting differences.
-     * Use EXACT to test text being entered into a document.
-     *
-     * @deprecated 1.18.0
-     *      Use the exact() method in the TextData\Text class instead
-     * @see TextData\Text::exact()
-     *
-     * @param mixed $value1
-     * @param mixed $value2
-     *
-     * @return array|bool
-     */
-    public static function EXACT($value1, $value2)
-    {
-        return TextData\Text::exact($value1, $value2);
-    }
-
-    /**
-     * TEXTJOIN.
-     *
-     * @deprecated 1.18.0
-     *      Use the TEXTJOIN() method in the TextData\Concatenate class instead
-     * @see TextData\Concatenate::TEXTJOIN()
-     *
-     * @param mixed $delimiter
-     * @param mixed $ignoreEmpty
-     * @param mixed $args
-     *
-     * @return array|string
-     */
-    public static function TEXTJOIN($delimiter, $ignoreEmpty, ...$args)
-    {
-        return TextData\Concatenate::TEXTJOIN($delimiter, $ignoreEmpty, ...$args);
-    }
-
-    /**
-     * REPT.
-     *
-     * Returns the result of builtin function repeat after validating args.
-     *
-     * @deprecated 1.18.0
-     *      Use the builtinREPT() method in the TextData\Concatenate class instead
-     * @see TextData\Concatenate::builtinREPT()
-     *
-     * @param array|string $str Should be numeric
-     * @param mixed $number Should be int
-     *
-     * @return array|string
-     */
-    public static function builtinREPT($str, $number)
-    {
-        return TextData\Concatenate::builtinREPT($str, $number);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
index f1aea16..6667bac 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
 
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 
 class CaseConvert
@@ -17,17 +18,20 @@ class CaseConvert
      * @param mixed $mixedCaseValue The string value to convert to lower case
      *                              Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed as the argument, then the returned result will also be an array
+     * @return array|string 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 lower($mixedCaseValue)
+    public static function lower(mixed $mixedCaseValue): array|string
     {
         if (is_array($mixedCaseValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $mixedCaseValue);
         }
 
-        $mixedCaseValue = Helpers::extractString($mixedCaseValue);
+        try {
+            $mixedCaseValue = Helpers::extractString($mixedCaseValue, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
 
         return StringHelper::strToLower($mixedCaseValue);
     }
@@ -40,17 +44,20 @@ class CaseConvert
      * @param mixed $mixedCaseValue The string value to convert to upper case
      *                              Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed as the argument, then the returned result will also be an array
+     * @return array|string 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 upper($mixedCaseValue)
+    public static function upper(mixed $mixedCaseValue): array|string
     {
         if (is_array($mixedCaseValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $mixedCaseValue);
         }
 
-        $mixedCaseValue = Helpers::extractString($mixedCaseValue);
+        try {
+            $mixedCaseValue = Helpers::extractString($mixedCaseValue, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
 
         return StringHelper::strToUpper($mixedCaseValue);
     }
@@ -63,17 +70,20 @@ class CaseConvert
      * @param mixed $mixedCaseValue The string value to convert to title case
      *                              Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed as the argument, then the returned result will also be an array
+     * @return array|string 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 proper($mixedCaseValue)
+    public static function proper(mixed $mixedCaseValue): array|string
     {
         if (is_array($mixedCaseValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $mixedCaseValue);
         }
 
-        $mixedCaseValue = Helpers::extractString($mixedCaseValue);
+        try {
+            $mixedCaseValue = Helpers::extractString($mixedCaseValue, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
 
         return StringHelper::strToTitle($mixedCaseValue);
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php
index 83af499..06d0f90 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
 
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 
@@ -20,13 +21,18 @@ class CharacterConvert
      *         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 character($character)
+    public static function character(mixed $character): array|string
     {
         if (is_array($character)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $character);
         }
 
-        $character = Helpers::validateInt($character);
+        try {
+            $character = Helpers::validateInt($character, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
+
         $min = Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE ? 0 : 1;
         if ($character < $min || $character > 255) {
             return ExcelError::VALUE();
@@ -46,13 +52,18 @@ class CharacterConvert
      *         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 code($characters)
+    public static function code(mixed $characters): array|string|int
     {
         if (is_array($characters)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $characters);
         }
 
-        $characters = Helpers::extractString($characters);
+        try {
+            $characters = Helpers::extractString($characters, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
+
         if ($characters === '') {
             return ExcelError::VALUE();
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
index 37c135d..78940ed 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php
@@ -27,7 +27,7 @@ class Concatenate
 
         foreach ($aArgs as $arg) {
             $value = Helpers::extractString($arg);
-            if (ErrorValue::isError($value)) {
+            if (ErrorValue::isError($value, true)) {
                 $returnValue = $value;
 
                 break;
@@ -56,7 +56,7 @@ class Concatenate
      *         If an array of values is passed for the $delimiter or $ignoreEmpty arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function TEXTJOIN($delimiter = '', $ignoreEmpty = true, ...$args)
+    public static function TEXTJOIN(mixed $delimiter = '', mixed $ignoreEmpty = true, mixed ...$args): array|string
     {
         if (is_array($delimiter) || is_array($ignoreEmpty)) {
             return self::evaluateArrayArgumentsSubset(
@@ -85,7 +85,7 @@ class Concatenate
     {
         foreach ($aArgs as $key => &$arg) {
             $value = Helpers::extractString($arg);
-            if (ErrorValue::isError($value)) {
+            if (ErrorValue::isError($value, true)) {
                 return $value;
             }
 
@@ -113,7 +113,7 @@ class Concatenate
      *         If an array of values is passed for the $stringValue or $repeatCount arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function builtinREPT($stringValue, $repeatCount)
+    public static function builtinREPT(mixed $stringValue, mixed $repeatCount): array|string
     {
         if (is_array($stringValue) || is_array($repeatCount)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $stringValue, $repeatCount);
@@ -123,7 +123,7 @@ class Concatenate
 
         if (!is_numeric($repeatCount) || $repeatCount < 0) {
             $returnValue = ExcelError::VALUE();
-        } elseif (ErrorValue::isError($stringValue)) {
+        } elseif (ErrorValue::isError($stringValue, true)) {
             $returnValue = $stringValue;
         } else {
             $returnValue = str_repeat($stringValue, (int) $repeatCount);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
index d3668f8..1dfb724 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Extract.php
@@ -24,20 +24,20 @@ class Extract
      *         If an array of values is passed for the $value or $chars arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function left($value, $chars = 1)
+    public static function left(mixed $value, mixed $chars = 1): array|string
     {
         if (is_array($value) || is_array($chars)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $chars);
         }
 
         try {
-            $value = Helpers::extractString($value);
+            $value = Helpers::extractString($value, true);
             $chars = Helpers::extractInt($chars, 0, 1);
         } catch (CalcExp $e) {
             return $e->getMessage();
         }
 
-        return mb_substr($value ?? '', 0, $chars, 'UTF-8');
+        return mb_substr($value, 0, $chars, 'UTF-8');
     }
 
     /**
@@ -54,21 +54,21 @@ class Extract
      *         If an array of values is passed for the $value, $start or $chars arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function mid($value, $start, $chars)
+    public static function mid(mixed $value, mixed $start, mixed $chars): array|string
     {
         if (is_array($value) || is_array($start) || is_array($chars)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $start, $chars);
         }
 
         try {
-            $value = Helpers::extractString($value);
+            $value = Helpers::extractString($value, true);
             $start = Helpers::extractInt($start, 1);
             $chars = Helpers::extractInt($chars, 0);
         } catch (CalcExp $e) {
             return $e->getMessage();
         }
 
-        return mb_substr($value ?? '', --$start, $chars, 'UTF-8');
+        return mb_substr($value, --$start, $chars, 'UTF-8');
     }
 
     /**
@@ -83,20 +83,20 @@ class Extract
      *         If an array of values is passed for the $value or $chars arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function right($value, $chars = 1)
+    public static function right(mixed $value, mixed $chars = 1): array|string
     {
         if (is_array($value) || is_array($chars)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $chars);
         }
 
         try {
-            $value = Helpers::extractString($value);
+            $value = Helpers::extractString($value, true);
             $chars = Helpers::extractInt($chars, 0, 1);
         } catch (CalcExp $e) {
             return $e->getMessage();
         }
 
-        return mb_substr($value ?? '', mb_strlen($value ?? '', 'UTF-8') - $chars, $chars, 'UTF-8');
+        return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
     }
 
     /**
@@ -122,17 +122,23 @@ class Extract
      *                             The default is a #N/A Error
      *                          Or can be an array of values
      *
-     * @return mixed|mixed[] the string extracted from text before the delimiter; or the $ifNotFound value
+     * @return array|string the string extracted from text before the delimiter; or the $ifNotFound value
      *         If an array of values is passed for any of the arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function before($text, $delimiter, $instance = 1, $matchMode = 0, $matchEnd = 0, $ifNotFound = '#N/A')
+    public static function before(mixed $text, $delimiter, mixed $instance = 1, mixed $matchMode = 0, mixed $matchEnd = 0, mixed $ifNotFound = '#N/A'): array|string
     {
         if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
             return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
         }
 
-        $text = Helpers::extractString($text ?? '');
+        try {
+            $text = Helpers::extractString($text ?? '', true);
+            Helpers::extractString(Functions::flattenSingleValue($delimiter ?? ''), true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
+
         $instance = (int) $instance;
         $matchMode = (int) $matchMode;
         $matchEnd = (int) $matchEnd;
@@ -180,17 +186,23 @@ class Extract
      *                             The default is a #N/A Error
      *                          Or can be an array of values
      *
-     * @return mixed|mixed[] the string extracted from text before the delimiter; or the $ifNotFound value
+     * @return array|string the string extracted from text before the delimiter; or the $ifNotFound value
      *         If an array of values is passed for any of the arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function after($text, $delimiter, $instance = 1, $matchMode = 0, $matchEnd = 0, $ifNotFound = '#N/A')
+    public static function after(mixed $text, $delimiter, mixed $instance = 1, mixed $matchMode = 0, mixed $matchEnd = 0, mixed $ifNotFound = '#N/A'): array|string
     {
         if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
             return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
         }
 
-        $text = Helpers::extractString($text ?? '');
+        try {
+            $text = Helpers::extractString($text ?? '', true);
+            Helpers::extractString(Functions::flattenSingleValue($delimiter ?? ''), true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
+
         $instance = (int) $instance;
         $matchMode = (int) $matchMode;
         $matchEnd = (int) $matchEnd;
@@ -216,15 +228,7 @@ class Extract
         return implode('', $split);
     }
 
-    /**
-     * @param null|array|string $delimiter
-     * @param int $matchMode
-     * @param int $matchEnd
-     * @param mixed $ifNotFound
-     *
-     * @return array|string
-     */
-    private static function validateTextBeforeAfter(string $text, $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound)
+    private static function validateTextBeforeAfter(string $text, null|array|string $delimiter, int $instance, int $matchMode, int $matchEnd, mixed $ifNotFound): array|string
     {
         $flags = self::matchFlags($matchMode);
         $delimiter = self::buildDelimiter($delimiter);
@@ -260,9 +264,7 @@ class Extract
         if (is_array($delimiter)) {
             $delimiter = Functions::flattenArray($delimiter);
             $quotedDelimiters = array_map(
-                function ($delimiter) {
-                    return preg_quote($delimiter ?? '');
-                },
+                fn ($delimiter): string => preg_quote($delimiter ?? '', '/'),
                 $delimiter
             );
             $delimiters = implode('|', $quotedDelimiters);
@@ -270,7 +272,7 @@ class Extract
             return '(' . $delimiters . ')';
         }
 
-        return '(' . preg_quote($delimiter ?? '') . ')';
+        return '(' . preg_quote($delimiter ?? '', '/') . ')';
     }
 
     private static function matchFlags(int $matchMode): string
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
index 93e7282..0560b37 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php
@@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
 use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
@@ -32,11 +33,10 @@ class Format
      *                            If you omit decimals, it is assumed to be 2
      *                         Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed for either of the arguments, then the returned result
+     * @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 DOLLAR($value = 0, $decimals = 2)
+    public static function DOLLAR(mixed $value = 0, mixed $decimals = 2)
     {
         if (is_array($value) || is_array($decimals)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals);
@@ -57,6 +57,7 @@ class Format
             if ($value < 0) {
                 $round = 0 - $round;
             }
+            /** @var float|int|string */
             $value = MathTrig\Round::multiple($value, $round);
         }
         $mask = "{$mask};-{$mask}";
@@ -74,11 +75,10 @@ class Format
      * @param mixed $noCommas Boolean value indicating whether the value should have thousands separators or not
      *                         Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed for either of the arguments, then the returned result
+     * @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 FIXEDFORMAT($value, $decimals = 2, $noCommas = false)
+    public static function FIXEDFORMAT(mixed $value, mixed $decimals = 2, mixed $noCommas = false): array|string
     {
         if (is_array($value) || is_array($decimals) || is_array($noCommas)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals, $noCommas);
@@ -115,21 +115,29 @@ class Format
      * @param mixed $format A string with the Format mask that should be used
      *                         Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed for either of the arguments, then the returned result
+     * @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 TEXTFORMAT($value, $format)
+    public static function TEXTFORMAT(mixed $value, mixed $format): array|string
     {
         if (is_array($value) || is_array($format)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
         }
 
-        $value = Helpers::extractString($value);
-        $format = Helpers::extractString($format);
+        try {
+            $value = Helpers::extractString($value, true);
+            $format = Helpers::extractString($format, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
+
+        $format = (string) NumberFormat::convertSystemFormats($format);
 
         if (!is_numeric($value) && Date::isDateTimeFormatCode($format)) {
-            $value = DateTimeExcel\DateValue::fromString($value);
+            $value1 = DateTimeExcel\DateValue::fromString($value);
+            $value2 = DateTimeExcel\TimeValue::fromString($value);
+            /** @var float|int|string */
+            $value = (is_numeric($value1) && is_numeric($value2)) ? ($value1 + $value2) : (is_numeric($value1) ? $value2 : $value1);
         }
 
         return (string) NumberFormat::toFormattedString($value, $format);
@@ -137,10 +145,8 @@ class Format
 
     /**
      * @param mixed $value Value to check
-     *
-     * @return mixed
      */
-    private static function convertValue($value)
+    private static function convertValue(mixed $value, bool $spacesMeanZero = false): mixed
     {
         $value = $value ?? 0;
         if (is_bool($value)) {
@@ -150,6 +156,15 @@ class Format
                 throw new CalcExp(ExcelError::VALUE());
             }
         }
+        if (is_string($value)) {
+            $value = trim($value);
+            if (ErrorValue::isError($value, true)) {
+                throw new CalcExp($value);
+            }
+            if ($spacesMeanZero && $value === '') {
+                $value = 0;
+            }
+        }
 
         return $value;
     }
@@ -164,7 +179,7 @@ class Format
      *         If an array of values is passed for the argument, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function VALUE($value = '')
+    public static function VALUE(mixed $value = '')
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
@@ -181,6 +196,9 @@ class Format
                 '',
                 trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
             );
+            if ($numberValue === '') {
+                return ExcelError::VALUE();
+            }
             if (is_numeric($numberValue)) {
                 return (float) $numberValue;
             }
@@ -188,7 +206,7 @@ class Format
             $dateSetting = Functions::getReturnDateType();
             Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
 
-            if (strpos($value, ':') !== false) {
+            if (str_contains($value, ':')) {
                 $timeValue = Functions::scalar(DateTimeExcel\TimeValue::fromString($value));
                 if ($timeValue !== ExcelError::VALUE()) {
                     Functions::setReturnDateType($dateSetting);
@@ -211,17 +229,15 @@ class Format
     }
 
     /**
-     * TEXT.
+     * VALUETOTEXT.
      *
      * @param mixed $value The value to format
      *                         Or can be an array of values
-     * @param mixed $format
      *
-     * @return array|string
-     *         If an array of values is passed for either of the arguments, then the returned result
+     * @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 valueToText($value, $format = false)
+    public static function valueToText(mixed $value, mixed $format = false): array|string
     {
         if (is_array($value) || is_array($format)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
@@ -242,18 +258,12 @@ class Format
         return (string) $value;
     }
 
-    /**
-     * @param mixed $decimalSeparator
-     */
-    private static function getDecimalSeparator($decimalSeparator): string
+    private static function getDecimalSeparator(mixed $decimalSeparator): string
     {
         return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator;
     }
 
-    /**
-     * @param mixed $groupSeparator
-     */
-    private static function getGroupSeparator($groupSeparator): string
+    private static function getGroupSeparator(mixed $groupSeparator): string
     {
         return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator;
     }
@@ -267,17 +277,15 @@ class Format
      *                         Or can be an array of values
      * @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
      *                         Or can be an array of values
-     *
-     * @return array|float|string
      */
-    public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
+    public static function NUMBERVALUE(mixed $value = '', mixed $decimalSeparator = null, mixed $groupSeparator = null): array|string|float
     {
         if (is_array($value) || is_array($decimalSeparator) || is_array($groupSeparator)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimalSeparator, $groupSeparator);
         }
 
         try {
-            $value = self::convertValue($value);
+            $value = self::convertValue($value, true);
             $decimalSeparator = self::getDecimalSeparator($decimalSeparator);
             $groupSeparator = self::getGroupSeparator($groupSeparator);
         } catch (CalcExp $e) {
@@ -285,12 +293,12 @@ class Format
         }
 
         if (!is_numeric($value)) {
-            $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE);
+            $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator, '/') . '/', $value, $matches, PREG_OFFSET_CAPTURE);
             if ($decimalPositions > 1) {
                 return ExcelError::VALUE();
             }
-            $decimalOffset = array_pop($matches[0])[1]; // @phpstan-ignore-line
-            if (strpos($value, $groupSeparator, $decimalOffset) !== false) {
+            $decimalOffset = array_pop($matches[0])[1] ?? null;
+            if ($decimalOffset === null || strpos($value, $groupSeparator, $decimalOffset) !== false) {
                 return ExcelError::VALUE();
             }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
index e7b67a3..719de04 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php
@@ -22,22 +22,19 @@ class Helpers
     /**
      * @param mixed $value String value from which to extract characters
      */
-    public static function extractString($value, bool $throwIfError = false): string
+    public static function extractString(mixed $value, bool $throwIfError = false): string
     {
         if (is_bool($value)) {
             return self::convertBooleanValue($value);
         }
-        if ($throwIfError && is_string($value) && ErrorValue::isError($value)) {
+        if ($throwIfError && is_string($value) && ErrorValue::isError($value, true)) {
             throw new CalcExp($value);
         }
 
         return (string) $value;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function extractInt($value, int $minValue, int $gnumericNull = 0, bool $ooBoolOk = false): int
+    public static function extractInt(mixed $value, int $minValue, int $gnumericNull = 0, bool $ooBoolOk = false): int
     {
         if ($value === null) {
             // usually 0, but sometimes 1 for Gnumeric
@@ -57,10 +54,7 @@ class Helpers
         return (int) $value;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function extractFloat($value): float
+    public static function extractFloat(mixed $value): float
     {
         if ($value === null) {
             $value = 0.0;
@@ -69,21 +63,28 @@ class Helpers
             $value = (float) $value;
         }
         if (!is_numeric($value)) {
+            if (is_string($value) && ErrorValue::isError($value, true)) {
+                throw new CalcExp($value);
+            }
+
             throw new CalcExp(ExcelError::VALUE());
         }
 
         return (float) $value;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function validateInt($value): int
+    public static function validateInt(mixed $value, bool $throwIfError = false): int
     {
         if ($value === null) {
             $value = 0;
         } elseif (is_bool($value)) {
             $value = (int) $value;
+        } elseif ($throwIfError && is_string($value) && !is_numeric($value)) {
+            if (!ErrorValue::isError($value, true)) {
+                $value = ExcelError::VALUE();
+            }
+
+            throw new CalcExp($value);
         }
 
         return (int) $value;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php
index 124f001..8f6f196 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php
@@ -25,11 +25,10 @@ class Replace
      * @param mixed $newText String to replace in the defined position
      *                         Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed for either of the arguments, then the returned result
+     * @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 replace($oldText, $start, $chars, $newText)
+    public static function replace(mixed $oldText, mixed $start, mixed $chars, mixed $newText): array|string
     {
         if (is_array($oldText) || is_array($start) || is_array($chars) || is_array($newText)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $oldText, $start, $chars, $newText);
@@ -66,11 +65,10 @@ class Replace
      * @param mixed $instance Integer instance Number for the occurrence of frmText to change
      *                         Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed for either of the arguments, then the returned result
+     * @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 substitute($text = '', $fromText = '', $toText = '', $instance = null)
+    public static function substitute(mixed $text = '', mixed $fromText = '', mixed $toText = '', mixed $instance = null): array|string
     {
         if (is_array($text) || is_array($fromText) || is_array($toText) || is_array($instance)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $text, $fromText, $toText, $instance);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
index 10b6a1a..663d49f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
@@ -25,15 +25,15 @@ class Search
      *         If an array of values is passed for the $value or $chars arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function sensitive($needle, $haystack, $offset = 1)
+    public static function sensitive(mixed $needle, mixed $haystack, mixed $offset = 1): array|string|int
     {
         if (is_array($needle) || is_array($haystack) || is_array($offset)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $needle, $haystack, $offset);
         }
 
         try {
-            $needle = Helpers::extractString($needle);
-            $haystack = Helpers::extractString($haystack);
+            $needle = Helpers::extractString($needle, true);
+            $haystack = Helpers::extractString($haystack, true);
             $offset = Helpers::extractInt($offset, 1, 0, true);
         } catch (CalcExp $e) {
             return $e->getMessage();
@@ -67,15 +67,15 @@ class Search
      *         If an array of values is passed for the $value or $chars arguments, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function insensitive($needle, $haystack, $offset = 1)
+    public static function insensitive(mixed $needle, mixed $haystack, mixed $offset = 1): array|string|int
     {
         if (is_array($needle) || is_array($haystack) || is_array($offset)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $needle, $haystack, $offset);
         }
 
         try {
-            $needle = Helpers::extractString($needle);
-            $haystack = Helpers::extractString($haystack);
+            $needle = Helpers::extractString($needle, true);
+            $haystack = Helpers::extractString($haystack, true);
             $offset = Helpers::extractInt($offset, 1, 0, true);
         } catch (CalcExp $e) {
             return $e->getMessage();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
index 7b97f91..f988a6c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
 
 use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
 
@@ -17,19 +18,22 @@ class Text
      * @param mixed $value String Value
      *                         Or can be an array of values
      *
-     * @return array|int
-     *         If an array of values is passed for the argument, then the returned result
+     * @return array|int|string If an array of values is passed for the argument, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function length($value = '')
+    public static function length(mixed $value = ''): array|int|string
     {
         if (is_array($value)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
         }
 
-        $value = Helpers::extractString($value);
+        try {
+            $value = Helpers::extractString($value, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
 
-        return mb_strlen($value ?? '', 'UTF-8');
+        return mb_strlen($value, 'UTF-8');
     }
 
     /**
@@ -42,18 +46,21 @@ class Text
      * @param mixed $value2 String Value
      *                         Or can be an array of values
      *
-     * @return array|bool
-     *         If an array of values is passed for either of the arguments, then the returned result
+     * @return array|bool|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 exact($value1, $value2)
+    public static function exact(mixed $value1, mixed $value2): array|bool|string
     {
         if (is_array($value1) || is_array($value2)) {
             return self::evaluateArrayArguments([self::class, __FUNCTION__], $value1, $value2);
         }
 
-        $value1 = Helpers::extractString($value1);
-        $value2 = Helpers::extractString($value2);
+        try {
+            $value1 = Helpers::extractString($value1, true);
+            $value2 = Helpers::extractString($value2, true);
+        } catch (CalcExp $e) {
+            return $e->getMessage();
+        }
 
         return $value2 === $value1;
     }
@@ -64,11 +71,10 @@ class Text
      * @param mixed $testValue Value to check
      *                         Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed for the argument, then the returned result
+     * @return array|string If an array of values is passed for the argument, then the returned result
      *            will also be an array with matching dimensions
      */
-    public static function test($testValue = '')
+    public static function test(mixed $testValue = ''): array|string
     {
         if (is_array($testValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $testValue);
@@ -100,11 +106,14 @@ class Text
      * @param mixed $padding The value with which to pad the result.
      *                              The default is #N/A.
      *
-     * @return array the array built from the text, split by the row and column delimiters
+     * @return array|string the array built from the text, split by the row and column delimiters, or an error string
      */
-    public static function split($text, $columnDelimiter = null, $rowDelimiter = null, bool $ignoreEmpty = false, bool $matchMode = true, $padding = '#N/A')
+    public static function split(mixed $text, $columnDelimiter = null, $rowDelimiter = null, bool $ignoreEmpty = false, bool $matchMode = true, mixed $padding = '#N/A'): array|string
     {
         $text = Functions::flattenSingleValue($text);
+        if (ErrorValue::isError($text, true)) {
+            return $text;
+        }
 
         $flags = self::matchFlags($matchMode);
 
@@ -121,9 +130,7 @@ class Text
         if ($ignoreEmpty === true) {
             $rows = array_values(array_filter(
                 $rows,
-                function ($row) {
-                    return $row !== '';
-                }
+                fn ($row): bool => $row !== ''
             ));
         }
 
@@ -139,9 +146,7 @@ class Text
                     if ($ignoreEmpty === true) {
                         $row = array_values(array_filter(
                             $row,
-                            function ($value) {
-                                return $value !== '';
-                            }
+                            fn ($value): bool => $value !== ''
                         ));
                     }
                 }
@@ -149,9 +154,7 @@ class Text
             if ($ignoreEmpty === true) {
                 $rows = array_values(array_filter(
                     $rows,
-                    function ($row) {
-                        return $row !== [] && $row !== [''];
-                    }
+                    fn ($row): bool => $row !== [] && $row !== ['']
                 ));
             }
         }
@@ -159,16 +162,11 @@ class Text
         return self::applyPadding($rows, $padding);
     }
 
-    /**
-     * @param mixed $padding
-     */
-    private static function applyPadding(array $rows, $padding): array
+    private static function applyPadding(array $rows, mixed $padding): array
     {
         $columnCount = array_reduce(
             $rows,
-            function (int $counter, array $row): int {
-                return max($counter, count($row));
-            },
+            fn (int $counter, array $row): int => max($counter, count($row)),
             0
         );
 
@@ -192,9 +190,7 @@ class Text
 
         if (is_array($delimiter) && count($valueSet) > 1) {
             $quotedDelimiters = array_map(
-                function ($delimiter) {
-                    return preg_quote($delimiter ?? '');
-                },
+                fn ($delimiter): string => preg_quote($delimiter ?? '', '/'),
                 $valueSet
             );
             $delimiters = implode('|', $quotedDelimiters);
@@ -202,7 +198,7 @@ class Text
             return '(' . $delimiters . ')';
         }
 
-        return '(' . preg_quote(/** @scrutinizer ignore-type */ Functions::flattenSingleValue($delimiter)) . ')';
+        return '(' . preg_quote(Functions::flattenSingleValue($delimiter), '/') . ')';
     }
 
     private static function matchFlags(bool $matchMode): string
@@ -227,10 +223,7 @@ class Text
         return ($format === 1) ? '{' . $result . '}' : $result;
     }
 
-    /**
-     * @param mixed $cellValue
-     */
-    private static function formatValueMode0($cellValue): string
+    private static function formatValueMode0(mixed $cellValue): string
     {
         if (is_bool($cellValue)) {
             return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
@@ -239,10 +232,7 @@ class Text
         return (string) $cellValue;
     }
 
-    /**
-     * @param mixed $cellValue
-     */
-    private static function formatValueMode1($cellValue): string
+    private static function formatValueMode1(mixed $cellValue): string
     {
         if (is_string($cellValue) && ErrorValue::isError($cellValue) === false) {
             return Calculation::FORMULA_STRING_QUOTE . $cellValue . Calculation::FORMULA_STRING_QUOTE;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php
index 27eceb9..d8f1706 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php
@@ -14,11 +14,10 @@ class Trim
      * @param mixed $stringValue String Value to check
      *                              Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed as the argument, then the returned result will also be an array
+     * @return array|string 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 nonPrintable($stringValue = '')
+    public static function nonPrintable(mixed $stringValue = '')
     {
         if (is_array($stringValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $stringValue);
@@ -35,11 +34,10 @@ class Trim
      * @param mixed $stringValue String Value to check
      *                              Or can be an array of values
      *
-     * @return array|string
-     *         If an array of values is passed as the argument, then the returned result will also be an array
+     * @return array|string 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 spaces($stringValue = '')
+    public static function spaces(mixed $stringValue = ''): array|string
     {
         if (is_array($stringValue)) {
             return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $stringValue);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php
index e8289e3..13c6e5b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php
@@ -7,24 +7,19 @@ use PhpOffice\PhpSpreadsheet\Calculation\Engine\BranchPruner;
 
 class Stack
 {
-    /**
-     * @var BranchPruner
-     */
-    private $branchPruner;
+    private BranchPruner $branchPruner;
 
     /**
      * The parser stack for formulae.
      *
      * @var mixed[]
      */
-    private $stack = [];
+    private array $stack = [];
 
     /**
      * Count of entries in the parser stack.
-     *
-     * @var int
      */
-    private $count = 0;
+    private int $count = 0;
 
     public function __construct(BranchPruner $branchPruner)
     {
@@ -41,10 +36,8 @@ class Stack
 
     /**
      * Push a new entry onto the stack.
-     *
-     * @param mixed $value
      */
-    public function push(string $type, $value, ?string $reference = null): void
+    public function push(string $type, mixed $value, ?string $reference = null): void
     {
         $stackItem = $this->getStackItem($type, $value, $reference);
         $this->stack[$this->count++] = $stackItem;
@@ -62,10 +55,7 @@ class Stack
         $this->stack[$this->count++] = $stackItem;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public function getStackItem(string $type, $value, ?string $reference = null): array
+    public function getStackItem(string $type, mixed $value, ?string $reference = null): array
     {
         $stackItem = [
             'type' => $type,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php
deleted file mode 100644
index f4dd40e..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Calculation;
-
-/**
- * @deprecated 1.18.0
- *
- * @codeCoverageIgnore
- */
-class Web
-{
-    /**
-     * WEBSERVICE.
-     *
-     * Returns data from a web service on the Internet or Intranet.
-     *
-     * Excel Function:
-     *        Webservice(url)
-     *
-     * @deprecated 1.18.0
-     *      Use the webService() method in the Web\Service class instead
-     * @see Web\Service::webService()
-     *
-     * @return string the output resulting from a call to the webservice
-     */
-    public static function WEBSERVICE(string $url)
-    {
-        return Web\Service::webService($url);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php
index 697d3a6..5581341 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php
@@ -18,7 +18,7 @@ class Service
      *
      * @return string the output resulting from a call to the webservice
      */
-    public static function webService(string $url)
+    public static function webService(string $url): string
     {
         $url = trim($url);
         if (strlen($url) > 2048) {
@@ -36,7 +36,7 @@ class Service
 
         try {
             $response = $client->sendRequest($request);
-        } catch (ClientExceptionInterface $e) {
+        } catch (ClientExceptionInterface) {
             return ExcelError::VALUE(); // cURL error
         }
 
@@ -60,11 +60,9 @@ class Service
      * Excel Function:
      *        urlEncode(text)
      *
-     * @param mixed $text
-     *
      * @return string the url encoded output
      */
-    public static function urlEncode($text)
+    public static function urlEncode(mixed $text): string
     {
         if (!is_string($text)) {
             return ExcelError::VALUE();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx
index c0cc1ce..080d5e7 100644
Binary files a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx and b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx differ
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config
index 86f94d3..689203c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config
@@ -1,27 +1,24 @@
+############################################################
 ##
-## PhpSpreadsheet
-##
+## PhpSpreadsheet - locale settings
 ##
+## български (Bulgarian)
 ##
+############################################################
 
-
-ArgumentSeparator	= ;
-
+ArgumentSeparator = ;
+##
+##  (For future use)
+##
+currencySymbol = лв
 
 ##
-##	(For future use)
+## Error Codes
 ##
-currencySymbol	= лв
-
-
-##
-##	Excel Error Codes	(For future use)
-
-##
-NULL	= #ПРАЗНО!
-DIV0	= #ДЕЛ/0!
-VALUE	= #СТОЙНОСТ!
-REF	= #РЕФ!
-NAME	= #ИМЕ?
-NUM	= #ЧИСЛО!
-NA	= #Н/Д
+NULL = #ПРАЗНО!
+DIV0 = #ДЕЛ/0!
+VALUE = #СТОЙНОСТ!
+REF = #РЕФ!
+NAME = #ИМЕ?
+NUM = #ЧИСЛО!
+NA = #Н/Д
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions
index 4bc1574..ec56433 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions
@@ -1,417 +1,409 @@
+############################################################
 ##
-## PhpSpreadsheet
-##
-##
-## Data in this file derived from information provided by web-junior (http://www.web-junior.net/)
+## PhpSpreadsheet - function name translations
 ##
+## български (Bulgarian)
 ##
+############################################################
 
 
 ##
-##	Add-in and Automation functions				Функции надстроек и автоматизации
+## Функции Куб (Cube Functions)
 ##
-GETPIVOTDATA		= ПОЛУЧИТЬ.ДАННЫЕ.СВОДНОЙ.ТАБЛИЦЫ	##	Возвращает данные, хранящиеся в отчете сводной таблицы.
-
+CUBEKPIMEMBER = КУБЭЛЕМЕНТКИП
+CUBEMEMBER = КУБЭЛЕМЕНТ
+CUBEMEMBERPROPERTY = КУБСВОЙСТВОЭЛЕМЕНТА
+CUBERANKEDMEMBER = КУБПОРЭЛЕМЕНТ
+CUBESET = КУБМНОЖ
+CUBESETCOUNT = КУБЧИСЛОЭЛМНОЖ
+CUBEVALUE = КУБЗНАЧЕНИЕ
 
 ##
-##	Cube functions						Функции Куб
+## Функции для работы с базами данных (Database Functions)
 ##
-CUBEKPIMEMBER		= КУБЭЛЕМЕНТКИП				##	Возвращает свойство ключевого индикатора производительности «(КИП)» и отображает имя «КИП» в ячейке. «КИП» представляет собой количественную величину, такую как ежемесячная валовая прибыль или ежеквартальная текучесть кадров, используемой для контроля эффективности работы организации.
-CUBEMEMBER		= КУБЭЛЕМЕНТ				##	Возвращает элемент или кортеж из куба. Используется для проверки существования элемента или кортежа в кубе.
-CUBEMEMBERPROPERTY	= КУБСВОЙСТВОЭЛЕМЕНТА			##	Возвращает значение свойства элемента из куба. Используется для проверки существования имени элемента в кубе и возвращает указанное свойство для этого элемента.
-CUBERANKEDMEMBER	= КУБПОРЭЛЕМЕНТ				##	Возвращает n-ый или ранжированный элемент в множество. Используется для возвращения одного или нескольких элементов в множество, например, лучшего продавца или 10 лучших студентов.
-CUBESET			= КУБМНОЖ				##	Определяет вычислительное множество элементов или кортежей, отправляя на сервер выражение, которое создает множество, а затем возвращает его в Microsoft Office Excel.
-CUBESETCOUNT		= КУБЧИСЛОЭЛМНОЖ			##	Возвращает число элементов множества.
-CUBEVALUE		= КУБЗНАЧЕНИЕ				##	Возвращает обобщенное значение из куба.
-
+DAVERAGE = ДСРЗНАЧ
+DCOUNT = БСЧЁТ
+DCOUNTA = БСЧЁТА
+DGET = БИЗВЛЕЧЬ
+DMAX = ДМАКС
+DMIN = ДМИН
+DPRODUCT = БДПРОИЗВЕД
+DSTDEV = ДСТАНДОТКЛ
+DSTDEVP = ДСТАНДОТКЛП
+DSUM = БДСУММ
+DVAR = БДДИСП
+DVARP = БДДИСПП
 
 ##
-##	Database functions					Функции для работы с базами данных
+## Функции даты и времени (Date & Time Functions)
 ##
-DAVERAGE		= ДСРЗНАЧ				##	Возвращает среднее значение выбранных записей базы данных.
-DCOUNT			= БСЧЁТ					##	Подсчитывает количество числовых ячеек в базе данных.
-DCOUNTA			= БСЧЁТА				##	Подсчитывает количество непустых ячеек в базе данных.
-DGET			= БИЗВЛЕЧЬ				##	Извлекает из базы данных одну запись, удовлетворяющую заданному условию.
-DMAX			= ДМАКС					##	Возвращает максимальное значение среди выделенных записей базы данных.
-DMIN			= ДМИН					##	Возвращает минимальное значение среди выделенных записей базы данных.
-DPRODUCT		= БДПРОИЗВЕД				##	Перемножает значения определенного поля в записях базы данных, удовлетворяющих условию.
-DSTDEV			= ДСТАНДОТКЛ				##	Оценивает стандартное отклонение по выборке для выделенных записей базы данных.
-DSTDEVP			= ДСТАНДОТКЛП				##	Вычисляет стандартное отклонение по генеральной совокупности для выделенных записей базы данных
-DSUM			= БДСУММ				##	Суммирует числа в поле для записей базы данных, удовлетворяющих условию.
-DVAR			= БДДИСП				##	Оценивает дисперсию по выборке из выделенных записей базы данных
-DVARP			= БДДИСПП				##	Вычисляет дисперсию по генеральной совокупности для выделенных записей базы данных
-
+DATE = ДАТА
+DATEVALUE = ДАТАЗНАЧ
+DAY = ДЕНЬ
+DAYS360 = ДНЕЙ360
+EDATE = ДАТАМЕС
+EOMONTH = КОНМЕСЯЦА
+HOUR = ЧАС
+MINUTE = МИНУТЫ
+MONTH = МЕСЯЦ
+NETWORKDAYS = ЧИСТРАБДНИ
+NOW = ТДАТА
+SECOND = СЕКУНДЫ
+TIME = ВРЕМЯ
+TIMEVALUE = ВРЕМЗНАЧ
+TODAY = СЕГОДНЯ
+WEEKDAY = ДЕНЬНЕД
+WEEKNUM = НОМНЕДЕЛИ
+WORKDAY = РАБДЕНЬ
+YEAR = ГОД
+YEARFRAC = ДОЛЯГОДА
 
 ##
-##	Date and time functions					Функции даты и времени
+## Инженерные функции (Engineering Functions)
 ##
-DATE			= ДАТА					##	Возвращает заданную дату в числовом формате.
-DATEVALUE		= ДАТАЗНАЧ				##	Преобразует дату из текстового формата в числовой формат.
-DAY			= ДЕНЬ					##	Преобразует дату в числовом формате в день месяца.
-DAYS360			= ДНЕЙ360				##	Вычисляет количество дней между двумя датами на основе 360-дневного года.
-EDATE			= ДАТАМЕС				##	Возвращает дату в числовом формате, отстоящую на заданное число месяцев вперед или назад от начальной даты.
-EOMONTH			= КОНМЕСЯЦА				##	Возвращает дату в числовом формате для последнего дня месяца, отстоящего вперед или назад на заданное число месяцев.
-HOUR			= ЧАС					##	Преобразует дату в числовом формате в часы.
-MINUTE			= МИНУТЫ				##	Преобразует дату в числовом формате в минуты.
-MONTH			= МЕСЯЦ					##	Преобразует дату в числовом формате в месяцы.
-NETWORKDAYS		= ЧИСТРАБДНИ				##	Возвращает количество рабочих дней между двумя датами.
-NOW			= ТДАТА					##	Возвращает текущую дату и время в числовом формате.
-SECOND			= СЕКУНДЫ				##	Преобразует дату в числовом формате в секунды.
-TIME			= ВРЕМЯ					##	Возвращает заданное время в числовом формате.
-TIMEVALUE		= ВРЕМЗНАЧ				##	Преобразует время из текстового формата в числовой формат.
-TODAY			= СЕГОДНЯ				##	Возвращает текущую дату в числовом формате.
-WEEKDAY			= ДЕНЬНЕД				##	Преобразует дату в числовом формате в день недели.
-WEEKNUM			= НОМНЕДЕЛИ				##	Преобразует числовое представление в число, которое указывает, на какую неделю года приходится указанная дата.
-WORKDAY			= РАБДЕНЬ				##	Возвращает дату в числовом формате, отстоящую вперед или назад на заданное количество рабочих дней.
-YEAR			= ГОД					##	Преобразует дату в числовом формате в год.
-YEARFRAC		= ДОЛЯГОДА				##	Возвращает долю года, которую составляет количество дней между начальной и конечной датами.
-
+BESSELI = БЕССЕЛЬ.I
+BESSELJ = БЕССЕЛЬ.J
+BESSELK = БЕССЕЛЬ.K
+BESSELY = БЕССЕЛЬ.Y
+BIN2DEC = ДВ.В.ДЕС
+BIN2HEX = ДВ.В.ШЕСТН
+BIN2OCT = ДВ.В.ВОСЬМ
+COMPLEX = КОМПЛЕКСН
+CONVERT = ПРЕОБР
+DEC2BIN = ДЕС.В.ДВ
+DEC2HEX = ДЕС.В.ШЕСТН
+DEC2OCT = ДЕС.В.ВОСЬМ
+DELTA = ДЕЛЬТА
+ERF = ФОШ
+ERFC = ДФОШ
+GESTEP = ПОРОГ
+HEX2BIN = ШЕСТН.В.ДВ
+HEX2DEC = ШЕСТН.В.ДЕС
+HEX2OCT = ШЕСТН.В.ВОСЬМ
+IMABS = МНИМ.ABS
+IMAGINARY = МНИМ.ЧАСТЬ
+IMARGUMENT = МНИМ.АРГУМЕНТ
+IMCONJUGATE = МНИМ.СОПРЯЖ
+IMCOS = МНИМ.COS
+IMDIV = МНИМ.ДЕЛ
+IMEXP = МНИМ.EXP
+IMLN = МНИМ.LN
+IMLOG10 = МНИМ.LOG10
+IMLOG2 = МНИМ.LOG2
+IMPOWER = МНИМ.СТЕПЕНЬ
+IMPRODUCT = МНИМ.ПРОИЗВЕД
+IMREAL = МНИМ.ВЕЩ
+IMSIN = МНИМ.SIN
+IMSQRT = МНИМ.КОРЕНЬ
+IMSUB = МНИМ.РАЗН
+IMSUM = МНИМ.СУММ
+OCT2BIN = ВОСЬМ.В.ДВ
+OCT2DEC = ВОСЬМ.В.ДЕС
+OCT2HEX = ВОСЬМ.В.ШЕСТН
 
 ##
-##	Engineering functions					Инженерные функции
+## Финансовые функции (Financial Functions)
 ##
-BESSELI			= БЕССЕЛЬ.I	 			##	Возвращает модифицированную функцию Бесселя In(x).
-BESSELJ			= БЕССЕЛЬ.J				##	Возвращает функцию Бесселя Jn(x).
-BESSELK			= БЕССЕЛЬ.K				##	Возвращает модифицированную функцию Бесселя Kn(x).
-BESSELY			= БЕССЕЛЬ.Y				##	Возвращает функцию Бесселя Yn(x).
-BIN2DEC			= ДВ.В.ДЕС				##	Преобразует двоичное число в десятичное.
-BIN2HEX			= ДВ.В.ШЕСТН				##	Преобразует двоичное число в шестнадцатеричное.
-BIN2OCT			= ДВ.В.ВОСЬМ				##	Преобразует двоичное число в восьмеричное.
-COMPLEX			= КОМПЛЕКСН				##	Преобразует коэффициенты при вещественной и мнимой частях комплексного числа в комплексное число.
-CONVERT			= ПРЕОБР				##	Преобразует число из одной системы единиц измерения в другую.
-DEC2BIN			= ДЕС.В.ДВ				##	Преобразует десятичное число в двоичное.
-DEC2HEX			= ДЕС.В.ШЕСТН				##	Преобразует десятичное число в шестнадцатеричное.
-DEC2OCT			= ДЕС.В.ВОСЬМ				##	Преобразует десятичное число в восьмеричное.
-DELTA			= ДЕЛЬТА				##	Проверяет равенство двух значений.
-ERF			= ФОШ					##	Возвращает функцию ошибки.
-ERFC			= ДФОШ					##	Возвращает дополнительную функцию ошибки.
-GESTEP			= ПОРОГ					##	Проверяет, не превышает ли данное число порогового значения.
-HEX2BIN			= ШЕСТН.В.ДВ				##	Преобразует шестнадцатеричное число в двоичное.
-HEX2DEC			= ШЕСТН.В.ДЕС				##	Преобразует шестнадцатеричное число в десятичное.
-HEX2OCT			= ШЕСТН.В.ВОСЬМ				##	Преобразует шестнадцатеричное число в восьмеричное.
-IMABS			= МНИМ.ABS				##	Возвращает абсолютную величину (модуль) комплексного числа.
-IMAGINARY		= МНИМ.ЧАСТЬ				##	Возвращает коэффициент при мнимой части комплексного числа.
-IMARGUMENT		= МНИМ.АРГУМЕНТ				##	Возвращает значение аргумента комплексного числа (тета) — угол, выраженный в радианах.
-IMCONJUGATE		= МНИМ.СОПРЯЖ				##	Возвращает комплексно-сопряженное комплексное число.
-IMCOS			= МНИМ.COS				##	Возвращает косинус комплексного числа.
-IMDIV			= МНИМ.ДЕЛ				##	Возвращает частное от деления двух комплексных чисел.
-IMEXP			= МНИМ.EXP				##	Возвращает экспоненту комплексного числа.
-IMLN			= МНИМ.LN				##	Возвращает натуральный логарифм комплексного числа.
-IMLOG10			= МНИМ.LOG10				##	Возвращает обычный (десятичный) логарифм комплексного числа.
-IMLOG2			= МНИМ.LOG2				##	Возвращает двоичный логарифм комплексного числа.
-IMPOWER			= МНИМ.СТЕПЕНЬ				##	Возвращает комплексное число, возведенное в целую степень.
-IMPRODUCT		= МНИМ.ПРОИЗВЕД				##	Возвращает произведение от 2 до 29 комплексных чисел.
-IMREAL			= МНИМ.ВЕЩ				##	Возвращает коэффициент при вещественной части комплексного числа.
-IMSIN			= МНИМ.SIN				##	Возвращает синус комплексного числа.
-IMSQRT			= МНИМ.КОРЕНЬ				##	Возвращает значение квадратного корня из комплексного числа.
-IMSUB			= МНИМ.РАЗН				##	Возвращает разность двух комплексных чисел.
-IMSUM			= МНИМ.СУММ				##	Возвращает сумму комплексных чисел.
-OCT2BIN			= ВОСЬМ.В.ДВ				##	Преобразует восьмеричное число в двоичное.
-OCT2DEC			= ВОСЬМ.В.ДЕС				##	Преобразует восьмеричное число в десятичное.
-OCT2HEX			= ВОСЬМ.В.ШЕСТН				##	Преобразует восьмеричное число в шестнадцатеричное.
-
+ACCRINT = НАКОПДОХОД
+ACCRINTM = НАКОПДОХОДПОГАШ
+AMORDEGRC = АМОРУМ
+AMORLINC = АМОРУВ
+COUPDAYBS = ДНЕЙКУПОНДО
+COUPDAYS = ДНЕЙКУПОН
+COUPDAYSNC = ДНЕЙКУПОНПОСЛЕ
+COUPNCD = ДАТАКУПОНПОСЛЕ
+COUPNUM = ЧИСЛКУПОН
+COUPPCD = ДАТАКУПОНДО
+CUMIPMT = ОБЩПЛАТ
+CUMPRINC = ОБЩДОХОД
+DB = ФУО
+DDB = ДДОБ
+DISC = СКИДКА
+DOLLARDE = РУБЛЬ.ДЕС
+DOLLARFR = РУБЛЬ.ДРОБЬ
+DURATION = ДЛИТ
+EFFECT = ЭФФЕКТ
+FV = БС
+FVSCHEDULE = БЗРАСПИС
+INTRATE = ИНОРМА
+IPMT = ПРПЛТ
+IRR = ВСД
+ISPMT = ПРОЦПЛАТ
+MDURATION = МДЛИТ
+MIRR = МВСД
+NOMINAL = НОМИНАЛ
+NPER = КПЕР
+NPV = ЧПС
+ODDFPRICE = ЦЕНАПЕРВНЕРЕГ
+ODDFYIELD = ДОХОДПЕРВНЕРЕГ
+ODDLPRICE = ЦЕНАПОСЛНЕРЕГ
+ODDLYIELD = ДОХОДПОСЛНЕРЕГ
+PMT = ПЛТ
+PPMT = ОСПЛТ
+PRICE = ЦЕНА
+PRICEDISC = ЦЕНАСКИДКА
+PRICEMAT = ЦЕНАПОГАШ
+PV = ПС
+RATE = СТАВКА
+RECEIVED = ПОЛУЧЕНО
+SLN = АПЛ
+SYD = АСЧ
+TBILLEQ = РАВНОКЧЕК
+TBILLPRICE = ЦЕНАКЧЕК
+TBILLYIELD = ДОХОДКЧЕК
+VDB = ПУО
+XIRR = ЧИСТВНДОХ
+XNPV = ЧИСТНЗ
+YIELD = ДОХОД
+YIELDDISC = ДОХОДСКИДКА
+YIELDMAT = ДОХОДПОГАШ
 
 ##
-##	Financial functions					Финансовые функции
+## Информационные функции (Information Functions)
 ##
-ACCRINT			= НАКОПДОХОД				##	Возвращает накопленный процент по ценным бумагам с периодической выплатой процентов.
-ACCRINTM		= НАКОПДОХОДПОГАШ			##	Возвращает накопленный процент по ценным бумагам, проценты по которым выплачиваются в срок погашения.
-AMORDEGRC		= АМОРУМ				##	Возвращает величину амортизации для каждого периода, используя коэффициент амортизации.
-AMORLINC		= АМОРУВ				##	Возвращает величину амортизации для каждого периода.
-COUPDAYBS		= ДНЕЙКУПОНДО				##	Возвращает количество дней от начала действия купона до даты соглашения.
-COUPDAYS		= ДНЕЙКУПОН				##	Возвращает число дней в периоде купона, содержащем дату соглашения.
-COUPDAYSNC		= ДНЕЙКУПОНПОСЛЕ			##	Возвращает число дней от даты соглашения до срока следующего купона.
-COUPNCD			= ДАТАКУПОНПОСЛЕ			##	Возвращает следующую дату купона после даты соглашения.
-COUPNUM			= ЧИСЛКУПОН				##	Возвращает количество купонов, которые могут быть оплачены между датой соглашения и сроком вступления в силу.
-COUPPCD			= ДАТАКУПОНДО				##	Возвращает предыдущую дату купона перед датой соглашения.
-CUMIPMT			= ОБЩПЛАТ				##	Возвращает общую выплату, произведенную между двумя периодическими выплатами.
-CUMPRINC		= ОБЩДОХОД				##	Возвращает общую выплату по займу между двумя периодами.
-DB			= ФУО					##	Возвращает величину амортизации актива для заданного периода, рассчитанную методом фиксированного уменьшения остатка.
-DDB			= ДДОБ					##	Возвращает величину амортизации актива за данный период, используя метод двойного уменьшения остатка или иной явно указанный метод.
-DISC			= СКИДКА				##	Возвращает норму скидки для ценных бумаг.
-DOLLARDE		= РУБЛЬ.ДЕС				##	Преобразует цену в рублях, выраженную в виде дроби, в цену в рублях, выраженную десятичным числом.
-DOLLARFR		= РУБЛЬ.ДРОБЬ				##	Преобразует цену в рублях, выраженную десятичным числом, в цену в рублях, выраженную в виде дроби.
-DURATION		= ДЛИТ					##	Возвращает ежегодную продолжительность действия ценных бумаг с периодическими выплатами по процентам.
-EFFECT			= ЭФФЕКТ				##	Возвращает действующие ежегодные процентные ставки.
-FV			= БС					##	Возвращает будущую стоимость инвестиции.
-FVSCHEDULE		= БЗРАСПИС				##	Возвращает будущую стоимость первоначальной основной суммы после начисления ряда сложных процентов.
-INTRATE			= ИНОРМА				##	Возвращает процентную ставку для полностью инвестированных ценных бумаг.
-IPMT			= ПРПЛТ					##	Возвращает величину выплаты прибыли на вложения за данный период.
-IRR			= ВСД					##	Возвращает внутреннюю ставку доходности для ряда потоков денежных средств.
-ISPMT			= ПРОЦПЛАТ				##	Вычисляет выплаты за указанный период инвестиции.
-MDURATION		= МДЛИТ					##	Возвращает модифицированную длительность Маколея для ценных бумаг с предполагаемой номинальной стоимостью 100 рублей.
-MIRR			= МВСД					##	Возвращает внутреннюю ставку доходности, при которой положительные и отрицательные денежные потоки имеют разные значения ставки.
-NOMINAL			= НОМИНАЛ				##	Возвращает номинальную годовую процентную ставку.
-NPER			= КПЕР					##	Возвращает общее количество периодов выплаты для данного вклада.
-NPV			= ЧПС					##	Возвращает чистую приведенную стоимость инвестиции, основанной на серии периодических денежных потоков и ставке дисконтирования.
-ODDFPRICE		= ЦЕНАПЕРВНЕРЕГ				##	Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг с нерегулярным первым периодом.
-ODDFYIELD		= ДОХОДПЕРВНЕРЕГ			##	Возвращает доход по ценным бумагам с нерегулярным первым периодом.
-ODDLPRICE		= ЦЕНАПОСЛНЕРЕГ				##	Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг с нерегулярным последним периодом.
-ODDLYIELD		= ДОХОДПОСЛНЕРЕГ			##	Возвращает доход по ценным бумагам с нерегулярным последним периодом.
-PMT			= ПЛТ					##	Возвращает величину выплаты за один период аннуитета.
-PPMT			= ОСПЛТ					##	Возвращает величину выплат в погашение основной суммы по инвестиции за заданный период.
-PRICE			= ЦЕНА					##	Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг, по которым производится периодическая выплата процентов.
-PRICEDISC		= ЦЕНАСКИДКА				##	Возвращает цену за 100 рублей номинальной стоимости ценных бумаг, на которые сделана скидка.
-PRICEMAT		= ЦЕНАПОГАШ				##	Возвращает цену за 100 рублей номинальной стоимости ценных бумаг, проценты по которым выплачиваются в срок погашения.
-PV			= ПС					##	Возвращает приведенную (к текущему моменту) стоимость инвестиции.
-RATE			= СТАВКА				##	Возвращает процентную ставку по аннуитету за один период.
-RECEIVED		= ПОЛУЧЕНО				##	Возвращает сумму, полученную к сроку погашения полностью обеспеченных ценных бумаг.
-SLN			= АПЛ					##	Возвращает величину линейной амортизации актива за один период.
-SYD			= АСЧ					##	Возвращает величину амортизации актива за данный период, рассчитанную методом суммы годовых чисел.
-TBILLEQ			= РАВНОКЧЕК				##	Возвращает эквивалентный облигации доход по казначейскому чеку.
-TBILLPRICE		= ЦЕНАКЧЕК				##	Возвращает цену за 100 рублей нарицательной стоимости для казначейского чека.
-TBILLYIELD		= ДОХОДКЧЕК				##	Возвращает доход по казначейскому чеку.
-VDB			= ПУО					##	Возвращает величину амортизации актива для указанного или частичного периода при использовании метода сокращающегося баланса.
-XIRR			= ЧИСТВНДОХ				##	Возвращает внутреннюю ставку доходности для графика денежных потоков, которые не обязательно носят периодический характер.
-XNPV			= ЧИСТНЗ				##	Возвращает чистую приведенную стоимость для денежных потоков, которые не обязательно являются периодическими.
-YIELD			= ДОХОД					##	Возвращает доход от ценных бумаг, по которым производятся периодические выплаты процентов.
-YIELDDISC		= ДОХОДСКИДКА				##	Возвращает годовой доход по ценным бумагам, на которые сделана скидка (пример — казначейские чеки).
-YIELDMAT		= ДОХОДПОГАШ				##	Возвращает годовой доход от ценных бумаг, проценты по которым выплачиваются в срок погашения.
-
+CELL = ЯЧЕЙКА
+ERROR.TYPE = ТИП.ОШИБКИ
+INFO = ИНФОРМ
+ISBLANK = ЕПУСТО
+ISERR = ЕОШ
+ISERROR = ЕОШИБКА
+ISEVEN = ЕЧЁТН
+ISLOGICAL = ЕЛОГИЧ
+ISNA = ЕНД
+ISNONTEXT = ЕНЕТЕКСТ
+ISNUMBER = ЕЧИСЛО
+ISODD = ЕНЕЧЁТ
+ISREF = ЕССЫЛКА
+ISTEXT = ЕТЕКСТ
+N = Ч
+NA = НД
+TYPE = ТИП
 
 ##
-##	Information functions					Информационные функции
+## Логические функции (Logical Functions)
 ##
-CELL			= ЯЧЕЙКА				##	Возвращает информацию о формате, расположении или содержимом ячейки.
-ERROR.TYPE		= ТИП.ОШИБКИ				##	Возвращает числовой код, соответствующий типу ошибки.
-INFO			= ИНФОРМ				##	Возвращает информацию о текущей операционной среде.
-ISBLANK			= ЕПУСТО				##	Возвращает значение ИСТИНА, если аргумент является ссылкой на пустую ячейку.
-ISERR			= ЕОШ					##	Возвращает значение ИСТИНА, если аргумент ссылается на любое значение ошибки, кроме #Н/Д.
-ISERROR			= ЕОШИБКА				##	Возвращает значение ИСТИНА, если аргумент ссылается на любое значение ошибки.
-ISEVEN			= ЕЧЁТН					##	Возвращает значение ИСТИНА, если значение аргумента является четным числом.
-ISLOGICAL		= ЕЛОГИЧ				##	Возвращает значение ИСТИНА, если аргумент ссылается на логическое значение.
-ISNA			= ЕНД					##	Возвращает значение ИСТИНА, если аргумент ссылается на значение ошибки #Н/Д.
-ISNONTEXT		= ЕНЕТЕКСТ				##	Возвращает значение ИСТИНА, если значение аргумента не является текстом.
-ISNUMBER		= ЕЧИСЛО				##	Возвращает значение ИСТИНА, если аргумент ссылается на число.
-ISODD			= ЕНЕЧЁТ				##	Возвращает значение ИСТИНА, если значение аргумента является нечетным числом.
-ISREF			= ЕССЫЛКА				##	Возвращает значение ИСТИНА, если значение аргумента является ссылкой.
-ISTEXT			= ЕТЕКСТ				##	Возвращает значение ИСТИНА, если значение аргумента является текстом.
-N			= Ч					##	Возвращает значение, преобразованное в число.
-NA			= НД					##	Возвращает значение ошибки #Н/Д.
-TYPE			= ТИП					##	Возвращает число, обозначающее тип данных значения.
-
+AND = И
+FALSE = ЛОЖЬ
+IF = ЕСЛИ
+IFERROR = ЕСЛИОШИБКА
+NOT = НЕ
+OR = ИЛИ
+TRUE = ИСТИНА
 
 ##
-##	Logical functions					Логические функции
+## Функции ссылки и поиска (Lookup & Reference Functions)
 ##
-AND			= И					##	Renvoie VRAI si tous ses arguments sont VRAI.
-FALSE			= ЛОЖЬ					##	Возвращает логическое значение ЛОЖЬ.
-IF			= ЕСЛИ					##	Выполняет проверку условия.
-IFERROR			= ЕСЛИОШИБКА				##	Возвращает введённое значение, если вычисление по формуле вызывает ошибку; в противном случае функция возвращает результат вычисления.
-NOT			= НЕ					##	Меняет логическое значение своего аргумента на противоположное.
-OR			= ИЛИ					##	Возвращает значение ИСТИНА, если хотя бы один аргумент имеет значение ИСТИНА.
-TRUE			= ИСТИНА				##	Возвращает логическое значение ИСТИНА.
-
+ADDRESS = АДРЕС
+AREAS = ОБЛАСТИ
+CHOOSE = ВЫБОР
+COLUMN = СТОЛБЕЦ
+COLUMNS = ЧИСЛСТОЛБ
+GETPIVOTDATA = ПОЛУЧИТЬ.ДАННЫЕ.СВОДНОЙ.ТАБЛИЦЫ
+HLOOKUP = ГПР
+HYPERLINK = ГИПЕРССЫЛКА
+INDEX = ИНДЕКС
+INDIRECT = ДВССЫЛ
+LOOKUP = ПРОСМОТР
+MATCH = ПОИСКПОЗ
+OFFSET = СМЕЩ
+ROW = СТРОКА
+ROWS = ЧСТРОК
+RTD = ДРВ
+TRANSPOSE = ТРАНСП
+VLOOKUP = ВПР
 
 ##
-##	Lookup and reference functions				Функции ссылки и поиска
+## Математические и тригонометрические функции (Math & Trig Functions)
 ##
-ADDRESS			= АДРЕС					##	Возвращает ссылку на отдельную ячейку листа в виде текста.
-AREAS			= ОБЛАСТИ				##	Возвращает количество областей в ссылке.
-CHOOSE			= ВЫБОР					##	Выбирает значение из списка значений по индексу.
-COLUMN			= СТОЛБЕЦ				##	Возвращает номер столбца, на который указывает ссылка.
-COLUMNS			= ЧИСЛСТОЛБ				##	Возвращает количество столбцов в ссылке.
-HLOOKUP			= ГПР					##	Ищет в первой строке массива и возвращает значение отмеченной ячейки
-HYPERLINK		= ГИПЕРССЫЛКА				##	Создает ссылку, открывающую документ, который находится на сервере сети, в интрасети или в Интернете.
-INDEX			= ИНДЕКС				##	Использует индекс для выбора значения из ссылки или массива.
-INDIRECT		= ДВССЫЛ				##	Возвращает ссылку, заданную текстовым значением.
-LOOKUP			= ПРОСМОТР				##	Ищет значения в векторе или массиве.
-MATCH			= ПОИСКПОЗ				##	Ищет значения в ссылке или массиве.
-OFFSET			= СМЕЩ					##	Возвращает смещение ссылки относительно заданной ссылки.
-ROW			= СТРОКА				##	Возвращает номер строки, определяемой ссылкой.
-ROWS			= ЧСТРОК				##	Возвращает количество строк в ссылке.
-RTD			= ДРВ					##	Извлекает данные реального времени из программ, поддерживающих автоматизацию COM (Программирование объектов. Стандартное средство для работы с объектами некоторого приложения из другого приложения или средства разработки. Программирование объектов (ранее называемое программированием OLE) является функцией модели COM (Component Object Model, модель компонентных объектов).).
-TRANSPOSE		= ТРАНСП				##	Возвращает транспонированный массив.
-VLOOKUP			= ВПР					##	Ищет значение в первом столбце массива и возвращает значение из ячейки в найденной строке и указанном столбце.
-
+ABS = ABS
+ACOS = ACOS
+ACOSH = ACOSH
+ASIN = ASIN
+ASINH = ASINH
+ATAN = ATAN
+ATAN2 = ATAN2
+ATANH = ATANH
+COMBIN = ЧИСЛКОМБ
+COS = COS
+COSH = COSH
+DEGREES = ГРАДУСЫ
+EVEN = ЧЁТН
+EXP = EXP
+FACT = ФАКТР
+FACTDOUBLE = ДВФАКТР
+GCD = НОД
+INT = ЦЕЛОЕ
+LCM = НОК
+LN = LN
+LOG = LOG
+LOG10 = LOG10
+MDETERM = МОПРЕД
+MINVERSE = МОБР
+MMULT = МУМНОЖ
+MOD = ОСТАТ
+MROUND = ОКРУГЛТ
+MULTINOMIAL = МУЛЬТИНОМ
+ODD = НЕЧЁТ
+PI = ПИ
+POWER = СТЕПЕНЬ
+PRODUCT = ПРОИЗВЕД
+QUOTIENT = ЧАСТНОЕ
+RADIANS = РАДИАНЫ
+RAND = СЛЧИС
+RANDBETWEEN = СЛУЧМЕЖДУ
+ROMAN = РИМСКОЕ
+ROUND = ОКРУГЛ
+ROUNDDOWN = ОКРУГЛВНИЗ
+ROUNDUP = ОКРУГЛВВЕРХ
+SERIESSUM = РЯД.СУММ
+SIGN = ЗНАК
+SIN = SIN
+SINH = SINH
+SQRT = КОРЕНЬ
+SQRTPI = КОРЕНЬПИ
+SUBTOTAL = ПРОМЕЖУТОЧНЫЕ.ИТОГИ
+SUM = СУММ
+SUMIF = СУММЕСЛИ
+SUMIFS = СУММЕСЛИМН
+SUMPRODUCT = СУММПРОИЗВ
+SUMSQ = СУММКВ
+SUMX2MY2 = СУММРАЗНКВ
+SUMX2PY2 = СУММСУММКВ
+SUMXMY2 = СУММКВРАЗН
+TAN = TAN
+TANH = TANH
+TRUNC = ОТБР
 
 ##
-##	Math and trigonometry functions				Математические и тригонометрические функции
+## Статистические функции (Statistical Functions)
 ##
-ABS			= ABS					##	Возвращает модуль (абсолютную величину) числа.
-ACOS			= ACOS					##	Возвращает арккосинус числа.
-ACOSH			= ACOSH					##	Возвращает гиперболический арккосинус числа.
-ASIN			= ASIN					##	Возвращает арксинус числа.
-ASINH			= ASINH					##	Возвращает гиперболический арксинус числа.
-ATAN			= ATAN					##	Возвращает арктангенс числа.
-ATAN2			= ATAN2					##	Возвращает арктангенс для заданных координат x и y.
-ATANH			= ATANH					##	Возвращает гиперболический арктангенс числа.
-CEILING			= ОКРВВЕРХ				##	Округляет число до ближайшего целого или до ближайшего кратного указанному значению.
-COMBIN			= ЧИСЛКОМБ				##	Возвращает количество комбинаций для заданного числа объектов.
-COS			= COS					##	Возвращает косинус числа.
-COSH			= COSH					##	Возвращает гиперболический косинус числа.
-DEGREES			= ГРАДУСЫ				##	Преобразует радианы в градусы.
-EVEN			= ЧЁТН					##	Округляет число до ближайшего четного целого.
-EXP			= EXP					##	Возвращает число e, возведенное в указанную степень.
-FACT			= ФАКТР					##	Возвращает факториал числа.
-FACTDOUBLE		= ДВФАКТР				##	Возвращает двойной факториал числа.
-FLOOR			= ОКРВНИЗ				##	Округляет число до ближайшего меньшего по модулю значения.
-GCD			= НОД					##	Возвращает наибольший общий делитель.
-INT			= ЦЕЛОЕ					##	Округляет число до ближайшего меньшего целого.
-LCM			= НОК					##	Возвращает наименьшее общее кратное.
-LN			= LN					##	Возвращает натуральный логарифм числа.
-LOG			= LOG					##	Возвращает логарифм числа по заданному основанию.
-LOG10			= LOG10					##	Возвращает десятичный логарифм числа.
-MDETERM			= МОПРЕД				##	Возвращает определитель матрицы массива.
-MINVERSE		= МОБР					##	Возвращает обратную матрицу массива.
-MMULT			= МУМНОЖ				##	Возвращает произведение матриц двух массивов.
-MOD			= ОСТАТ					##	Возвращает остаток от деления.
-MROUND			= ОКРУГЛТ				##	Возвращает число, округленное с требуемой точностью.
-MULTINOMIAL		= МУЛЬТИНОМ				##	Возвращает мультиномиальный коэффициент множества чисел.
-ODD			= НЕЧЁТ					##	Округляет число до ближайшего нечетного целого.
-PI			= ПИ					##	Возвращает число пи.
-POWER			= СТЕПЕНЬ				##	Возвращает результат возведения числа в степень.
-PRODUCT			= ПРОИЗВЕД				##	Возвращает произведение аргументов.
-QUOTIENT		= ЧАСТНОЕ				##	Возвращает целую часть частного при делении.
-RADIANS			= РАДИАНЫ				##	Преобразует градусы в радианы.
-RAND			= СЛЧИС					##	Возвращает случайное число в интервале от 0 до 1.
-RANDBETWEEN		= СЛУЧМЕЖДУ				##	Возвращает случайное число в интервале между двумя заданными числами.
-ROMAN			= РИМСКОЕ				##	Преобразует арабские цифры в римские в виде текста.
-ROUND			= ОКРУГЛ				##	Округляет число до указанного количества десятичных разрядов.
-ROUNDDOWN		= ОКРУГЛВНИЗ				##	Округляет число до ближайшего меньшего по модулю значения.
-ROUNDUP			= ОКРУГЛВВЕРХ				##	Округляет число до ближайшего большего по модулю значения.
-SERIESSUM		= РЯД.СУММ				##	Возвращает сумму степенного ряда, вычисленную по формуле.
-SIGN			= ЗНАК					##	Возвращает знак числа.
-SIN			= SIN					##	Возвращает синус заданного угла.
-SINH			= SINH					##	Возвращает гиперболический синус числа.
-SQRT			= КОРЕНЬ				##	Возвращает положительное значение квадратного корня.
-SQRTPI			= КОРЕНЬПИ				##	Возвращает квадратный корень из значения выражения (число * ПИ).
-SUBTOTAL		= ПРОМЕЖУТОЧНЫЕ.ИТОГИ			##	Возвращает промежуточный итог в списке или базе данных.
-SUM			= СУММ					##	Суммирует аргументы.
-SUMIF			= СУММЕСЛИ				##	Суммирует ячейки, удовлетворяющие заданному условию.
-SUMIFS			= СУММЕСЛИМН				##	Суммирует диапазон ячеек, удовлетворяющих нескольким условиям.
-SUMPRODUCT		= СУММПРОИЗВ				##	Возвращает сумму произведений соответствующих элементов массивов.
-SUMSQ			= СУММКВ				##	Возвращает сумму квадратов аргументов.
-SUMX2MY2		= СУММРАЗНКВ				##	Возвращает сумму разностей квадратов соответствующих значений в двух массивах.
-SUMX2PY2		= СУММСУММКВ				##	Возвращает сумму сумм квадратов соответствующих элементов двух массивов.
-SUMXMY2			= СУММКВРАЗН				##	Возвращает сумму квадратов разностей соответствующих значений в двух массивах.
-TAN			= TAN					##	Возвращает тангенс числа.
-TANH			= TANH					##	Возвращает гиперболический тангенс числа.
-TRUNC			= ОТБР					##	Отбрасывает дробную часть числа.
-
+AVEDEV = СРОТКЛ
+AVERAGE = СРЗНАЧ
+AVERAGEA = СРЗНАЧА
+AVERAGEIF = СРЗНАЧЕСЛИ
+AVERAGEIFS = СРЗНАЧЕСЛИМН
+CORREL = КОРРЕЛ
+COUNT = СЧЁТ
+COUNTA = СЧЁТЗ
+COUNTBLANK = СЧИТАТЬПУСТОТЫ
+COUNTIF = СЧЁТЕСЛИ
+COUNTIFS = СЧЁТЕСЛИМН
+DEVSQ = КВАДРОТКЛ
+FISHER = ФИШЕР
+FISHERINV = ФИШЕРОБР
+FREQUENCY = ЧАСТОТА
+GAMMALN = ГАММАНЛОГ
+GEOMEAN = СРГЕОМ
+GROWTH = РОСТ
+HARMEAN = СРГАРМ
+INTERCEPT = ОТРЕЗОК
+KURT = ЭКСЦЕСС
+LARGE = НАИБОЛЬШИЙ
+LINEST = ЛИНЕЙН
+LOGEST = ЛГРФПРИБЛ
+MAX = МАКС
+MAXA = МАКСА
+MEDIAN = МЕДИАНА
+MIN = МИН
+MINA = МИНА
+PEARSON = ПИРСОН
+PERMUT = ПЕРЕСТ
+PROB = ВЕРОЯТНОСТЬ
+RSQ = КВПИРСОН
+SKEW = СКОС
+SLOPE = НАКЛОН
+SMALL = НАИМЕНЬШИЙ
+STANDARDIZE = НОРМАЛИЗАЦИЯ
+STDEVA = СТАНДОТКЛОНА
+STDEVPA = СТАНДОТКЛОНПА
+STEYX = СТОШYX
+TREND = ТЕНДЕНЦИЯ
+TRIMMEAN = УРЕЗСРЕДНЕЕ
+VARA = ДИСПА
+VARPA = ДИСПРА
 
 ##
-##	Statistical functions					Статистические функции
+## Текстовые функции (Text Functions)
 ##
-AVEDEV			= СРОТКЛ				##	Возвращает среднее арифметическое абсолютных значений отклонений точек данных от среднего.
-AVERAGE			= СРЗНАЧ				##	Возвращает среднее арифметическое аргументов.
-AVERAGEA		= СРЗНАЧА				##	Возвращает среднее арифметическое аргументов, включая числа, текст и логические значения.
-AVERAGEIF		= СРЗНАЧЕСЛИ 				##	Возвращает среднее значение (среднее арифметическое) всех ячеек в диапазоне, которые удовлетворяют данному условию.
-AVERAGEIFS		= СРЗНАЧЕСЛИМН 				##	Возвращает среднее значение (среднее арифметическое) всех ячеек, которые удовлетворяют нескольким условиям.
-BETADIST		= БЕТАРАСП				##	Возвращает интегральную функцию бета-распределения.
-BETAINV			= БЕТАОБР				##	Возвращает обратную интегральную функцию указанного бета-распределения.
-BINOMDIST		= БИНОМРАСП				##	Возвращает отдельное значение биномиального распределения.
-CHIDIST			= ХИ2РАСП				##	Возвращает одностороннюю вероятность распределения хи-квадрат.
-CHIINV			= ХИ2ОБР				##	Возвращает обратное значение односторонней вероятности распределения хи-квадрат.
-CHITEST			= ХИ2ТЕСТ				##	Возвращает тест на независимость.
-CONFIDENCE		= ДОВЕРИТ				##	Возвращает доверительный интервал для среднего значения по генеральной совокупности.
-CORREL			= КОРРЕЛ				##	Возвращает коэффициент корреляции между двумя множествами данных.
-COUNT			= СЧЁТ					##	Подсчитывает количество чисел в списке аргументов.
-COUNTA			= СЧЁТЗ					##	Подсчитывает количество значений в списке аргументов.
-COUNTBLANK		= СЧИТАТЬПУСТОТЫ			##	Подсчитывает количество пустых ячеек в диапазоне
-COUNTIF			= СЧЁТЕСЛИ 				##	Подсчитывает количество ячеек в диапазоне, удовлетворяющих заданному условию
-COUNTIFS		= СЧЁТЕСЛИМН				##	Подсчитывает количество ячеек внутри диапазона, удовлетворяющих нескольким условиям.
-COVAR			= КОВАР					##	Возвращает ковариацию, среднее произведений парных отклонений
-CRITBINOM		= КРИТБИНОМ				##	Возвращает наименьшее значение, для которого интегральное биномиальное распределение меньше или равно заданному критерию.
-DEVSQ			= КВАДРОТКЛ				##	Возвращает сумму квадратов отклонений.
-EXPONDIST		= ЭКСПРАСП				##	Возвращает экспоненциальное распределение.
-FDIST			= FРАСП					##	Возвращает F-распределение вероятности.
-FINV			= FРАСПОБР				##	Возвращает обратное значение для F-распределения вероятности.
-FISHER			= ФИШЕР					##	Возвращает преобразование Фишера.
-FISHERINV		= ФИШЕРОБР				##	Возвращает обратное преобразование Фишера.
-FORECAST		= ПРЕДСКАЗ				##	Возвращает значение линейного тренда.
-FREQUENCY		= ЧАСТОТА				##	Возвращает распределение частот в виде вертикального массива.
-FTEST			= ФТЕСТ					##	Возвращает результат F-теста.
-GAMMADIST		= ГАММАРАСП				##	Возвращает гамма-распределение.
-GAMMAINV		= ГАММАОБР				##	Возвращает обратное гамма-распределение.
-GAMMALN			= ГАММАНЛОГ				##	Возвращает натуральный логарифм гамма функции, Γ(x).
-GEOMEAN			= СРГЕОМ				##	Возвращает среднее геометрическое.
-GROWTH			= РОСТ					##	Возвращает значения в соответствии с экспоненциальным трендом.
-HARMEAN			= СРГАРМ				##	Возвращает среднее гармоническое.
-HYPGEOMDIST		= ГИПЕРГЕОМЕТ				##	Возвращает гипергеометрическое распределение.
-INTERCEPT		= ОТРЕЗОК				##	Возвращает отрезок, отсекаемый на оси линией линейной регрессии.
-KURT			= ЭКСЦЕСС				##	Возвращает эксцесс множества данных.
-LARGE			= НАИБОЛЬШИЙ				##	Возвращает k-ое наибольшее значение в множестве данных.
-LINEST			= ЛИНЕЙН				##	Возвращает параметры линейного тренда.
-LOGEST			= ЛГРФПРИБЛ				##	Возвращает параметры экспоненциального тренда.
-LOGINV			= ЛОГНОРМОБР				##	Возвращает обратное логарифмическое нормальное распределение.
-LOGNORMDIST		= ЛОГНОРМРАСП				##	Возвращает интегральное логарифмическое нормальное распределение.
-MAX			= МАКС					##	Возвращает наибольшее значение в списке аргументов.
-MAXA			= МАКСА					##	Возвращает наибольшее значение в списке аргументов, включая числа, текст и логические значения.
-MEDIAN			= МЕДИАНА				##	Возвращает медиану заданных чисел.
-MIN			= МИН					##	Возвращает наименьшее значение в списке аргументов.
-MINA			= МИНА					##	Возвращает наименьшее значение в списке аргументов, включая числа, текст и логические значения.
-MODE			= МОДА					##	Возвращает значение моды множества данных.
-NEGBINOMDIST		= ОТРБИНОМРАСП				##	Возвращает отрицательное биномиальное распределение.
-NORMDIST		= НОРМРАСП				##	Возвращает нормальную функцию распределения.
-NORMINV			= НОРМОБР				##	Возвращает обратное нормальное распределение.
-NORMSDIST		= НОРМСТРАСП				##	Возвращает стандартное нормальное интегральное распределение.
-NORMSINV		= НОРМСТОБР				##	Возвращает обратное значение стандартного нормального распределения.
-PEARSON			= ПИРСОН				##	Возвращает коэффициент корреляции Пирсона.
-PERCENTILE		= ПЕРСЕНТИЛЬ				##	Возвращает k-ую персентиль для значений диапазона.
-PERCENTRANK		= ПРОЦЕНТРАНГ				##	Возвращает процентную норму значения в множестве данных.
-PERMUT			= ПЕРЕСТ				##	Возвращает количество перестановок для заданного числа объектов.
-POISSON			= ПУАССОН				##	Возвращает распределение Пуассона.
-PROB			= ВЕРОЯТНОСТЬ				##	Возвращает вероятность того, что значение из диапазона находится внутри заданных пределов.
-QUARTILE		= КВАРТИЛЬ				##	Возвращает квартиль множества данных.
-RANK			= РАНГ					##	Возвращает ранг числа в списке чисел.
-RSQ			= КВПИРСОН				##	Возвращает квадрат коэффициента корреляции Пирсона.
-SKEW			= СКОС					##	Возвращает асимметрию распределения.
-SLOPE			= НАКЛОН				##	Возвращает наклон линии линейной регрессии.
-SMALL			= НАИМЕНЬШИЙ				##	Возвращает k-ое наименьшее значение в множестве данных.
-STANDARDIZE		= НОРМАЛИЗАЦИЯ				##	Возвращает нормализованное значение.
-STDEV			= СТАНДОТКЛОН				##	Оценивает стандартное отклонение по выборке.
-STDEVA			= СТАНДОТКЛОНА				##	Оценивает стандартное отклонение по выборке, включая числа, текст и логические значения.
-STDEVP			= СТАНДОТКЛОНП				##	Вычисляет стандартное отклонение по генеральной совокупности.
-STDEVPA			= СТАНДОТКЛОНПА				##	Вычисляет стандартное отклонение по генеральной совокупности, включая числа, текст и логические значения.
-STEYX			= СТОШYX				##	Возвращает стандартную ошибку предсказанных значений y для каждого значения x в регрессии.
-TDIST			= СТЬЮДРАСП				##	Возвращает t-распределение Стьюдента.
-TINV			= СТЬЮДРАСПОБР				##	Возвращает обратное t-распределение Стьюдента.
-TREND			= ТЕНДЕНЦИЯ				##	Возвращает значения в соответствии с линейным трендом.
-TRIMMEAN		= УРЕЗСРЕДНЕЕ				##	Возвращает среднее внутренности множества данных.
-TTEST			= ТТЕСТ					##	Возвращает вероятность, соответствующую критерию Стьюдента.
-VAR			= ДИСП					##	Оценивает дисперсию по выборке.
-VARA			= ДИСПА					##	Оценивает дисперсию по выборке, включая числа, текст и логические значения.
-VARP			= ДИСПР					##	Вычисляет дисперсию для генеральной совокупности.
-VARPA			= ДИСПРА				##	Вычисляет дисперсию для генеральной совокупности, включая числа, текст и логические значения.
-WEIBULL			= ВЕЙБУЛЛ				##	Возвращает распределение Вейбулла.
-ZTEST			= ZТЕСТ					##	Возвращает двустороннее P-значение z-теста.
-
+ASC = ASC
+BAHTTEXT = БАТТЕКСТ
+CHAR = СИМВОЛ
+CLEAN = ПЕЧСИМВ
+CODE = КОДСИМВ
+DOLLAR = РУБЛЬ
+EXACT = СОВПАД
+FIND = НАЙТИ
+FINDB = НАЙТИБ
+FIXED = ФИКСИРОВАННЫЙ
+LEFT = ЛЕВСИМВ
+LEFTB = ЛЕВБ
+LEN = ДЛСТР
+LENB = ДЛИНБ
+LOWER = СТРОЧН
+MID = ПСТР
+MIDB = ПСТРБ
+PHONETIC = PHONETIC
+PROPER = ПРОПНАЧ
+REPLACE = ЗАМЕНИТЬ
+REPLACEB = ЗАМЕНИТЬБ
+REPT = ПОВТОР
+RIGHT = ПРАВСИМВ
+RIGHTB = ПРАВБ
+SEARCH = ПОИСК
+SEARCHB = ПОИСКБ
+SUBSTITUTE = ПОДСТАВИТЬ
+T = Т
+TEXT = ТЕКСТ
+TRIM = СЖПРОБЕЛЫ
+UPPER = ПРОПИСН
+VALUE = ЗНАЧЕН
 
 ##
-##	Text functions						Текстовые функции
+##  (Web Functions)
 ##
-ASC			= ASC					##	Для языков с двухбайтовыми наборами знаков (например, катакана) преобразует полноширинные (двухбайтовые) знаки в полуширинные (однобайтовые).
-BAHTTEXT		= БАТТЕКСТ				##	Преобразует число в текст, используя денежный формат ß (БАТ).
-CHAR			= СИМВОЛ				##	Возвращает знак с заданным кодом.
-CLEAN			= ПЕЧСИМВ				##	Удаляет все непечатаемые знаки из текста.
-CODE			= КОДСИМВ				##	Возвращает числовой код первого знака в текстовой строке.
-CONCATENATE		= СЦЕПИТЬ				##	Объединяет несколько текстовых элементов в один.
-DOLLAR			= РУБЛЬ					##	Преобразует число в текст, используя денежный формат.
-EXACT			= СОВПАД				##	Проверяет идентичность двух текстовых значений.
-FIND			= НАЙТИ					##	Ищет вхождения одного текстового значения в другом (с учетом регистра).
-FINDB			= НАЙТИБ				##	Ищет вхождения одного текстового значения в другом (с учетом регистра).
-FIXED			= ФИКСИРОВАННЫЙ				##	Форматирует число и преобразует его в текст с заданным числом десятичных знаков.
-JIS			= JIS					##	Для языков с двухбайтовыми наборами знаков (например, катакана) преобразует полуширинные (однобайтовые) знаки в текстовой строке в полноширинные (двухбайтовые).
-LEFT			= ЛЕВСИМВ				##	Возвращает крайние слева знаки текстового значения.
-LEFTB			= ЛЕВБ					##	Возвращает крайние слева знаки текстового значения.
-LEN			= ДЛСТР					##	Возвращает количество знаков в текстовой строке.
-LENB			= ДЛИНБ					##	Возвращает количество знаков в текстовой строке.
-LOWER			= СТРОЧН				##	Преобразует все буквы текста в строчные.
-MID			= ПСТР					##	Возвращает заданное число знаков из строки текста, начиная с указанной позиции.
-MIDB			= ПСТРБ					##	Возвращает заданное число знаков из строки текста, начиная с указанной позиции.
-PHONETIC		= PHONETIC				##	Извлекает фонетические (фуригана) знаки из текстовой строки.
-PROPER			= ПРОПНАЧ				##	Преобразует первую букву в каждом слове текста в прописную.
-REPLACE			= ЗАМЕНИТЬ				##	Заменяет знаки в тексте.
-REPLACEB		= ЗАМЕНИТЬБ				##	Заменяет знаки в тексте.
-REPT			= ПОВТОР				##	Повторяет текст заданное число раз.
-RIGHT			= ПРАВСИМВ				##	Возвращает крайние справа знаки текстовой строки.
-RIGHTB			= ПРАВБ					##	Возвращает крайние справа знаки текстовой строки.
-SEARCH			= ПОИСК					##	Ищет вхождения одного текстового значения в другом (без учета регистра).
-SEARCHB			= ПОИСКБ				##	Ищет вхождения одного текстового значения в другом (без учета регистра).
-SUBSTITUTE		= ПОДСТАВИТЬ				##	Заменяет в текстовой строке старый текст новым.
-T			= Т					##	Преобразует аргументы в текст.
-TEXT			= ТЕКСТ					##	Форматирует число и преобразует его в текст.
-TRIM			= СЖПРОБЕЛЫ				##	Удаляет из текста пробелы.
-UPPER			= ПРОПИСН				##	Преобразует все буквы текста в прописные.
-VALUE			= ЗНАЧЕН				##	Преобразует текстовый аргумент в число.
+
+##
+##  (Compatibility Functions)
+##
+BETADIST = БЕТАРАСП
+BETAINV = БЕТАОБР
+BINOMDIST = БИНОМРАСП
+CEILING = ОКРВВЕРХ
+CHIDIST = ХИ2РАСП
+CHIINV = ХИ2ОБР
+CHITEST = ХИ2ТЕСТ
+CONCATENATE = СЦЕПИТЬ
+CONFIDENCE = ДОВЕРИТ
+COVAR = КОВАР
+CRITBINOM = КРИТБИНОМ
+EXPONDIST = ЭКСПРАСП
+FDIST = FРАСП
+FINV = FРАСПОБР
+FLOOR = ОКРВНИЗ
+FORECAST = ПРЕДСКАЗ
+FTEST = ФТЕСТ
+GAMMADIST = ГАММАРАСП
+GAMMAINV = ГАММАОБР
+HYPGEOMDIST = ГИПЕРГЕОМЕТ
+LOGINV = ЛОГНОРМОБР
+LOGNORMDIST = ЛОГНОРМРАСП
+MODE = МОДА
+NEGBINOMDIST = ОТРБИНОМРАСП
+NORMDIST = НОРМРАСП
+NORMINV = НОРМОБР
+NORMSDIST = НОРМСТРАСП
+NORMSINV = НОРМСТОБР
+PERCENTILE = ПЕРСЕНТИЛЬ
+PERCENTRANK = ПРОЦЕНТРАНГ
+POISSON = ПУАССОН
+QUARTILE = КВАРТИЛЬ
+RANK = РАНГ
+STDEV = СТАНДОТКЛОН
+STDEVP = СТАНДОТКЛОНП
+TDIST = СТЬЮДРАСП
+TINV = СТЬЮДРАСПОБР
+TTEST = ТТЕСТ
+VAR = ДИСП
+VARP = ДИСПР
+WEIBULL = ВЕЙБУЛЛ
+ZTEST = ZТЕСТ
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config
index 859e4be..4e068ab 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config
@@ -1,8 +1,24 @@
+############################################################
 ##
-## PhpSpreadsheet
+## PhpSpreadsheet - locale settings
 ##
+## English-UK (English-UK)
+##
+############################################################
+
+ArgumentSeparator = ,
+##
+##  (For future use)
+##
+currencySymbol = £
 
 ##
-##	(For future use)
+## Error Codes
 ##
-currencySymbol	= £
+NULL
+DIV0
+VALUE
+REF
+NAME
+NUM
+NA
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php
index ed765aa..923f941 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressHelper.php
@@ -11,7 +11,7 @@ class AddressHelper
     public const R1C1_COORDINATE_REGEX = '/(R((?:\[-?\d*\])|(?:\d*))?)(C((?:\[-?\d*\])|(?:\d*))?)/i';
 
     /** @return string[] */
-    public static function getRowAndColumnChars()
+    public static function getRowAndColumnChars(): array
     {
         $rowChar = 'R';
         $colChar = 'C';
@@ -96,7 +96,7 @@ class AddressHelper
         int $currentRowNumber = 1,
         int $currentColumnNumber = 1
     ): string {
-        if (substr($formula, 0, 3) == 'of:') {
+        if (str_starts_with($formula, 'of:')) {
             // We have an old-style SpreadsheetML Formula
             return self::convertSpreadsheetMLFormula($formula);
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressRange.php
index 5475231..22e9d11 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressRange.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AddressRange.php
@@ -2,21 +2,26 @@
 
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
+/**
+ * @template T
+ */
 interface AddressRange
 {
     public const MAX_ROW = 1048576;
 
     public const MAX_COLUMN = 'XFD';
 
-    /**
-     * @return mixed
-     */
-    public function from();
+    public const MAX_COLUMN_INT = 16384;
 
     /**
-     * @return mixed
+     * @return T
      */
-    public function to();
+    public function from(): mixed;
+
+    /**
+     * @return T
+     */
+    public function to(): mixed;
 
     public function __toString(): string;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
index 49631f7..4168645 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
@@ -3,7 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
-use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Calculation\Engine\FormattedNumber;
 use PhpOffice\PhpSpreadsheet\Shared\Date;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
@@ -15,10 +15,8 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
      *
      * @param Cell $cell Cell to bind value to
      * @param mixed $value Value to bind in cell
-     *
-     * @return bool
      */
-    public function bindValue(Cell $cell, $value = null)
+    public function bindValue(Cell $cell, mixed $value = null): bool
     {
         if ($value === null) {
             return parent::bindValue($cell, $value);
@@ -31,54 +29,44 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
         $dataType = parent::dataTypeForValue($value);
 
         // Style logic - strings
-        if ($dataType === DataType::TYPE_STRING && !$value instanceof RichText) {
+        if ($dataType === DataType::TYPE_STRING && is_string($value)) {
             //    Test for booleans using locale-setting
-            if ($value == Calculation::getTRUE()) {
+            if (StringHelper::strToUpper($value) === Calculation::getTRUE()) {
                 $cell->setValueExplicit(true, DataType::TYPE_BOOL);
 
                 return true;
-            } elseif ($value == Calculation::getFALSE()) {
+            } elseif (StringHelper::strToUpper($value) === Calculation::getFALSE()) {
                 $cell->setValueExplicit(false, DataType::TYPE_BOOL);
 
                 return true;
             }
 
             // Check for fractions
-            if (preg_match('/^([+-]?)\s*(\d+)\s?\/\s*(\d+)$/', $value, $matches)) {
+            if (preg_match('~^([+-]?)\s*(\d+)\s*/\s*(\d+)$~', $value, $matches)) {
                 return $this->setProperFraction($matches, $cell);
-            } elseif (preg_match('/^([+-]?)(\d*) +(\d*)\s?\/\s*(\d*)$/', $value, $matches)) {
+            } elseif (preg_match('~^([+-]?)(\d+)\s+(\d+)\s*/\s*(\d+)$~', $value, $matches)) {
                 return $this->setImproperFraction($matches, $cell);
             }
 
+            $decimalSeparatorNoPreg = StringHelper::getDecimalSeparator();
+            $decimalSeparator = preg_quote($decimalSeparatorNoPreg, '/');
+            $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
+
             // Check for percentage
-            if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $value)) {
-                return $this->setPercentage($value, $cell);
+            if (preg_match('/^\-?\d*' . $decimalSeparator . '?\d*\s?\%$/', (string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value))) {
+                return $this->setPercentage((string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $cell);
             }
 
             // Check for currency
-            $currencyCode = StringHelper::getCurrencyCode();
-            $decimalSeparator = StringHelper::getDecimalSeparator();
-            $thousandsSeparator = StringHelper::getThousandsSeparator();
-            if (preg_match('/^' . preg_quote($currencyCode, '/') . ' *(\d{1,3}(' . preg_quote($thousandsSeparator, '/') . '\d{3})*|(\d+))(' . preg_quote($decimalSeparator, '/') . '\d{2})?$/', $value)) {
+            if (preg_match(FormattedNumber::currencyMatcherRegexp(), (string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $matches, PREG_UNMATCHED_AS_NULL)) {
                 // Convert value to number
-                $value = (float) trim(str_replace([$currencyCode, $thousandsSeparator, $decimalSeparator], ['', '', '.'], $value));
-                $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
-                // Set style
-                $cell->getWorksheet()->getStyle($cell->getCoordinate())
-                    ->getNumberFormat()->setFormatCode(
-                        str_replace('$', $currencyCode, NumberFormat::FORMAT_CURRENCY_USD_SIMPLE)
-                    );
+                $sign = ($matches['PrefixedSign'] ?? $matches['PrefixedSign2'] ?? $matches['PostfixedSign']) ?? null;
+                $currencyCode = $matches['PrefixedCurrency'] ?? $matches['PostfixedCurrency'];
+                /** @var string */
+                $temp = str_replace([$decimalSeparatorNoPreg, $currencyCode, ' ', '-'], ['.', '', '', ''], (string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value));
+                $value = (float) ($sign . trim($temp));
 
-                return true;
-            } elseif (preg_match('/^\$ *(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/', $value)) {
-                // Convert value to number
-                $value = (float) trim(str_replace(['$', ','], '', $value));
-                $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
-                // Set style
-                $cell->getWorksheet()->getStyle($cell->getCoordinate())
-                    ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_CURRENCY_USD_SIMPLE);
-
-                return true;
+                return $this->setCurrency($value, $cell, $currencyCode ?? '');
             }
 
             // Check for time without seconds e.g. '9:45', '09:45'
@@ -96,7 +84,7 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
                 // Convert value to number
                 $cell->setValueExplicit($d, DataType::TYPE_NUMERIC);
                 // Determine style. Either there is a time part or not. Look for ':'
-                if (strpos($value, ':') !== false) {
+                if (str_contains($value, ':')) {
                     $formatCode = 'yyyy-mm-dd h:mm';
                 } else {
                     $formatCode = 'yyyy-mm-dd';
@@ -108,7 +96,7 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
             }
 
             // Check for newline character "\n"
-            if (strpos($value, "\n") !== false) {
+            if (str_contains($value, "\n")) {
                 $cell->setValueExplicit($value, DataType::TYPE_STRING);
                 // Set style
                 $cell->getWorksheet()->getStyle($cell->getCoordinate())
@@ -175,6 +163,18 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
         return true;
     }
 
+    protected function setCurrency(float $value, Cell $cell, string $currencyCode): bool
+    {
+        $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
+        // Set style
+        $cell->getWorksheet()->getStyle($cell->getCoordinate())
+            ->getNumberFormat()->setFormatCode(
+                str_replace('$', '[$' . $currencyCode . ']', NumberFormat::FORMAT_CURRENCY_USD)
+            );
+
+        return true;
+    }
+
     protected function setTimeHoursMinutes(string $value, Cell $cell): bool
     {
         // Convert value to number
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
index 58a39af..9ddafce 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
@@ -3,33 +3,33 @@
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Collection\Cells;
-use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\CellStyleAssessor;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
 use PhpOffice\PhpSpreadsheet\Style\Style;
 use PhpOffice\PhpSpreadsheet\Worksheet\Table;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use Stringable;
 
-class Cell
+class Cell implements Stringable
 {
     /**
      * Value binder to use.
-     *
-     * @var IValueBinder
      */
-    private static $valueBinder;
+    private static ?IValueBinder $valueBinder = null;
 
     /**
      * Value of the cell.
-     *
-     * @var mixed
      */
-    private $value;
+    private mixed $value;
 
     /**
      *    Calculated value of the cell (used for caching)
@@ -45,42 +45,36 @@ class Cell
 
     /**
      * Type of the cell data.
-     *
-     * @var string
      */
-    private $dataType;
+    private string $dataType;
 
     /**
      * The collection of cells that this cell belongs to (i.e. The Cell Collection for the parent Worksheet).
-     *
-     * @var ?Cells
      */
-    private $parent;
+    private ?Cells $parent;
 
     /**
      * Index to the cellXf reference for the styling of this cell.
-     *
-     * @var int
      */
-    private $xfIndex = 0;
+    private int $xfIndex = 0;
 
     /**
      * Attributes of the formula.
-     *
-     * @var mixed
      */
-    private $formulaAttributes;
+    private mixed $formulaAttributes = null;
+
+    private IgnoredErrors $ignoredErrors;
 
     /**
      * Update the cell into the cell collection.
      *
-     * @return $this
+     * @throws SpreadsheetException
      */
     public function updateInCollection(): self
     {
         $parent = $this->parent;
         if ($parent === null) {
-            throw new Exception('Cannot update when cell is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot update when cell is not bound to a worksheet');
         }
         $parent->update($this);
 
@@ -100,9 +94,9 @@ class Cell
     /**
      * Create a new Cell.
      *
-     * @param mixed $value
+     * @throws SpreadsheetException
      */
-    public function __construct($value, ?string $dataType, Worksheet $worksheet)
+    public function __construct(mixed $value, ?string $dataType, Worksheet $worksheet)
     {
         // Initialise cell value
         $this->value = $value;
@@ -117,20 +111,21 @@ class Cell
             }
             $this->dataType = $dataType;
         } elseif (self::getValueBinder()->bindValue($this, $value) === false) {
-            throw new Exception('Value could not be bound to cell.');
+            throw new SpreadsheetException('Value could not be bound to cell.');
         }
+        $this->ignoredErrors = new IgnoredErrors();
     }
 
     /**
      * Get cell coordinate column.
      *
-     * @return string
+     * @throws SpreadsheetException
      */
-    public function getColumn()
+    public function getColumn(): string
     {
         $parent = $this->parent;
         if ($parent === null) {
-            throw new Exception('Cannot get column when cell is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot get column when cell is not bound to a worksheet');
         }
 
         return $parent->getCurrentColumn();
@@ -139,13 +134,13 @@ class Cell
     /**
      * Get cell coordinate row.
      *
-     * @return int
+     * @throws SpreadsheetException
      */
-    public function getRow()
+    public function getRow(): int
     {
         $parent = $this->parent;
         if ($parent === null) {
-            throw new Exception('Cannot get row when cell is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot get row when cell is not bound to a worksheet');
         }
 
         return $parent->getCurrentRow();
@@ -154,9 +149,9 @@ class Cell
     /**
      * Get cell coordinate.
      *
-     * @return string
+     * @throws SpreadsheetException
      */
-    public function getCoordinate()
+    public function getCoordinate(): string
     {
         $parent = $this->parent;
         if ($parent !== null) {
@@ -165,7 +160,7 @@ class Cell
             $coordinate = null;
         }
         if ($coordinate === null) {
-            throw new Exception('Coordinate no longer exists');
+            throw new SpreadsheetException('Coordinate no longer exists');
         }
 
         return $coordinate;
@@ -173,32 +168,40 @@ class Cell
 
     /**
      * Get cell value.
-     *
-     * @return mixed
      */
-    public function getValue()
+    public function getValue(): mixed
     {
         return $this->value;
     }
 
+    public function getValueString(): string
+    {
+        $value = $this->value;
+
+        return ($value === '' || is_scalar($value) || $value instanceof Stringable) ? "$value" : '';
+    }
+
     /**
      * Get cell value with formatting.
      */
     public function getFormattedValue(): string
     {
-        return (string) NumberFormat::toFormattedString(
+        $currentCalendar = SharedDate::getExcelCalendar();
+        SharedDate::setExcelCalendar($this->getWorksheet()->getParent()?->getExcelCalendar());
+        $formattedValue = (string) NumberFormat::toFormattedString(
             $this->getCalculatedValue(),
-            (string) $this->getStyle()->getNumberFormat()->getFormatCode()
+            (string) $this->getStyle()->getNumberFormat()->getFormatCode(true)
         );
+        SharedDate::setExcelCalendar($currentCalendar);
+
+        return $formattedValue;
     }
 
-    /**
-     * @param mixed $oldValue
-     * @param mixed $newValue
-     */
-    protected static function updateIfCellIsTableHeader(Worksheet $workSheet, self $cell, $oldValue, $newValue): void
+    protected static function updateIfCellIsTableHeader(?Worksheet $workSheet, self $cell, mixed $oldValue, mixed $newValue): void
     {
-        if (StringHelper::strToLower($oldValue ?? '') === StringHelper::strToLower($newValue ?? '')) {
+        $oldValue = (is_scalar($oldValue) || $oldValue instanceof Stringable) ? ((string) $oldValue) : null;
+        $newValue = (is_scalar($newValue) || $newValue instanceof Stringable) ? ((string) $newValue) : null;
+        if (StringHelper::strToLower($oldValue ?? '') === StringHelper::strToLower($newValue ?? '') || $workSheet === null) {
             return;
         }
 
@@ -221,13 +224,15 @@ class Cell
      *    Sets the value for a cell, automatically determining the datatype using the value binder
      *
      * @param mixed $value Value
+     * @param null|IValueBinder $binder Value Binder to override the currently set Value Binder
      *
-     * @return $this
+     * @throws SpreadsheetException
      */
-    public function setValue($value): self
+    public function setValue(mixed $value, ?IValueBinder $binder = null): self
     {
-        if (!self::getValueBinder()->bindValue($this, $value)) {
-            throw new Exception('Value could not be bound to cell.');
+        $binder ??= self::getValueBinder();
+        if (!$binder->bindValue($this, $value)) {
+            throw new SpreadsheetException('Value could not be bound to cell.');
         }
 
         return $this;
@@ -244,11 +249,12 @@ class Cell
      *       If you do mismatch value and datatype, then the value you enter may be changed to match the datatype
      *          that you specify.
      *
-     * @return Cell
+     * @throws SpreadsheetException
      */
-    public function setValueExplicit($value, string $dataType = DataType::TYPE_STRING)
+    public function setValueExplicit(mixed $value, string $dataType = DataType::TYPE_STRING): self
     {
         $oldValue = $this->value;
+        $quotePrefix = false;
 
         // set the value according to data type
         switch ($dataType) {
@@ -261,19 +267,29 @@ class Cell
                 // no break
             case DataType::TYPE_STRING:
                 // Synonym for string
+                if (is_string($value) && strlen($value) > 1 && $value[0] === '=') {
+                    $quotePrefix = true;
+                }
+                // no break
             case DataType::TYPE_INLINE:
                 // Rich text
-                $this->value = DataType::checkString($value);
+                if ($value !== null && !is_scalar($value) && !($value instanceof Stringable)) {
+                    throw new SpreadsheetException('Invalid unstringable value for datatype Inline/String/String2');
+                }
+                $this->value = DataType::checkString(($value instanceof RichText) ? $value : ((string) $value));
 
                 break;
             case DataType::TYPE_NUMERIC:
                 if (is_string($value) && !is_numeric($value)) {
-                    throw new Exception('Invalid numeric value for datatype Numeric');
+                    throw new SpreadsheetException('Invalid numeric value for datatype Numeric');
                 }
                 $this->value = 0 + $value;
 
                 break;
             case DataType::TYPE_FORMULA:
+                if ($value !== null && !is_scalar($value) && !($value instanceof Stringable)) {
+                    throw new SpreadsheetException('Invalid unstringable value for datatype Formula');
+                }
                 $this->value = (string) $value;
 
                 break;
@@ -291,7 +307,7 @@ class Cell
 
                 break;
             default:
-                throw new Exception('Invalid datatype: ' . $dataType);
+                throw new SpreadsheetException('Invalid datatype: ' . $dataType);
         }
 
         // set the datatype
@@ -299,45 +315,36 @@ class Cell
 
         $this->updateInCollection();
         $cellCoordinate = $this->getCoordinate();
-        self::updateIfCellIsTableHeader($this->getParent()->getParent(), $this, $oldValue, $value); // @phpstan-ignore-line
+        self::updateIfCellIsTableHeader($this->getParent()?->getParent(), $this, $oldValue, $value);
+        $this->getWorksheet()->applyStylesFromArray($cellCoordinate, ['quotePrefix' => $quotePrefix]);
 
-        return $this->getParent()->get($cellCoordinate); // @phpstan-ignore-line
+        return $this->getParent()?->get($cellCoordinate) ?? $this;
     }
 
     public const CALCULATE_DATE_TIME_ASIS = 0;
     public const CALCULATE_DATE_TIME_FLOAT = 1;
     public const CALCULATE_TIME_FLOAT = 2;
 
-    /** @var int */
-    private static $calculateDateTimeType = self::CALCULATE_DATE_TIME_ASIS;
+    private static int $calculateDateTimeType = self::CALCULATE_DATE_TIME_ASIS;
 
     public static function getCalculateDateTimeType(): int
     {
         return self::$calculateDateTimeType;
     }
 
+    /** @throws CalculationException */
     public static function setCalculateDateTimeType(int $calculateDateTimeType): void
     {
-        switch ($calculateDateTimeType) {
-            case self::CALCULATE_DATE_TIME_ASIS:
-            case self::CALCULATE_DATE_TIME_FLOAT:
-            case self::CALCULATE_TIME_FLOAT:
-                self::$calculateDateTimeType = $calculateDateTimeType;
-
-                break;
-            default:
-                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception("Invalid value $calculateDateTimeType for calculated date time type");
-        }
+        self::$calculateDateTimeType = match ($calculateDateTimeType) {
+            self::CALCULATE_DATE_TIME_ASIS, self::CALCULATE_DATE_TIME_FLOAT, self::CALCULATE_TIME_FLOAT => $calculateDateTimeType,
+            default => throw new CalculationException("Invalid value $calculateDateTimeType for calculated date time type"),
+        };
     }
 
     /**
      * Convert date, time, or datetime from int to float if desired.
-     *
-     * @param mixed $result
-     *
-     * @return mixed
      */
-    private function convertDateTimeInt($result)
+    private function convertDateTimeInt(mixed $result): mixed
     {
         if (is_int($result)) {
             if (self::$calculateDateTimeType === self::CALCULATE_TIME_FLOAT) {
@@ -354,44 +361,60 @@ class Cell
         return $result;
     }
 
+    /**
+     * Get calculated cell value converted to string.
+     */
+    public function getCalculatedValueString(): string
+    {
+        $value = $this->getCalculatedValue();
+
+        return ($value === '' || is_scalar($value) || $value instanceof Stringable) ? "$value" : '';
+    }
+
     /**
      * Get calculated cell value.
      *
      * @param bool $resetLog Whether the calculation engine logger should be reset or not
      *
-     * @return mixed
+     * @throws CalculationException
      */
-    public function getCalculatedValue(bool $resetLog = true)
+    public function getCalculatedValue(bool $resetLog = true): mixed
     {
         if ($this->dataType === DataType::TYPE_FORMULA) {
             try {
-                $index = $this->getWorksheet()->getParent()->getActiveSheetIndex();
+                $currentCalendar = SharedDate::getExcelCalendar();
+                SharedDate::setExcelCalendar($this->getWorksheet()->getParent()?->getExcelCalendar());
+                $index = $this->getWorksheet()->getParentOrThrow()->getActiveSheetIndex();
                 $selected = $this->getWorksheet()->getSelectedCells();
                 $result = Calculation::getInstance(
                     $this->getWorksheet()->getParent()
                 )->calculateCellValue($this, $resetLog);
                 $result = $this->convertDateTimeInt($result);
                 $this->getWorksheet()->setSelectedCells($selected);
-                $this->getWorksheet()->getParent()->setActiveSheetIndex($index);
+                $this->getWorksheet()->getParentOrThrow()->setActiveSheetIndex($index);
                 //    We don't yet handle array returns
                 if (is_array($result)) {
                     while (is_array($result)) {
                         $result = array_shift($result);
                     }
                 }
-            } catch (Exception $ex) {
+            } catch (SpreadsheetException $ex) {
+                SharedDate::setExcelCalendar($currentCalendar);
                 if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
                     return $this->calculatedValue; // Fallback for calculations referencing external files.
                 } elseif (preg_match('/[Uu]ndefined (name|offset: 2|array key 2)/', $ex->getMessage()) === 1) {
                     return ExcelError::NAME();
                 }
 
-                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(
-                    $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage()
+                throw new CalculationException(
+                    $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage(),
+                    $ex->getCode(),
+                    $ex
                 );
             }
+            SharedDate::setExcelCalendar($currentCalendar);
 
-            if ($result === '#Not Yet Implemented') {
+            if ($result === Functions::NOT_YET_IMPLEMENTED) {
                 return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
             }
 
@@ -408,10 +431,10 @@ class Cell
      *
      * @param mixed $originalValue Value
      */
-    public function setCalculatedValue($originalValue): self
+    public function setCalculatedValue(mixed $originalValue, bool $tryNumeric = true): self
     {
         if ($originalValue !== null) {
-            $this->calculatedValue = (is_numeric($originalValue)) ? (float) $originalValue : $originalValue;
+            $this->calculatedValue = ($tryNumeric && is_numeric($originalValue)) ? (0 + $originalValue) : $originalValue;
         }
 
         return $this->updateInCollection();
@@ -424,10 +447,8 @@ class Cell
      *    Note that this value is not guaranteed to reflect the actual calculated value because it is
      *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
      *        values used by the formula have changed since it was last calculated.
-     *
-     * @return mixed
      */
-    public function getOldCalculatedValue()
+    public function getOldCalculatedValue(): mixed
     {
         return $this->calculatedValue;
     }
@@ -445,14 +466,11 @@ class Cell
      *
      * @param string $dataType see DataType::TYPE_*
      */
-    public function setDataType($dataType): self
+    public function setDataType(string $dataType): self
     {
-        if ($dataType == DataType::TYPE_STRING2) {
-            $dataType = DataType::TYPE_STRING;
-        }
-        $this->dataType = $dataType;
+        $this->setValueExplicit($this->value, $dataType);
 
-        return $this->updateInCollection();
+        return $this;
     }
 
     /**
@@ -465,11 +483,13 @@ class Cell
 
     /**
      *    Does this cell contain Data validation rules?
+     *
+     * @throws SpreadsheetException
      */
     public function hasDataValidation(): bool
     {
         if (!isset($this->parent)) {
-            throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot check for data validation when cell is not bound to a worksheet');
         }
 
         return $this->getWorksheet()->dataValidationExists($this->getCoordinate());
@@ -477,11 +497,13 @@ class Cell
 
     /**
      * Get Data validation rules.
+     *
+     * @throws SpreadsheetException
      */
     public function getDataValidation(): DataValidation
     {
         if (!isset($this->parent)) {
-            throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot get data validation for cell that is not bound to a worksheet');
         }
 
         return $this->getWorksheet()->getDataValidation($this->getCoordinate());
@@ -489,11 +511,13 @@ class Cell
 
     /**
      * Set Data validation rules.
+     *
+     * @throws SpreadsheetException
      */
     public function setDataValidation(?DataValidation $dataValidation = null): self
     {
         if (!isset($this->parent)) {
-            throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot set data validation for cell that is not bound to a worksheet');
         }
 
         $this->getWorksheet()->setDataValidation($this->getCoordinate(), $dataValidation);
@@ -513,11 +537,13 @@ class Cell
 
     /**
      * Does this cell contain a Hyperlink?
+     *
+     * @throws SpreadsheetException
      */
     public function hasHyperlink(): bool
     {
         if (!isset($this->parent)) {
-            throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot check for hyperlink when cell is not bound to a worksheet');
         }
 
         return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());
@@ -525,11 +551,13 @@ class Cell
 
     /**
      * Get Hyperlink.
+     *
+     * @throws SpreadsheetException
      */
     public function getHyperlink(): Hyperlink
     {
         if (!isset($this->parent)) {
-            throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot get hyperlink for cell that is not bound to a worksheet');
         }
 
         return $this->getWorksheet()->getHyperlink($this->getCoordinate());
@@ -537,11 +565,13 @@ class Cell
 
     /**
      * Set Hyperlink.
+     *
+     * @throws SpreadsheetException
      */
     public function setHyperlink(?Hyperlink $hyperlink = null): self
     {
         if (!isset($this->parent)) {
-            throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
+            throw new SpreadsheetException('Cannot set hyperlink for cell that is not bound to a worksheet');
         }
 
         $this->getWorksheet()->setHyperlink($this->getCoordinate(), $hyperlink);
@@ -551,16 +581,16 @@ class Cell
 
     /**
      * Get cell collection.
-     *
-     * @return ?Cells
      */
-    public function getParent()
+    public function getParent(): ?Cells
     {
         return $this->parent;
     }
 
     /**
      * Get parent worksheet.
+     *
+     * @throws SpreadsheetException
      */
     public function getWorksheet(): Worksheet
     {
@@ -572,7 +602,7 @@ class Cell
         }
 
         if ($worksheet === null) {
-            throw new Exception('Worksheet no longer exists');
+            throw new SpreadsheetException('Worksheet no longer exists');
         }
 
         return $worksheet;
@@ -679,8 +709,8 @@ class Cell
         $myRow = $this->getRow();
 
         // Verify if cell is in range
-        return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&
-                ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);
+        return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn)
+            && ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);
     }
 
     /**
@@ -760,11 +790,9 @@ class Cell
     /**
      * Set the formula attributes.
      *
-     * @param mixed $attributes
-     *
      * @return $this
      */
-    public function setFormulaAttributes($attributes): self
+    public function setFormulaAttributes(mixed $attributes): self
     {
         $this->formulaAttributes = $attributes;
 
@@ -773,21 +801,49 @@ class Cell
 
     /**
      * Get the formula attributes.
-     *
-     * @return mixed
      */
-    public function getFormulaAttributes()
+    public function getFormulaAttributes(): mixed
     {
         return $this->formulaAttributes;
     }
 
     /**
      * Convert to string.
-     *
-     * @return string
      */
-    public function __toString()
+    public function __toString(): string
     {
-        return (string) $this->getValue();
+        $retVal = $this->value;
+
+        return ($retVal === null || is_scalar($retVal) || $retVal instanceof Stringable) ? ((string) $retVal) : '';
+    }
+
+    public function getIgnoredErrors(): IgnoredErrors
+    {
+        return $this->ignoredErrors;
+    }
+
+    public function isLocked(): bool
+    {
+        $protected = $this->parent?->getParent()?->getProtection()?->getSheet();
+        if ($protected !== true) {
+            return false;
+        }
+        $locked = $this->getStyle()->getProtection()->getLocked();
+
+        return $locked !== Protection::PROTECTION_UNPROTECTED;
+    }
+
+    public function isHiddenOnFormulaBar(): bool
+    {
+        if ($this->getDataType() !== DataType::TYPE_FORMULA) {
+            return false;
+        }
+        $protected = $this->parent?->getParent()?->getProtection()?->getSheet();
+        if ($protected !== true) {
+            return false;
+        }
+        $hidden = $this->getStyle()->getProtection()->getHidden();
+
+        return $hidden !== Protection::PROTECTION_UNPROTECTED;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php
index a0c544f..ab6258e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellAddress.php
@@ -4,33 +4,19 @@ namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use PhpOffice\PhpSpreadsheet\Exception;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use Stringable;
 
-class CellAddress
+class CellAddress implements Stringable
 {
-    /**
-     * @var ?Worksheet
-     */
-    protected $worksheet;
+    protected ?Worksheet $worksheet;
 
-    /**
-     * @var string
-     */
-    protected $cellAddress;
+    protected string $cellAddress;
 
-    /**
-     * @var string
-     */
-    protected $columnName;
+    protected string $columnName = '';
 
-    /**
-     * @var int
-     */
-    protected $columnId;
+    protected int $columnId;
 
-    /**
-     * @var int
-     */
-    protected $rowId;
+    protected int $rowId;
 
     public function __construct(string $cellAddress, ?Worksheet $worksheet = null)
     {
@@ -39,43 +25,39 @@ class CellAddress
         $this->worksheet = $worksheet;
     }
 
+    public function __destruct()
+    {
+        unset($this->worksheet);
+    }
+
     /**
-     * @param mixed $columnId
-     * @param mixed $rowId
+     * @phpstan-assert int|numeric-string $columnId
+     * @phpstan-assert int|numeric-string $rowId
      */
-    private static function validateColumnAndRow($columnId, $rowId): void
+    private static function validateColumnAndRow(int|string $columnId, int|string $rowId): void
     {
         if (!is_numeric($columnId) || $columnId <= 0 || !is_numeric($rowId) || $rowId <= 0) {
             throw new Exception('Row and Column Ids must be positive integer values');
         }
     }
 
-    /**
-     * @param mixed $columnId
-     * @param mixed $rowId
-     */
-    public static function fromColumnAndRow($columnId, $rowId, ?Worksheet $worksheet = null): self
+    public static function fromColumnAndRow(int|string $columnId, int|string $rowId, ?Worksheet $worksheet = null): self
     {
         self::validateColumnAndRow($columnId, $rowId);
 
-        /** @phpstan-ignore-next-line */
-        return new static(Coordinate::stringFromColumnIndex($columnId) . ((string) $rowId), $worksheet);
+        return new self(Coordinate::stringFromColumnIndex($columnId) . $rowId, $worksheet);
     }
 
     public static function fromColumnRowArray(array $array, ?Worksheet $worksheet = null): self
     {
         [$columnId, $rowId] = $array;
 
-        return static::fromColumnAndRow($columnId, $rowId, $worksheet);
+        return self::fromColumnAndRow($columnId, $rowId, $worksheet);
     }
 
-    /**
-     * @param mixed $cellAddress
-     */
-    public static function fromCellAddress($cellAddress, ?Worksheet $worksheet = null): self
+    public static function fromCellAddress(string $cellAddress, ?Worksheet $worksheet = null): self
     {
-        /** @phpstan-ignore-next-line */
-        return new static($cellAddress, $worksheet);
+        return new self($cellAddress, $worksheet);
     }
 
     /**
@@ -131,7 +113,7 @@ class CellAddress
             $newRowId = 1;
         }
 
-        return static::fromColumnAndRow($this->columnId, $newRowId);
+        return self::fromColumnAndRow($this->columnId, $newRowId);
     }
 
     public function previousRow(int $offset = 1): self
@@ -146,7 +128,7 @@ class CellAddress
             $newColumnId = 1;
         }
 
-        return static::fromColumnAndRow($newColumnId, $this->rowId);
+        return self::fromColumnAndRow($newColumnId, $this->rowId);
     }
 
     public function previousColumn(int $offset = 1): self
@@ -159,7 +141,7 @@ class CellAddress
      *     (ie. if a Worksheet was provided to the constructor).
      *     e.g. "'Mark''s Worksheet'!C5".
      */
-    public function __toString()
+    public function __toString(): string
     {
         return $this->fullCellAddress();
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php
index 908a0d0..677009d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/CellRange.php
@@ -4,18 +4,16 @@ namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use PhpOffice\PhpSpreadsheet\Exception;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use Stringable;
 
-class CellRange implements AddressRange
+/**
+ * @implements AddressRange<CellAddress>
+ */
+class CellRange implements AddressRange, Stringable
 {
-    /**
-     * @var CellAddress
-     */
-    protected $from;
+    protected CellAddress $from;
 
-    /**
-     * @var CellAddress
-     */
-    protected $to;
+    protected CellAddress $to;
 
     public function __construct(CellAddress $from, CellAddress $to)
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php
index 1e521a1..7f32a05 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/ColumnRange.php
@@ -3,23 +3,18 @@
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use Stringable;
 
-class ColumnRange implements AddressRange
+/**
+ * @implements AddressRange<string>
+ */
+class ColumnRange implements AddressRange, Stringable
 {
-    /**
-     * @var ?Worksheet
-     */
-    protected $worksheet;
+    protected ?Worksheet $worksheet;
 
-    /**
-     * @var int
-     */
-    protected $from;
+    protected int $from;
 
-    /**
-     * @var int
-     */
-    protected $to;
+    protected int $to;
 
     public function __construct(string $from, ?string $to = null, ?Worksheet $worksheet = null)
     {
@@ -30,6 +25,11 @@ class ColumnRange implements AddressRange
         $this->worksheet = $worksheet;
     }
 
+    public function __destruct()
+    {
+        $this->worksheet = null;
+    }
+
     public static function fromColumnIndexes(int $from, int $to, ?Worksheet $worksheet = null): self
     {
         return new self(Coordinate::stringFromColumnIndex($from), Coordinate::stringFromColumnIndex($to), $worksheet);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
index 89b4768..313cc3d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
@@ -2,7 +2,6 @@
 
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Exception;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
@@ -15,6 +14,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 abstract class Coordinate
 {
     public const A1_COORDINATE_REGEX = '/^(?<col>\$?[A-Z]{1,3})(?<row>\$?\d{1,7})$/i';
+    public const FULL_REFERENCE_REGEX = '/^(?:(?<worksheet>[^!]*)!)?(?<localReference>(?<firstCoordinate>[$]?[A-Z]{1,3}[$]?\d{1,7})(?:\:(?<secondCoordinate>[$]?[A-Z]{1,3}[$]?\d{1,7}))?)$/i';
 
     /**
      * Default range variable constant.
@@ -30,7 +30,7 @@ abstract class Coordinate
      *
      * @return array{0: string, 1: string} Array containing column and row (indexes 0 and 1)
      */
-    public static function coordinateFromString($cellAddress): array
+    public static function coordinateFromString(string $cellAddress): array
     {
         if (preg_match(self::A1_COORDINATE_REGEX, $cellAddress, $matches)) {
             return [$matches['col'], $matches['row']];
@@ -69,21 +69,22 @@ abstract class Coordinate
      *
      * @return bool Whether the coordinate represents a range of cells
      */
-    public static function coordinateIsRange($cellAddress)
+    public static function coordinateIsRange(string $cellAddress): bool
     {
-        return (strpos($cellAddress, ':') !== false) || (strpos($cellAddress, ',') !== false);
+        return str_contains($cellAddress, ':') || str_contains($cellAddress, ',');
     }
 
     /**
      * Make string row, column or cell coordinate absolute.
      *
-     * @param string $cellAddress e.g. 'A' or '1' or 'A1'
+     * @param int|string $cellAddress e.g. 'A' or '1' or 'A1'
      *                    Note that this value can be a row or column reference as well as a cell reference
      *
      * @return string Absolute coordinate        e.g. '$A' or '$1' or '$A$1'
      */
-    public static function absoluteReference($cellAddress)
+    public static function absoluteReference(int|string $cellAddress): string
     {
+        $cellAddress = (string) $cellAddress;
         if (self::coordinateIsRange($cellAddress)) {
             throw new Exception('Cell coordinate string can not be a range of cells');
         }
@@ -112,7 +113,7 @@ abstract class Coordinate
      *
      * @return string Absolute coordinate        e.g. '$A$1'
      */
-    public static function absoluteCoordinate($cellAddress)
+    public static function absoluteCoordinate(string $cellAddress): string
     {
         if (self::coordinateIsRange($cellAddress)) {
             throw new Exception('Cell coordinate string can not be a range of cells');
@@ -125,7 +126,7 @@ abstract class Coordinate
         }
 
         // Create absolute coordinate
-        [$column, $row] = self::coordinateFromString($cellAddress);
+        [$column, $row] = self::coordinateFromString($cellAddress ?? 'A1');
         $column = ltrim($column, '$');
         $row = ltrim($row, '$');
 
@@ -141,7 +142,7 @@ abstract class Coordinate
      *                                e.g. ['B4','D9'] or [['B4','D9'], ['H2','O11']]
      *                                        or ['B4']
      */
-    public static function splitRange($range)
+    public static function splitRange(string $range): array
     {
         // Ensure $pRange is a valid range
         if (empty($range)) {
@@ -149,13 +150,12 @@ abstract class Coordinate
         }
 
         $exploded = explode(',', $range);
-        $counter = count($exploded);
-        for ($i = 0; $i < $counter; ++$i) {
-            // @phpstan-ignore-next-line
-            $exploded[$i] = explode(':', $exploded[$i]);
+        $outArray = [];
+        foreach ($exploded as $value) {
+            $outArray[] = explode(':', $value);
         }
 
-        return $exploded;
+        return $outArray;
     }
 
     /**
@@ -165,7 +165,7 @@ abstract class Coordinate
      *
      * @return string String representation of $pRange
      */
-    public static function buildRange(array $range)
+    public static function buildRange(array $range): string
     {
         // Verify range
         if (empty($range) || !is_array($range[0])) {
@@ -200,7 +200,7 @@ abstract class Coordinate
         $range = strtoupper($range);
 
         // Extract range
-        if (strpos($range, ':') === false) {
+        if (!str_contains($range, ':')) {
             $rangeA = $rangeB = $range;
         } else {
             [$rangeA, $rangeB] = explode(':', $range);
@@ -234,7 +234,7 @@ abstract class Coordinate
      *
      * @return array Range dimension (width, height)
      */
-    public static function rangeDimension($range)
+    public static function rangeDimension(string $range): array
     {
         // Calculate range outer borders
         [$rangeStart, $rangeEnd] = self::rangeBoundaries($range);
@@ -250,7 +250,7 @@ abstract class Coordinate
      * @return array Range coordinates [Start Cell, End Cell]
      *                    where Start Cell and End Cell are arrays [Column ID, Row Number]
      */
-    public static function getRangeBoundaries($range)
+    public static function getRangeBoundaries(string $range): array
     {
         [$rangeA, $rangeB] = self::rangeBoundaries($range);
 
@@ -260,19 +260,105 @@ abstract class Coordinate
         ];
     }
 
+    /**
+     * Check if cell or range reference is valid and return an array with type of reference (cell or range), worksheet (if it was given)
+     * and the coordinate or the first coordinate and second coordinate if it is a range.
+     *
+     * @param string $reference Coordinate or Range (e.g. A1:A1, B2, B:C, 2:3)
+     *
+     * @return array reference data
+     */
+    private static function validateReferenceAndGetData($reference): array
+    {
+        $data = [];
+        preg_match(self::FULL_REFERENCE_REGEX, $reference, $matches);
+        if (count($matches) === 0) {
+            return ['type' => 'invalid'];
+        }
+
+        if (isset($matches['secondCoordinate'])) {
+            $data['type'] = 'range';
+            $data['firstCoordinate'] = str_replace('$', '', $matches['firstCoordinate']);
+            $data['secondCoordinate'] = str_replace('$', '', $matches['secondCoordinate']);
+        } else {
+            $data['type'] = 'coordinate';
+            $data['coordinate'] = str_replace('$', '', $matches['firstCoordinate']);
+        }
+
+        $worksheet = $matches['worksheet'];
+        if ($worksheet !== '') {
+            if (substr($worksheet, 0, 1) === "'" && substr($worksheet, -1, 1) === "'") {
+                $worksheet = substr($worksheet, 1, -1);
+            }
+            $data['worksheet'] = strtolower($worksheet);
+        }
+        $data['localReference'] = str_replace('$', '', $matches['localReference']);
+
+        return $data;
+    }
+
+    /**
+     * Check if coordinate is inside a range.
+     *
+     * @param string $range Cell range, Single Cell, Row/Column Range (e.g. A1:A1, B2, B:C, 2:3)
+     * @param string $coordinate Cell coordinate (e.g. A1)
+     *
+     * @return bool true if coordinate is inside range
+     */
+    public static function coordinateIsInsideRange(string $range, string $coordinate): bool
+    {
+        $rangeData = self::validateReferenceAndGetData($range);
+        if ($rangeData['type'] === 'invalid') {
+            throw new Exception('First argument needs to be a range');
+        }
+
+        $coordinateData = self::validateReferenceAndGetData($coordinate);
+        if ($coordinateData['type'] === 'invalid') {
+            throw new Exception('Second argument needs to be a single coordinate');
+        }
+
+        if (isset($coordinateData['worksheet']) && !isset($rangeData['worksheet'])) {
+            return false;
+        }
+        if (!isset($coordinateData['worksheet']) && isset($rangeData['worksheet'])) {
+            return false;
+        }
+
+        if (isset($coordinateData['worksheet'], $rangeData['worksheet'])) {
+            if ($coordinateData['worksheet'] !== $rangeData['worksheet']) {
+                return false;
+            }
+        }
+
+        $boundaries = self::rangeBoundaries($rangeData['localReference']);
+        $coordinates = self::indexesFromString($coordinateData['localReference']);
+
+        $columnIsInside = $boundaries[0][0] <= $coordinates[0] && $coordinates[0] <= $boundaries[1][0];
+        if (!$columnIsInside) {
+            return false;
+        }
+        $rowIsInside = $boundaries[0][1] <= $coordinates[1] && $coordinates[1] <= $boundaries[1][1];
+        if (!$rowIsInside) {
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * Column index from string.
      *
-     * @param string $columnAddress eg 'A'
+     * @param ?string $columnAddress eg 'A'
      *
      * @return int Column index (A = 1)
      */
-    public static function columnIndexFromString($columnAddress)
+    public static function columnIndexFromString(?string $columnAddress): int
     {
         //    Using a lookup cache adds a slight memory overhead, but boosts speed
         //    caching using a static within the method is faster than a class static,
         //        though it's additional memory overhead
         static $indexCache = [];
+        $columnAddress = $columnAddress ?? '';
 
         if (isset($indexCache[$columnAddress])) {
             return $indexCache[$columnAddress];
@@ -318,11 +404,9 @@ abstract class Coordinate
     /**
      * String from column index.
      *
-     * @param int $columnIndex Column index (A = 1)
-     *
-     * @return string
+     * @param int|numeric-string $columnIndex Column index (A = 1)
      */
-    public static function stringFromColumnIndex($columnIndex)
+    public static function stringFromColumnIndex(int|string $columnIndex): string
     {
         static $indexCache = [];
         static $lookupCache = ' ABCDEFGHIJKLMNOPQRSTUVWXYZ';
@@ -348,7 +432,7 @@ abstract class Coordinate
      *
      * @return array Array containing single cell references
      */
-    public static function extractAllCellReferencesInRange($cellRange): array
+    public static function extractAllCellReferencesInRange(string $cellRange): array
     {
         if (substr_count($cellRange, '!') > 1) {
             throw new Exception('3-D Range References are not supported');
@@ -356,14 +440,14 @@ abstract class Coordinate
 
         [$worksheet, $cellRange] = Worksheet::extractSheetTitle($cellRange, true);
         $quoted = '';
-        if ($worksheet > '') {
+        if ($worksheet) {
             $quoted = Worksheet::nameRequiresQuotes($worksheet) ? "'" : '';
-            if (substr($worksheet, 0, 1) === "'" && substr($worksheet, -1, 1) === "'") {
+            if (str_starts_with($worksheet, "'") && str_ends_with($worksheet, "'")) {
                 $worksheet = substr($worksheet, 1, -1);
             }
             $worksheet = str_replace("'", "''", $worksheet);
         }
-        [$ranges, $operators] = self::getCellBlocksFromRangeString($cellRange);
+        [$ranges, $operators] = self::getCellBlocksFromRangeString($cellRange ?? 'A1');
 
         $cells = [];
         foreach ($ranges as $range) {
@@ -379,9 +463,7 @@ abstract class Coordinate
         $cellList = array_merge(...$cells);
 
         return array_map(
-            function ($cellAddress) use ($worksheet, $quoted) {
-                return ($worksheet !== '') ? "{$quoted}{$worksheet}{$quoted}!{$cellAddress}" : $cellAddress;
-            },
+            fn ($cellAddress) => ($worksheet !== '') ? "{$quoted}{$worksheet}{$quoted}!{$cellAddress}" : $cellAddress,
             self::sortCellReferenceArray($cellList)
         );
     }
@@ -422,6 +504,44 @@ abstract class Coordinate
         return array_values($sortKeys);
     }
 
+    /**
+     * Get all cell references applying union and intersection.
+     *
+     * @param string $cellBlock A cell range e.g. A1:B5,D1:E5 B2:C4
+     *
+     * @return string A string without intersection operator.
+     *   If there was no intersection to begin with, return original argument.
+     *   Otherwise, return cells and/or cell ranges in that range separated by comma.
+     */
+    public static function resolveUnionAndIntersection(string $cellBlock, string $implodeCharacter = ','): string
+    {
+        $cellBlock = preg_replace('/  +/', ' ', trim($cellBlock)) ?? $cellBlock;
+        $cellBlock = preg_replace('/ ,/', ',', $cellBlock) ?? $cellBlock;
+        $cellBlock = preg_replace('/, /', ',', $cellBlock) ?? $cellBlock;
+        $array1 = [];
+        $blocks = explode(',', $cellBlock);
+        foreach ($blocks as $block) {
+            $block0 = explode(' ', $block);
+            if (count($block0) === 1) {
+                $array1 = array_merge($array1, $block0);
+            } else {
+                $blockIdx = -1;
+                $array2 = [];
+                foreach ($block0 as $block00) {
+                    ++$blockIdx;
+                    if ($blockIdx === 0) {
+                        $array2 = self::getReferencesForCellBlock($block00);
+                    } else {
+                        $array2 = array_intersect($array2, self::getReferencesForCellBlock($block00));
+                    }
+                }
+                $array1 = array_merge($array1, $array2);
+            }
+        }
+
+        return implode($implodeCharacter, $array1);
+    }
+
     /**
      * Get all cell references for an individual cell block.
      *
@@ -429,7 +549,7 @@ abstract class Coordinate
      *
      * @return array All individual cells in that range
      */
-    private static function getReferencesForCellBlock($cellBlock)
+    private static function getReferencesForCellBlock(string $cellBlock): array
     {
         $returnValue = [];
 
@@ -460,7 +580,7 @@ abstract class Coordinate
             $currentColumnIndex = $startColumnIndex;
             $currentRow = $startRow;
 
-            self::validateRange($cellBlock, $startColumnIndex, $endColumnIndex, $currentRow, $endRow);
+            self::validateRange($cellBlock, $startColumnIndex, $endColumnIndex, (int) $currentRow, (int) $endRow);
 
             // Loop cells
             while ($currentColumnIndex < $endColumnIndex) {
@@ -493,7 +613,7 @@ abstract class Coordinate
      *
      * @return array associative array mapping coordinate ranges to valuea
      */
-    public static function mergeRangesInCollection(array $coordinateCollection)
+    public static function mergeRangesInCollection(array $coordinateCollection): array
     {
         $hashedValues = [];
         $mergedCoordCollection = [];
@@ -507,7 +627,7 @@ abstract class Coordinate
 
             [$column, $row] = self::coordinateFromString($coord);
             $row = (int) (ltrim($row, '$'));
-            $hashCode = $column . '-' . (is_object($value) ? $value->getHashCode() : $value);
+            $hashCode = $column . '-' . ((is_object($value) && method_exists($value, 'getHashCode')) ? $value->getHashCode() : $value);
 
             if (!isset($hashedValues[$hashCode])) {
                 $hashedValues[$hashCode] = (object) [
@@ -566,17 +686,14 @@ abstract class Coordinate
      * Get the individual cell blocks from a range string, removing any $ characters.
      *      then splitting by operators and returning an array with ranges and operators.
      *
-     * @param string $rangeString
-     *
      * @return array[]
      */
-    private static function getCellBlocksFromRangeString($rangeString)
+    private static function getCellBlocksFromRangeString(string $rangeString): array
     {
         $rangeString = str_replace('$', '', strtoupper($rangeString));
 
         // split range sets on intersection (space) or union (,) operators
-        $tokens = preg_split('/([ ,])/', $rangeString, -1, PREG_SPLIT_DELIM_CAPTURE);
-        /** @phpstan-ignore-next-line */
+        $tokens = preg_split('/([ ,])/', $rangeString, -1, PREG_SPLIT_DELIM_CAPTURE) ?: [];
         $split = array_chunk($tokens, 2);
         $ranges = array_column($split, 0);
         $operators = array_column($split, 1);
@@ -589,12 +706,8 @@ abstract class Coordinate
      * row.
      *
      * @param string $cellBlock The original range, for displaying a meaningful error message
-     * @param int $startColumnIndex
-     * @param int $endColumnIndex
-     * @param int $currentRow
-     * @param int $endRow
      */
-    private static function validateRange($cellBlock, $startColumnIndex, $endColumnIndex, $currentRow, $endRow): void
+    private static function validateRange(string $cellBlock, int $startColumnIndex, int $endColumnIndex, int $currentRow, int $endRow): void
     {
         if ($startColumnIndex >= $endColumnIndex || $currentRow > $endRow) {
             throw new Exception('Invalid range: "' . $cellBlock . '"');
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php
index f19984d..a213725 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataType.php
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use Stringable;
 
 class DataType
 {
@@ -23,7 +24,7 @@ class DataType
      *
      * @var array<string, int>
      */
-    private static $errorCodes = [
+    private static array $errorCodes = [
         '#NULL!' => 0,
         '#DIV/0!' => 1,
         '#VALUE!' => 2,
@@ -41,7 +42,7 @@ class DataType
      *
      * @return array<string, int>
      */
-    public static function getErrorCodes()
+    public static function getErrorCodes(): array
     {
         return self::$errorCodes;
     }
@@ -53,7 +54,7 @@ class DataType
      *
      * @return RichText|string Sanitized value
      */
-    public static function checkString($textValue)
+    public static function checkString(null|RichText|string $textValue): RichText|string
     {
         if ($textValue instanceof RichText) {
             // TODO: Sanitize Rich-Text string (max. character count is 32,767)
@@ -76,9 +77,9 @@ class DataType
      *
      * @return string Sanitized value
      */
-    public static function checkErrorCode($value)
+    public static function checkErrorCode(mixed $value): string
     {
-        $value = (string) $value;
+        $value = (is_scalar($value) || $value instanceof Stringable) ? ((string) $value) : '#NULL!';
 
         if (!isset(self::$errorCodes[$value])) {
             $value = '#NULL!';
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
index 7ee53ea..9a5f44e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidation.php
@@ -28,97 +28,72 @@ class DataValidation
     const OPERATOR_LESSTHANOREQUAL = 'lessThanOrEqual';
     const OPERATOR_NOTBETWEEN = 'notBetween';
     const OPERATOR_NOTEQUAL = 'notEqual';
+    private const DEFAULT_OPERATOR = self::OPERATOR_BETWEEN;
 
     /**
      * Formula 1.
-     *
-     * @var string
      */
-    private $formula1 = '';
+    private string $formula1 = '';
 
     /**
      * Formula 2.
-     *
-     * @var string
      */
-    private $formula2 = '';
+    private string $formula2 = '';
 
     /**
      * Type.
-     *
-     * @var string
      */
-    private $type = self::TYPE_NONE;
+    private string $type = self::TYPE_NONE;
 
     /**
      * Error style.
-     *
-     * @var string
      */
-    private $errorStyle = self::STYLE_STOP;
+    private string $errorStyle = self::STYLE_STOP;
 
     /**
      * Operator.
-     *
-     * @var string
      */
-    private $operator = self::OPERATOR_BETWEEN;
+    private string $operator = self::DEFAULT_OPERATOR;
 
     /**
      * Allow Blank.
-     *
-     * @var bool
      */
-    private $allowBlank = false;
+    private bool $allowBlank = false;
 
     /**
      * Show DropDown.
-     *
-     * @var bool
      */
-    private $showDropDown = false;
+    private bool $showDropDown = false;
 
     /**
      * Show InputMessage.
-     *
-     * @var bool
      */
-    private $showInputMessage = false;
+    private bool $showInputMessage = false;
 
     /**
      * Show ErrorMessage.
-     *
-     * @var bool
      */
-    private $showErrorMessage = false;
+    private bool $showErrorMessage = false;
 
     /**
      * Error title.
-     *
-     * @var string
      */
-    private $errorTitle = '';
+    private string $errorTitle = '';
 
     /**
      * Error.
-     *
-     * @var string
      */
-    private $error = '';
+    private string $error = '';
 
     /**
      * Prompt title.
-     *
-     * @var string
      */
-    private $promptTitle = '';
+    private string $promptTitle = '';
 
     /**
      * Prompt.
-     *
-     * @var string
      */
-    private $prompt = '';
+    private string $prompt = '';
 
     /**
      * Create a new DataValidation.
@@ -129,10 +104,8 @@ class DataValidation
 
     /**
      * Get Formula 1.
-     *
-     * @return string
      */
-    public function getFormula1()
+    public function getFormula1(): string
     {
         return $this->formula1;
     }
@@ -140,11 +113,9 @@ class DataValidation
     /**
      * Set Formula 1.
      *
-     * @param string $formula
-     *
      * @return $this
      */
-    public function setFormula1($formula)
+    public function setFormula1(string $formula): static
     {
         $this->formula1 = $formula;
 
@@ -153,10 +124,8 @@ class DataValidation
 
     /**
      * Get Formula 2.
-     *
-     * @return string
      */
-    public function getFormula2()
+    public function getFormula2(): string
     {
         return $this->formula2;
     }
@@ -164,11 +133,9 @@ class DataValidation
     /**
      * Set Formula 2.
      *
-     * @param string $formula
-     *
      * @return $this
      */
-    public function setFormula2($formula)
+    public function setFormula2(string $formula): static
     {
         $this->formula2 = $formula;
 
@@ -177,10 +144,8 @@ class DataValidation
 
     /**
      * Get Type.
-     *
-     * @return string
      */
-    public function getType()
+    public function getType(): string
     {
         return $this->type;
     }
@@ -188,11 +153,9 @@ class DataValidation
     /**
      * Set Type.
      *
-     * @param string $type
-     *
      * @return $this
      */
-    public function setType($type)
+    public function setType(string $type): static
     {
         $this->type = $type;
 
@@ -201,10 +164,8 @@ class DataValidation
 
     /**
      * Get Error style.
-     *
-     * @return string
      */
-    public function getErrorStyle()
+    public function getErrorStyle(): string
     {
         return $this->errorStyle;
     }
@@ -216,7 +177,7 @@ class DataValidation
      *
      * @return $this
      */
-    public function setErrorStyle($errorStyle)
+    public function setErrorStyle(string $errorStyle): static
     {
         $this->errorStyle = $errorStyle;
 
@@ -225,10 +186,8 @@ class DataValidation
 
     /**
      * Get Operator.
-     *
-     * @return string
      */
-    public function getOperator()
+    public function getOperator(): string
     {
         return $this->operator;
     }
@@ -236,23 +195,19 @@ class DataValidation
     /**
      * Set Operator.
      *
-     * @param string $operator
-     *
      * @return $this
      */
-    public function setOperator($operator)
+    public function setOperator(string $operator): static
     {
-        $this->operator = $operator;
+        $this->operator = ($operator === '') ? self::DEFAULT_OPERATOR : $operator;
 
         return $this;
     }
 
     /**
      * Get Allow Blank.
-     *
-     * @return bool
      */
-    public function getAllowBlank()
+    public function getAllowBlank(): bool
     {
         return $this->allowBlank;
     }
@@ -260,11 +215,9 @@ class DataValidation
     /**
      * Set Allow Blank.
      *
-     * @param bool $allowBlank
-     *
      * @return $this
      */
-    public function setAllowBlank($allowBlank)
+    public function setAllowBlank(bool $allowBlank): static
     {
         $this->allowBlank = $allowBlank;
 
@@ -273,10 +226,8 @@ class DataValidation
 
     /**
      * Get Show DropDown.
-     *
-     * @return bool
      */
-    public function getShowDropDown()
+    public function getShowDropDown(): bool
     {
         return $this->showDropDown;
     }
@@ -284,11 +235,9 @@ class DataValidation
     /**
      * Set Show DropDown.
      *
-     * @param bool $showDropDown
-     *
      * @return $this
      */
-    public function setShowDropDown($showDropDown)
+    public function setShowDropDown(bool $showDropDown): static
     {
         $this->showDropDown = $showDropDown;
 
@@ -297,10 +246,8 @@ class DataValidation
 
     /**
      * Get Show InputMessage.
-     *
-     * @return bool
      */
-    public function getShowInputMessage()
+    public function getShowInputMessage(): bool
     {
         return $this->showInputMessage;
     }
@@ -308,11 +255,9 @@ class DataValidation
     /**
      * Set Show InputMessage.
      *
-     * @param bool $showInputMessage
-     *
      * @return $this
      */
-    public function setShowInputMessage($showInputMessage)
+    public function setShowInputMessage(bool $showInputMessage): static
     {
         $this->showInputMessage = $showInputMessage;
 
@@ -321,10 +266,8 @@ class DataValidation
 
     /**
      * Get Show ErrorMessage.
-     *
-     * @return bool
      */
-    public function getShowErrorMessage()
+    public function getShowErrorMessage(): bool
     {
         return $this->showErrorMessage;
     }
@@ -332,11 +275,9 @@ class DataValidation
     /**
      * Set Show ErrorMessage.
      *
-     * @param bool $showErrorMessage
-     *
      * @return $this
      */
-    public function setShowErrorMessage($showErrorMessage)
+    public function setShowErrorMessage(bool $showErrorMessage): static
     {
         $this->showErrorMessage = $showErrorMessage;
 
@@ -345,10 +286,8 @@ class DataValidation
 
     /**
      * Get Error title.
-     *
-     * @return string
      */
-    public function getErrorTitle()
+    public function getErrorTitle(): string
     {
         return $this->errorTitle;
     }
@@ -356,11 +295,9 @@ class DataValidation
     /**
      * Set Error title.
      *
-     * @param string $errorTitle
-     *
      * @return $this
      */
-    public function setErrorTitle($errorTitle)
+    public function setErrorTitle(string $errorTitle): static
     {
         $this->errorTitle = $errorTitle;
 
@@ -369,10 +306,8 @@ class DataValidation
 
     /**
      * Get Error.
-     *
-     * @return string
      */
-    public function getError()
+    public function getError(): string
     {
         return $this->error;
     }
@@ -380,11 +315,9 @@ class DataValidation
     /**
      * Set Error.
      *
-     * @param string $error
-     *
      * @return $this
      */
-    public function setError($error)
+    public function setError(string $error): static
     {
         $this->error = $error;
 
@@ -393,10 +326,8 @@ class DataValidation
 
     /**
      * Get Prompt title.
-     *
-     * @return string
      */
-    public function getPromptTitle()
+    public function getPromptTitle(): string
     {
         return $this->promptTitle;
     }
@@ -404,11 +335,9 @@ class DataValidation
     /**
      * Set Prompt title.
      *
-     * @param string $promptTitle
-     *
      * @return $this
      */
-    public function setPromptTitle($promptTitle)
+    public function setPromptTitle(string $promptTitle): static
     {
         $this->promptTitle = $promptTitle;
 
@@ -417,10 +346,8 @@ class DataValidation
 
     /**
      * Get Prompt.
-     *
-     * @return string
      */
-    public function getPrompt()
+    public function getPrompt(): string
     {
         return $this->prompt;
     }
@@ -428,11 +355,9 @@ class DataValidation
     /**
      * Set Prompt.
      *
-     * @param string $prompt
-     *
      * @return $this
      */
-    public function setPrompt($prompt)
+    public function setPrompt(string $prompt): static
     {
         $this->prompt = $prompt;
 
@@ -444,24 +369,24 @@ class DataValidation
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->formula1 .
-            $this->formula2 .
-            $this->type .
-            $this->errorStyle .
-            $this->operator .
-            ($this->allowBlank ? 't' : 'f') .
-            ($this->showDropDown ? 't' : 'f') .
-            ($this->showInputMessage ? 't' : 'f') .
-            ($this->showErrorMessage ? 't' : 'f') .
-            $this->errorTitle .
-            $this->error .
-            $this->promptTitle .
-            $this->prompt .
-            $this->sqref .
-            __CLASS__
+            $this->formula1
+            . $this->formula2
+            . $this->type
+            . $this->errorStyle
+            . $this->operator
+            . ($this->allowBlank ? 't' : 'f')
+            . ($this->showDropDown ? 't' : 'f')
+            . ($this->showInputMessage ? 't' : 'f')
+            . ($this->showErrorMessage ? 't' : 'f')
+            . $this->errorTitle
+            . $this->error
+            . $this->promptTitle
+            . $this->prompt
+            . $this->sqref
+            . __CLASS__
         );
     }
 
@@ -480,8 +405,7 @@ class DataValidation
         }
     }
 
-    /** @var ?string */
-    private $sqref;
+    private ?string $sqref = null;
 
     public function getSqref(): ?string
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php
index 0e395a7..63ef899 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DataValidator.php
@@ -15,12 +15,10 @@ class DataValidator
      * Does this cell contain valid value?
      *
      * @param Cell $cell Cell to check the value
-     *
-     * @return bool
      */
-    public function isValid(Cell $cell)
+    public function isValid(Cell $cell): bool
     {
-        if (!$cell->hasDataValidation()) {
+        if (!$cell->hasDataValidation() || $cell->getDataValidation()->getType() === DataValidation::TYPE_NONE) {
             return true;
         }
 
@@ -31,32 +29,71 @@ class DataValidator
             return false;
         }
 
-        // TODO: write check on all cases
-        switch ($dataValidation->getType()) {
-            case DataValidation::TYPE_LIST:
-                return $this->isValueInList($cell);
+        $returnValue = false;
+        $type = $dataValidation->getType();
+        if ($type === DataValidation::TYPE_LIST) {
+            $returnValue = $this->isValueInList($cell);
+        } elseif ($type === DataValidation::TYPE_WHOLE) {
+            if (!is_numeric($cellValue) || fmod((float) $cellValue, 1) != 0) {
+                $returnValue = false;
+            } else {
+                $returnValue = $this->numericOperator($dataValidation, (int) $cellValue);
+            }
+        } elseif ($type === DataValidation::TYPE_DECIMAL || $type === DataValidation::TYPE_DATE || $type === DataValidation::TYPE_TIME) {
+            if (!is_numeric($cellValue)) {
+                $returnValue = false;
+            } else {
+                $returnValue = $this->numericOperator($dataValidation, (float) $cellValue);
+            }
+        } elseif ($type === DataValidation::TYPE_TEXTLENGTH) {
+            $returnValue = $this->numericOperator($dataValidation, mb_strlen($cell->getValueString()));
         }
 
-        return false;
+        return $returnValue;
+    }
+
+    private function numericOperator(DataValidation $dataValidation, int|float $cellValue): bool
+    {
+        $operator = $dataValidation->getOperator();
+        $formula1 = $dataValidation->getFormula1();
+        $formula2 = $dataValidation->getFormula2();
+        $returnValue = false;
+        if ($operator === DataValidation::OPERATOR_BETWEEN) {
+            $returnValue = $cellValue >= $formula1 && $cellValue <= $formula2;
+        } elseif ($operator === DataValidation::OPERATOR_NOTBETWEEN) {
+            $returnValue = $cellValue < $formula1 || $cellValue > $formula2;
+        } elseif ($operator === DataValidation::OPERATOR_EQUAL) {
+            $returnValue = $cellValue == $formula1;
+        } elseif ($operator === DataValidation::OPERATOR_NOTEQUAL) {
+            $returnValue = $cellValue != $formula1;
+        } elseif ($operator === DataValidation::OPERATOR_LESSTHAN) {
+            $returnValue = $cellValue < $formula1;
+        } elseif ($operator === DataValidation::OPERATOR_LESSTHANOREQUAL) {
+            $returnValue = $cellValue <= $formula1;
+        } elseif ($operator === DataValidation::OPERATOR_GREATERTHAN) {
+            $returnValue = $cellValue > $formula1;
+        } elseif ($operator === DataValidation::OPERATOR_GREATERTHANOREQUAL) {
+            $returnValue = $cellValue >= $formula1;
+        }
+
+        return $returnValue;
     }
 
     /**
      * Does this cell contain valid value, based on list?
      *
      * @param Cell $cell Cell to check the value
-     *
-     * @return bool
      */
-    private function isValueInList(Cell $cell)
+    private function isValueInList(Cell $cell): bool
     {
-        $cellValue = $cell->getValue();
+        $cellValueString = $cell->getValueString();
         $dataValidation = $cell->getDataValidation();
 
         $formula1 = $dataValidation->getFormula1();
         if (!empty($formula1)) {
             // inline values list
             if ($formula1[0] === '"') {
-                return in_array(strtolower($cellValue), explode(',', strtolower(trim($formula1, '"'))), true);
+                return in_array(strtolower($cellValueString), explode(',', strtolower(trim($formula1, '"'))), true);
             } elseif (strpos($formula1, ':') > 0) {
                 // values list cells
                 $matchFormula = '=MATCH(' . $cell->getCoordinate() . ', ' . $formula1 . ', 0)';
@@ -69,7 +106,7 @@ class DataValidator
                     }
 
                     return $result !== ExcelError::NA();
-                } catch (Exception $ex) {
+                } catch (Exception) {
                     return false;
                 }
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
index 92e1d25..f36934e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
@@ -3,8 +3,12 @@
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use DateTimeInterface;
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
+use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use Stringable;
 
 class DefaultValueBinder implements IValueBinder
 {
@@ -13,22 +17,20 @@ class DefaultValueBinder implements IValueBinder
      *
      * @param Cell $cell Cell to bind value to
      * @param mixed $value Value to bind in cell
-     *
-     * @return bool
      */
-    public function bindValue(Cell $cell, $value)
+    public function bindValue(Cell $cell, mixed $value): bool
     {
         // sanitize UTF-8 strings
         if (is_string($value)) {
             $value = StringHelper::sanitizeUTF8($value);
-        } elseif (is_object($value)) {
-            // Handle any objects that might be injected
-            if ($value instanceof DateTimeInterface) {
-                $value = $value->format('Y-m-d H:i:s');
-            } elseif (!($value instanceof RichText)) {
-                // Attempt to cast any unexpected objects to string
-                $value = (string) $value; // @phpstan-ignore-line
-            }
+        } elseif ($value === null || is_scalar($value) || $value instanceof RichText) {
+            // No need to do anything
+        } elseif ($value instanceof DateTimeInterface) {
+            $value = $value->format('Y-m-d H:i:s');
+        } elseif ($value instanceof Stringable) {
+            $value = (string) $value;
+        } else {
+            throw new SpreadsheetException('Unable to bind unstringable ' . gettype($value));
         }
 
         // Set value explicit
@@ -40,42 +42,68 @@ class DefaultValueBinder implements IValueBinder
 
     /**
      * DataType for value.
-     *
-     * @param mixed $value
-     *
-     * @return string
      */
-    public static function dataTypeForValue($value)
+    public static function dataTypeForValue(mixed $value): string
     {
         // Match the value against a few data types
         if ($value === null) {
             return DataType::TYPE_NULL;
-        } elseif (is_float($value) || is_int($value)) {
+        }
+        if (is_float($value) || is_int($value)) {
             return DataType::TYPE_NUMERIC;
-        } elseif (is_bool($value)) {
+        }
+        if (is_bool($value)) {
             return DataType::TYPE_BOOL;
-        } elseif ($value === '') {
+        }
+        if ($value === '') {
             return DataType::TYPE_STRING;
-        } elseif ($value instanceof RichText) {
+        }
+        if ($value instanceof RichText) {
             return DataType::TYPE_INLINE;
-        } elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=') {
+        }
+        if ($value instanceof Stringable) {
+            $value = (string) $value;
+        }
+        if (!is_string($value)) {
+            $gettype = is_object($value) ? get_class($value) : gettype($value);
+
+            throw new SpreadsheetException("unusable type $gettype");
+        }
+        if (strlen($value) > 1 && $value[0] === '=') {
+            $calculation = new Calculation();
+            $calculation->disableBranchPruning();
+
+            try {
+                if (empty($calculation->parseFormula($value))) {
+                    return DataType::TYPE_STRING;
+                }
+            } catch (CalculationException $e) {
+                $message = $e->getMessage();
+                if (
+                    $message === 'Formula Error: An unexpected error occurred'
+                    || str_contains($message, 'has no operands')
+                ) {
+                    return DataType::TYPE_STRING;
+                }
+            }
+
             return DataType::TYPE_FORMULA;
-        } elseif (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $value)) {
+        }
+        if (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $value)) {
             $tValue = ltrim($value, '+-');
-            if (is_string($value) && strlen($tValue) > 1 && $tValue[0] === '0' && $tValue[1] !== '.') {
+            if (strlen($tValue) > 1 && $tValue[0] === '0' && $tValue[1] !== '.') {
                 return DataType::TYPE_STRING;
-            } elseif ((strpos($value, '.') === false) && ($value > PHP_INT_MAX)) {
+            } elseif ((!str_contains($value, '.')) && ($value > PHP_INT_MAX)) {
                 return DataType::TYPE_STRING;
             } elseif (!is_numeric($value)) {
                 return DataType::TYPE_STRING;
             }
 
             return DataType::TYPE_NUMERIC;
-        } elseif (is_string($value)) {
-            $errorCodes = DataType::getErrorCodes();
-            if (isset($errorCodes[$value])) {
-                return DataType::TYPE_ERROR;
-            }
+        }
+        $errorCodes = DataType::getErrorCodes();
+        if (isset($errorCodes[$value])) {
+            return DataType::TYPE_ERROR;
         }
 
         return DataType::TYPE_STRING;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
index ffdcbac..3117a7d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Hyperlink.php
@@ -6,17 +6,13 @@ class Hyperlink
 {
     /**
      * URL to link the cell to.
-     *
-     * @var string
      */
-    private $url;
+    private string $url;
 
     /**
      * Tooltip to display on the hyperlink.
-     *
-     * @var string
      */
-    private $tooltip;
+    private string $tooltip;
 
     /**
      * Create a new Hyperlink.
@@ -24,7 +20,7 @@ class Hyperlink
      * @param string $url Url to link the cell to
      * @param string $tooltip Tooltip to display on the hyperlink
      */
-    public function __construct($url = '', $tooltip = '')
+    public function __construct(string $url = '', string $tooltip = '')
     {
         // Initialise member variables
         $this->url = $url;
@@ -33,10 +29,8 @@ class Hyperlink
 
     /**
      * Get URL.
-     *
-     * @return string
      */
-    public function getUrl()
+    public function getUrl(): string
     {
         return $this->url;
     }
@@ -44,11 +38,9 @@ class Hyperlink
     /**
      * Set URL.
      *
-     * @param string $url
-     *
      * @return $this
      */
-    public function setUrl($url)
+    public function setUrl(string $url): static
     {
         $this->url = $url;
 
@@ -57,10 +49,8 @@ class Hyperlink
 
     /**
      * Get tooltip.
-     *
-     * @return string
      */
-    public function getTooltip()
+    public function getTooltip(): string
     {
         return $this->tooltip;
     }
@@ -68,11 +58,9 @@ class Hyperlink
     /**
      * Set tooltip.
      *
-     * @param string $tooltip
-     *
      * @return $this
      */
-    public function setTooltip($tooltip)
+    public function setTooltip(string $tooltip): static
     {
         $this->tooltip = $tooltip;
 
@@ -81,18 +69,13 @@ class Hyperlink
 
     /**
      * Is this hyperlink internal? (to another worksheet).
-     *
-     * @return bool
      */
-    public function isInternal()
+    public function isInternal(): bool
     {
-        return strpos($this->url, 'sheet://') !== false;
+        return str_contains($this->url, 'sheet://');
     }
 
-    /**
-     * @return string
-     */
-    public function getTypeHyperlink()
+    public function getTypeHyperlink(): string
     {
         return $this->isInternal() ? '' : 'External';
     }
@@ -102,12 +85,12 @@ class Hyperlink
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->url .
-            $this->tooltip .
-            __CLASS__
+            $this->url
+            . $this->tooltip
+            . __CLASS__
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php
index 5af9f5f..b2f7e91 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IValueBinder.php
@@ -9,8 +9,6 @@ interface IValueBinder
      *
      * @param Cell $cell Cell to bind value to
      * @param mixed $value Value to bind in cell
-     *
-     * @return bool
      */
-    public function bindValue(Cell $cell, $value);
+    public function bindValue(Cell $cell, mixed $value): bool;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IgnoredErrors.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IgnoredErrors.php
new file mode 100644
index 0000000..a7c4d19
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/IgnoredErrors.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+class IgnoredErrors
+{
+    private bool $numberStoredAsText = false;
+
+    private bool $formula = false;
+
+    private bool $twoDigitTextYear = false;
+
+    private bool $evalError = false;
+
+    public function setNumberStoredAsText(bool $value): self
+    {
+        $this->numberStoredAsText = $value;
+
+        return $this;
+    }
+
+    public function getNumberStoredAsText(): bool
+    {
+        return $this->numberStoredAsText;
+    }
+
+    public function setFormula(bool $value): self
+    {
+        $this->formula = $value;
+
+        return $this;
+    }
+
+    public function getFormula(): bool
+    {
+        return $this->formula;
+    }
+
+    public function setTwoDigitTextYear(bool $value): self
+    {
+        $this->twoDigitTextYear = $value;
+
+        return $this;
+    }
+
+    public function getTwoDigitTextYear(): bool
+    {
+        return $this->twoDigitTextYear;
+    }
+
+    public function setEvalError(bool $value): self
+    {
+        $this->evalError = $value;
+
+        return $this;
+    }
+
+    public function getEvalError(): bool
+    {
+        return $this->evalError;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php
index 38e6c14..4ed232a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/RowRange.php
@@ -3,23 +3,18 @@
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+use Stringable;
 
-class RowRange implements AddressRange
+/**
+ * @implements AddressRange<int>
+ */
+class RowRange implements AddressRange, Stringable
 {
-    /**
-     * @var ?Worksheet
-     */
-    protected $worksheet;
+    protected ?Worksheet $worksheet;
 
-    /**
-     * @var int
-     */
-    protected $from;
+    protected int $from;
 
-    /**
-     * @var int
-     */
-    protected $to;
+    protected int $to;
 
     public function __construct(int $from, ?int $to = null, ?Worksheet $worksheet = null)
     {
@@ -27,6 +22,11 @@ class RowRange implements AddressRange
         $this->worksheet = $worksheet;
     }
 
+    public function __destruct()
+    {
+        $this->worksheet = null;
+    }
+
     public static function fromArray(array $array, ?Worksheet $worksheet = null): self
     {
         [$from, $to] = $array;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
index 1dcb612..d86cdab 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
@@ -3,30 +3,20 @@
 namespace PhpOffice\PhpSpreadsheet\Cell;
 
 use DateTimeInterface;
+use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+use Stringable;
 
-class StringValueBinder implements IValueBinder
+class StringValueBinder extends DefaultValueBinder implements IValueBinder
 {
-    /**
-     * @var bool
-     */
-    protected $convertNull = true;
+    protected bool $convertNull = true;
 
-    /**
-     * @var bool
-     */
-    protected $convertBoolean = true;
+    protected bool $convertBoolean = true;
 
-    /**
-     * @var bool
-     */
-    protected $convertNumeric = true;
+    protected bool $convertNumeric = true;
 
-    /**
-     * @var bool
-     */
-    protected $convertFormula = true;
+    protected bool $convertFormula = true;
 
     public function setNullConversion(bool $suppressConversion = false): self
     {
@@ -77,11 +67,14 @@ class StringValueBinder implements IValueBinder
      * @param Cell $cell Cell to bind value to
      * @param mixed $value Value to bind in cell
      */
-    public function bindValue(Cell $cell, $value)
+    public function bindValue(Cell $cell, mixed $value): bool
     {
         if (is_object($value)) {
             return $this->bindObjectValue($cell, $value);
         }
+        if ($value !== null && !is_scalar($value)) {
+            throw new SpreadsheetException('Unable to bind unstringable ' . gettype($value));
+        }
 
         // sanitize UTF-8 strings
         if (is_string($value)) {
@@ -94,12 +87,9 @@ class StringValueBinder implements IValueBinder
             $cell->setValueExplicit($value, DataType::TYPE_BOOL);
         } elseif ((is_int($value) || is_float($value)) && $this->convertNumeric === false) {
             $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
-        } elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=' && $this->convertFormula === false) {
+        } elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=' && $this->convertFormula === false && parent::dataTypeForValue($value) === DataType::TYPE_FORMULA) {
             $cell->setValueExplicit($value, DataType::TYPE_FORMULA);
         } else {
-            if (is_string($value) && strlen($value) > 1 && $value[0] === '=') {
-                $cell->getStyle()->setQuotePrefix(true);
-            }
             $cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
         }
 
@@ -111,14 +101,15 @@ class StringValueBinder implements IValueBinder
         // Handle any objects that might be injected
         if ($value instanceof DateTimeInterface) {
             $value = $value->format('Y-m-d H:i:s');
+            $cell->setValueExplicit($value, DataType::TYPE_STRING);
         } elseif ($value instanceof RichText) {
             $cell->setValueExplicit($value, DataType::TYPE_INLINE);
-
-            return true;
+        } elseif ($value instanceof Stringable) {
+            $cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
+        } else {
+            throw new SpreadsheetException('Unable to bind unstringable object of type ' . get_class($value));
         }
 
-        $cell->setValueExplicit((string) $value, DataType::TYPE_STRING); // @phpstan-ignore-line
-
         return true;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php
index bf4e295..b2f18f6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/CellReferenceHelper.php
@@ -7,30 +7,15 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 
 class CellReferenceHelper
 {
-    /**
-     * @var string
-     */
-    protected $beforeCellAddress;
+    protected string $beforeCellAddress;
 
-    /**
-     * @var int
-     */
-    protected $beforeColumn;
+    protected int $beforeColumn;
 
-    /**
-     * @var int
-     */
-    protected $beforeRow;
+    protected int $beforeRow;
 
-    /**
-     * @var int
-     */
-    protected $numberOfColumns;
+    protected int $numberOfColumns;
 
-    /**
-     * @var int
-     */
-    protected $numberOfRows;
+    protected int $numberOfRows;
 
     public function __construct(string $beforeCellAddress = 'A1', int $numberOfColumns = 0, int $numberOfRows = 0)
     {
@@ -40,7 +25,7 @@ class CellReferenceHelper
 
         // Get coordinate of $beforeCellAddress
         [$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
-        $this->beforeColumn = (int) Coordinate::columnIndexFromString($beforeColumn);
+        $this->beforeColumn = Coordinate::columnIndexFromString($beforeColumn);
         $this->beforeRow = (int) $beforeRow;
     }
 
@@ -51,12 +36,12 @@ class CellReferenceHelper
 
     public function refreshRequired(string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): bool
     {
-        return $this->beforeCellAddress !== $beforeCellAddress ||
-            $this->numberOfColumns !== $numberOfColumns ||
-            $this->numberOfRows !== $numberOfRows;
+        return $this->beforeCellAddress !== $beforeCellAddress
+            || $this->numberOfColumns !== $numberOfColumns
+            || $this->numberOfRows !== $numberOfRows;
     }
 
-    public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false): string
+    public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false): string
     {
         if (Coordinate::coordinateIsRange($cellReference)) {
             throw new Exception('Only single cell references may be passed to this method.');
@@ -64,13 +49,16 @@ class CellReferenceHelper
 
         // Get coordinate of $cellReference
         [$newColumn, $newRow] = Coordinate::coordinateFromString($cellReference);
-        $newColumnIndex = (int) Coordinate::columnIndexFromString(str_replace('$', '', $newColumn));
+        $newColumnIndex = Coordinate::columnIndexFromString(str_replace('$', '', $newColumn));
         $newRowIndex = (int) str_replace('$', '', $newRow);
 
         $absoluteColumn = $newColumn[0] === '$' ? '$' : '';
         $absoluteRow = $newRow[0] === '$' ? '$' : '';
         // Verify which parts should be updated
-        if ($includeAbsoluteReferences === false) {
+        if ($onlyAbsoluteReferences === true) {
+            $updateColumn = (($absoluteColumn === '$') && $newColumnIndex >= $this->beforeColumn);
+            $updateRow = (($absoluteRow === '$') && $newRowIndex >= $this->beforeRow);
+        } elseif ($includeAbsoluteReferences === false) {
             $updateColumn = (($absoluteColumn !== '$') && $newColumnIndex >= $this->beforeColumn);
             $updateRow = (($absoluteRow !== '$') && $newRowIndex >= $this->beforeRow);
         } else {
@@ -98,15 +86,15 @@ class CellReferenceHelper
         $cellColumnIndex = Coordinate::columnIndexFromString($cellColumn);
         //    Is cell within the range of rows/columns if we're deleting
         if (
-            $this->numberOfRows < 0 &&
-            ($cellRow >= ($this->beforeRow + $this->numberOfRows)) &&
-            ($cellRow < $this->beforeRow)
+            $this->numberOfRows < 0
+            && ($cellRow >= ($this->beforeRow + $this->numberOfRows))
+            && ($cellRow < $this->beforeRow)
         ) {
             return true;
         } elseif (
-            $this->numberOfColumns < 0 &&
-            ($cellColumnIndex >= ($this->beforeColumn + $this->numberOfColumns)) &&
-            ($cellColumnIndex < $this->beforeColumn)
+            $this->numberOfColumns < 0
+            && ($cellColumnIndex >= ($this->beforeColumn + $this->numberOfColumns))
+            && ($cellColumnIndex < $this->beforeColumn)
         ) {
             return true;
         }
@@ -116,10 +104,9 @@ class CellReferenceHelper
 
     protected function updateColumnReference(int $newColumnIndex, string $absoluteColumn): string
     {
-        $newColumn = Coordinate::stringFromColumnIndex($newColumnIndex + $this->numberOfColumns);
-        $newColumn = ($newColumn > AddressRange::MAX_COLUMN) ? AddressRange::MAX_COLUMN : $newColumn;
+        $newColumn = Coordinate::stringFromColumnIndex(min($newColumnIndex + $this->numberOfColumns, AddressRange::MAX_COLUMN_INT));
 
-        return $absoluteColumn . $newColumn;
+        return "{$absoluteColumn}{$newColumn}";
     }
 
     protected function updateRowReference(int $newRowIndex, string $absoluteRow): string
@@ -127,6 +114,6 @@ class CellReferenceHelper
         $newRow = $newRowIndex + $this->numberOfRows;
         $newRow = ($newRow > AddressRange::MAX_ROW) ? AddressRange::MAX_ROW : $newRow;
 
-        return $absoluteRow . (string) $newRow;
+        return "{$absoluteRow}{$newRow}";
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
index ade7b99..dc1fca4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
@@ -26,38 +26,37 @@ class Axis extends Properties
 
     /**
      * Chart Major Gridlines as.
-     *
-     * @var ?GridLines
      */
-    private $majorGridlines;
+    private ?GridLines $majorGridlines = null;
 
     /**
      * Chart Minor Gridlines as.
-     *
-     * @var ?GridLines
      */
-    private $minorGridlines;
+    private ?GridLines $minorGridlines = null;
 
     /**
      * Axis Number.
      *
-     * @var mixed[]
+     * @var array{format: string, source_linked: int, numeric: ?bool}
      */
-    private $axisNumber = [
+    private array $axisNumber = [
         'format' => self::FORMAT_CODE_GENERAL,
         'source_linked' => 1,
         'numeric' => null,
     ];
 
-    /** @var string */
-    private $axisType = '';
+    private string $axisType = '';
+
+    private ?AxisText $axisText = null;
+
+    private ?Title $dispUnitsTitle = null;
 
     /**
      * Axis Options.
      *
-     * @var mixed[]
+     * @var array<string, null|string>
      */
-    private $axisOptions = [
+    private array $axisOptions = [
         'minimum' => null,
         'maximum' => null,
         'major_unit' => null,
@@ -73,14 +72,35 @@ class Axis extends Properties
         'majorTimeUnit' => self::TIME_UNIT_YEARS,
         'minorTimeUnit' => self::TIME_UNIT_MONTHS,
         'baseTimeUnit' => self::TIME_UNIT_DAYS,
+        'logBase' => null,
+        'dispUnitsBuiltIn' => null,
+    ];
+    public const DISP_UNITS_HUNDREDS = 'hundreds';
+    public const DISP_UNITS_THOUSANDS = 'thousands';
+    public const DISP_UNITS_TEN_THOUSANDS = 'tenThousands';
+    public const DISP_UNITS_HUNDRED_THOUSANDS = 'hundredThousands';
+    public const DISP_UNITS_MILLIONS = 'millions';
+    public const DISP_UNITS_TEN_MILLIONS = 'tenMillions';
+    public const DISP_UNITS_HUNDRED_MILLIONS = 'hundredMillions';
+    public const DISP_UNITS_BILLIONS = 'billions';
+    public const DISP_UNITS_TRILLIONS = 'trillions';
+    public const TRILLION_INDEX = (PHP_INT_SIZE > 4) ? 1000000000000 : '1000000000000';
+    public const DISP_UNITS_BUILTIN_INT = [
+        100 => self::DISP_UNITS_HUNDREDS,
+        1000 => self::DISP_UNITS_THOUSANDS,
+        10000 => self::DISP_UNITS_TEN_THOUSANDS,
+        100000 => self::DISP_UNITS_HUNDRED_THOUSANDS,
+        1000000 => self::DISP_UNITS_MILLIONS,
+        10000000 => self::DISP_UNITS_TEN_MILLIONS,
+        100000000 => self::DISP_UNITS_HUNDRED_MILLIONS,
+        1000000000 => self::DISP_UNITS_BILLIONS,
+        self::TRILLION_INDEX => self::DISP_UNITS_TRILLIONS, // overflow for 32-bit
     ];
 
     /**
      * Fill Properties.
-     *
-     * @var ChartColor
      */
-    private $fillColor;
+    private ChartColor $fillColor;
 
     private const NUMERIC_FORMAT = [
         Properties::FORMAT_CODE_NUMBER,
@@ -88,14 +108,14 @@ class Axis extends Properties
         Properties::FORMAT_CODE_DATE_ISO8601,
     ];
 
+    private bool $noFill = false;
+
     /**
      * Get Series Data Type.
-     *
-     * @param mixed $format_code
      */
-    public function setAxisNumberProperties($format_code, ?bool $numeric = null, int $sourceLinked = 0): void
+    public function setAxisNumberProperties(string $format_code, ?bool $numeric = null, int $sourceLinked = 0): void
     {
-        $format = (string) $format_code;
+        $format = $format_code;
         $this->axisNumber['format'] = $format;
         $this->axisNumber['source_linked'] = $sourceLinked;
         if (is_bool($numeric)) {
@@ -107,20 +127,16 @@ class Axis extends Properties
 
     /**
      * Get Axis Number Format Data Type.
-     *
-     * @return string
      */
-    public function getAxisNumberFormat()
+    public function getAxisNumberFormat(): string
     {
         return $this->axisNumber['format'];
     }
 
     /**
      * Get Axis Number Source Linked.
-     *
-     * @return string
      */
-    public function getAxisNumberSourceLinked()
+    public function getAxisNumberSourceLinked(): string
     {
         return (string) $this->axisNumber['source_linked'];
     }
@@ -130,10 +146,10 @@ class Axis extends Properties
         return $this->axisType === self::AXIS_TYPE_DATE || (bool) $this->axisNumber['numeric'];
     }
 
-    public function setAxisOption(string $key, ?string $value): void
+    public function setAxisOption(string $key, null|float|int|string $value): void
     {
         if ($value !== null && $value !== '') {
-            $this->axisOptions[$key] = $value;
+            $this->axisOptions[$key] = (string) $value;
         }
     }
 
@@ -147,15 +163,17 @@ class Axis extends Properties
         ?string $axisOrientation = null,
         ?string $majorTmt = null,
         ?string $minorTmt = null,
-        ?string $minimum = null,
-        ?string $maximum = null,
-        ?string $majorUnit = null,
-        ?string $minorUnit = null,
-        ?string $textRotation = null,
+        null|float|int|string $minimum = null,
+        null|float|int|string $maximum = null,
+        null|float|int|string $majorUnit = null,
+        null|float|int|string $minorUnit = null,
+        null|float|int|string $textRotation = null,
         ?string $hidden = null,
         ?string $baseTimeUnit = null,
         ?string $majorTimeUnit = null,
-        ?string $minorTimeUnit = null
+        ?string $minorTimeUnit = null,
+        null|float|int|string $logBase = null,
+        ?string $dispUnitsBuiltIn = null
     ): void {
         $this->axisOptions['axis_labels'] = $axisLabels;
         $this->setAxisOption('horizontal_crosses_value', $horizontalCrossesValue);
@@ -172,26 +190,30 @@ class Axis extends Properties
         $this->setAxisOption('baseTimeUnit', $baseTimeUnit);
         $this->setAxisOption('majorTimeUnit', $majorTimeUnit);
         $this->setAxisOption('minorTimeUnit', $minorTimeUnit);
+        $this->setAxisOption('logBase', $logBase);
+        $this->setAxisOption('dispUnitsBuiltIn', $dispUnitsBuiltIn);
     }
 
     /**
      * Get Axis Options Property.
-     *
-     * @param string $property
-     *
-     * @return ?string
      */
-    public function getAxisOptionsProperty($property)
+    public function getAxisOptionsProperty(string $property): ?string
     {
+        if ($property === 'textRotation') {
+            if ($this->axisText !== null) {
+                if ($this->axisText->getRotation() !== null) {
+                    return (string) $this->axisText->getRotation();
+                }
+            }
+        }
+
         return $this->axisOptions[$property];
     }
 
     /**
      * Set Axis Orientation Property.
-     *
-     * @param string $orientation
      */
-    public function setAxisOrientation($orientation): void
+    public function setAxisOrientation(string $orientation): void
     {
         $this->axisOptions['orientation'] = (string) $orientation;
     }
@@ -214,24 +236,16 @@ class Axis extends Properties
 
     /**
      * Set Fill Property.
-     *
-     * @param ?string $color
-     * @param ?int $alpha
-     * @param ?string $AlphaType
      */
-    public function setFillParameters($color, $alpha = null, $AlphaType = ChartColor::EXCEL_COLOR_TYPE_RGB): void
+    public function setFillParameters(?string $color, ?int $alpha = null, ?string $AlphaType = ChartColor::EXCEL_COLOR_TYPE_RGB): void
     {
         $this->fillColor->setColorProperties($color, $alpha, $AlphaType);
     }
 
     /**
      * Get Fill Property.
-     *
-     * @param string $property
-     *
-     * @return string
      */
-    public function getFillProperty($property)
+    public function getFillProperty(string $property): string
     {
         return (string) $this->fillColor->getColorProperty($property);
     }
@@ -241,24 +255,7 @@ class Axis extends Properties
         return $this->fillColor;
     }
 
-    /**
-     * Get Line Color Property.
-     *
-     * @deprecated 1.24.0
-     *      Use the getLineColor property in the Properties class instead
-     * @see Properties::getLineColorProperty()
-     *
-     * @param string $propertyName
-     *
-     * @return null|int|string
-     */
-    public function getLineProperty($propertyName)
-    {
-        return $this->getLineColorProperty($propertyName);
-    }
-
-    /** @var string */
-    private $crossBetween = ''; // 'between' or 'midCat' might be better
+    private string $crossBetween = ''; // 'between' or 'midCat' might be better
 
     public function setCrossBetween(string $crossBetween): self
     {
@@ -295,4 +292,53 @@ class Axis extends Properties
 
         return $this;
     }
+
+    public function getAxisText(): ?AxisText
+    {
+        return $this->axisText;
+    }
+
+    public function setAxisText(?AxisText $axisText): self
+    {
+        $this->axisText = $axisText;
+
+        return $this;
+    }
+
+    public function setNoFill(bool $noFill): self
+    {
+        $this->noFill = $noFill;
+
+        return $this;
+    }
+
+    public function getNoFill(): bool
+    {
+        return $this->noFill;
+    }
+
+    public function setDispUnitsTitle(?Title $dispUnitsTitle): self
+    {
+        $this->dispUnitsTitle = $dispUnitsTitle;
+
+        return $this;
+    }
+
+    public function getDispUnitsTitle(): ?Title
+    {
+        return $this->dispUnitsTitle;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        parent::__clone();
+        $this->majorGridlines = ($this->majorGridlines === null) ? null : clone $this->majorGridlines;
+        $this->majorGridlines = ($this->minorGridlines === null) ? null : clone $this->minorGridlines;
+        $this->axisText = ($this->axisText === null) ? null : clone $this->axisText;
+        $this->dispUnitsTitle = ($this->dispUnitsTitle === null) ? null : clone $this->dispUnitsTitle;
+        $this->fillColor = clone $this->fillColor;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php
new file mode 100644
index 0000000..09d6b2a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/AxisText.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Chart;
+
+use PhpOffice\PhpSpreadsheet\Style\Font;
+
+class AxisText extends Properties
+{
+    private ?int $rotation = null;
+
+    private Font $font;
+
+    public function __construct()
+    {
+        parent::__construct();
+        $this->font = new Font();
+        $this->font->setSize(null, true);
+    }
+
+    public function setRotation(?int $rotation): self
+    {
+        $this->rotation = $rotation;
+
+        return $this;
+    }
+
+    public function getRotation(): ?int
+    {
+        return $this->rotation;
+    }
+
+    public function getFillColorObject(): ChartColor
+    {
+        $fillColor = $this->font->getChartColor();
+        if ($fillColor === null) {
+            $fillColor = new ChartColor();
+            $this->font->setChartColorFromObject($fillColor);
+        }
+
+        return $fillColor;
+    }
+
+    public function getFont(): Font
+    {
+        return $this->font;
+    }
+
+    public function setFont(Font $font): self
+    {
+        $this->font = $font;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        parent::__clone();
+        $this->font = clone $this->font;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
index 2ff22a3..dae43c9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Chart.php
@@ -9,156 +9,124 @@ class Chart
 {
     /**
      * Chart Name.
-     *
-     * @var string
      */
-    private $name = '';
+    private string $name;
 
     /**
      * Worksheet.
-     *
-     * @var ?Worksheet
      */
-    private $worksheet;
+    private ?Worksheet $worksheet = null;
 
     /**
      * Chart Title.
-     *
-     * @var ?Title
      */
-    private $title;
+    private ?Title $title;
 
     /**
      * Chart Legend.
-     *
-     * @var ?Legend
      */
-    private $legend;
+    private ?Legend $legend;
 
     /**
      * X-Axis Label.
-     *
-     * @var ?Title
      */
-    private $xAxisLabel;
+    private ?Title $xAxisLabel;
 
     /**
      * Y-Axis Label.
-     *
-     * @var ?Title
      */
-    private $yAxisLabel;
+    private ?Title $yAxisLabel;
 
     /**
      * Chart Plot Area.
-     *
-     * @var ?PlotArea
      */
-    private $plotArea;
+    private ?PlotArea $plotArea;
 
     /**
      * Plot Visible Only.
-     *
-     * @var bool
      */
-    private $plotVisibleOnly = true;
+    private bool $plotVisibleOnly;
 
     /**
      * Display Blanks as.
-     *
-     * @var string
      */
-    private $displayBlanksAs = DataSeries::EMPTY_AS_GAP;
+    private string $displayBlanksAs;
 
     /**
      * Chart Asix Y as.
-     *
-     * @var Axis
      */
-    private $yAxis;
+    private Axis $yAxis;
 
     /**
      * Chart Asix X as.
-     *
-     * @var Axis
      */
-    private $xAxis;
+    private Axis $xAxis;
 
     /**
      * Top-Left Cell Position.
-     *
-     * @var string
      */
-    private $topLeftCellRef = 'A1';
+    private string $topLeftCellRef = 'A1';
 
     /**
      * Top-Left X-Offset.
-     *
-     * @var int
      */
-    private $topLeftXOffset = 0;
+    private int $topLeftXOffset = 0;
 
     /**
      * Top-Left Y-Offset.
-     *
-     * @var int
      */
-    private $topLeftYOffset = 0;
+    private int $topLeftYOffset = 0;
 
     /**
      * Bottom-Right Cell Position.
-     *
-     * @var string
      */
-    private $bottomRightCellRef = '';
+    private string $bottomRightCellRef = '';
 
     /**
      * Bottom-Right X-Offset.
-     *
-     * @var int
      */
-    private $bottomRightXOffset = 10;
+    private int $bottomRightXOffset = 10;
 
     /**
      * Bottom-Right Y-Offset.
-     *
-     * @var int
      */
-    private $bottomRightYOffset = 10;
+    private int $bottomRightYOffset = 10;
 
-    /** @var ?int */
-    private $rotX;
+    private ?int $rotX = null;
 
-    /** @var ?int */
-    private $rotY;
+    private ?int $rotY = null;
 
-    /** @var ?int */
-    private $rAngAx;
+    private ?int $rAngAx = null;
 
-    /** @var ?int */
-    private $perspective;
+    private ?int $perspective = null;
 
-    /** @var bool */
-    private $oneCellAnchor = false;
+    private bool $oneCellAnchor = false;
 
-    /** @var bool */
-    private $autoTitleDeleted = false;
+    private bool $autoTitleDeleted = false;
 
-    /** @var bool */
-    private $noFill = false;
+    private bool $noFill = false;
 
-    /** @var bool */
-    private $roundedCorners = false;
+    private bool $roundedCorners = false;
+
+    private GridLines $borderLines;
+
+    private ChartColor $fillColor;
+
+    /**
+     * Rendered width in pixels.
+     */
+    private ?float $renderedWidth = null;
+
+    /**
+     * Rendered height in pixels.
+     */
+    private ?float $renderedHeight = null;
 
     /**
      * Create a new Chart.
      * majorGridlines and minorGridlines are deprecated, moved to Axis.
-     *
-     * @param mixed $name
-     * @param mixed $plotVisibleOnly
-     * @param string $displayBlanksAs
      */
-    public function __construct($name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, $plotVisibleOnly = true, $displayBlanksAs = DataSeries::EMPTY_AS_GAP, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null)
+    public function __construct(string $name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, bool $plotVisibleOnly = true, string $displayBlanksAs = DataSeries::EMPTY_AS_GAP, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null)
     {
         $this->name = $name;
         $this->title = $title;
@@ -176,14 +144,19 @@ class Chart
         if ($minorGridlines !== null) {
             $this->yAxis->setMinorGridlines($minorGridlines);
         }
+        $this->fillColor = new ChartColor();
+        $this->borderLines = new GridLines();
+    }
+
+    public function __destruct()
+    {
+        $this->worksheet = null;
     }
 
     /**
      * Get Name.
-     *
-     * @return string
      */
-    public function getName()
+    public function getName(): string
     {
         return $this->name;
     }
@@ -208,7 +181,7 @@ class Chart
      *
      * @return $this
      */
-    public function setWorksheet(?Worksheet $worksheet = null)
+    public function setWorksheet(?Worksheet $worksheet = null): static
     {
         $this->worksheet = $worksheet;
 
@@ -225,7 +198,7 @@ class Chart
      *
      * @return $this
      */
-    public function setTitle(Title $title)
+    public function setTitle(Title $title): static
     {
         $this->title = $title;
 
@@ -242,7 +215,7 @@ class Chart
      *
      * @return $this
      */
-    public function setLegend(Legend $legend)
+    public function setLegend(Legend $legend): static
     {
         $this->legend = $legend;
 
@@ -259,7 +232,7 @@ class Chart
      *
      * @return $this
      */
-    public function setXAxisLabel(Title $label)
+    public function setXAxisLabel(Title $label): static
     {
         $this->xAxisLabel = $label;
 
@@ -276,7 +249,7 @@ class Chart
      *
      * @return $this
      */
-    public function setYAxisLabel(Title $label)
+    public function setYAxisLabel(Title $label): static
     {
         $this->yAxisLabel = $label;
 
@@ -288,6 +261,16 @@ class Chart
         return $this->plotArea;
     }
 
+    public function getPlotAreaOrThrow(): PlotArea
+    {
+        $plotArea = $this->getPlotArea();
+        if ($plotArea !== null) {
+            return $plotArea;
+        }
+
+        throw new Exception('Chart has no PlotArea');
+    }
+
     /**
      * Set Plot Area.
      */
@@ -300,10 +283,8 @@ class Chart
 
     /**
      * Get Plot Visible Only.
-     *
-     * @return bool
      */
-    public function getPlotVisibleOnly()
+    public function getPlotVisibleOnly(): bool
     {
         return $this->plotVisibleOnly;
     }
@@ -311,11 +292,9 @@ class Chart
     /**
      * Set Plot Visible Only.
      *
-     * @param bool $plotVisibleOnly
-     *
      * @return $this
      */
-    public function setPlotVisibleOnly($plotVisibleOnly)
+    public function setPlotVisibleOnly(bool $plotVisibleOnly): static
     {
         $this->plotVisibleOnly = $plotVisibleOnly;
 
@@ -324,10 +303,8 @@ class Chart
 
     /**
      * Get Display Blanks as.
-     *
-     * @return string
      */
-    public function getDisplayBlanksAs()
+    public function getDisplayBlanksAs(): string
     {
         return $this->displayBlanksAs;
     }
@@ -335,11 +312,9 @@ class Chart
     /**
      * Set Display Blanks as.
      *
-     * @param string $displayBlanksAs
-     *
      * @return $this
      */
-    public function setDisplayBlanksAs($displayBlanksAs)
+    public function setDisplayBlanksAs(string $displayBlanksAs): static
     {
         $this->displayBlanksAs = $displayBlanksAs;
 
@@ -376,42 +351,12 @@ class Chart
         return $this;
     }
 
-    /**
-     * Get Major Gridlines.
-     *
-     * @deprecated 1.24.0 Use Axis->getMajorGridlines()
-     * @see Axis::getMajorGridlines()
-     *
-     * @codeCoverageIgnore
-     */
-    public function getMajorGridlines(): ?GridLines
-    {
-        return $this->yAxis->getMajorGridLines();
-    }
-
-    /**
-     * Get Minor Gridlines.
-     *
-     * @deprecated 1.24.0 Use Axis->getMinorGridlines()
-     * @see Axis::getMinorGridlines()
-     *
-     * @codeCoverageIgnore
-     */
-    public function getMinorGridlines(): ?GridLines
-    {
-        return $this->yAxis->getMinorGridLines();
-    }
-
     /**
      * Set the Top Left position for the chart.
      *
-     * @param string $cellAddress
-     * @param int $xOffset
-     * @param int $yOffset
-     *
      * @return $this
      */
-    public function setTopLeftPosition($cellAddress, $xOffset = null, $yOffset = null)
+    public function setTopLeftPosition(string $cellAddress, ?int $xOffset = null, ?int $yOffset = null): static
     {
         $this->topLeftCellRef = $cellAddress;
         if ($xOffset !== null) {
@@ -442,10 +387,8 @@ class Chart
 
     /**
      * Get the cell address where the top left of the chart is fixed.
-     *
-     * @return string
      */
-    public function getTopLeftCell()
+    public function getTopLeftCell(): string
     {
         return $this->topLeftCellRef;
     }
@@ -453,11 +396,9 @@ class Chart
     /**
      * Set the Top Left cell position for the chart.
      *
-     * @param string $cellAddress
-     *
      * @return $this
      */
-    public function setTopLeftCell($cellAddress)
+    public function setTopLeftCell(string $cellAddress): static
     {
         $this->topLeftCellRef = $cellAddress;
 
@@ -467,12 +408,9 @@ class Chart
     /**
      * Set the offset position within the Top Left cell for the chart.
      *
-     * @param ?int $xOffset
-     * @param ?int $yOffset
-     *
      * @return $this
      */
-    public function setTopLeftOffset($xOffset, $yOffset)
+    public function setTopLeftOffset(?int $xOffset, ?int $yOffset): static
     {
         if ($xOffset !== null) {
             $this->setTopLeftXOffset($xOffset);
@@ -490,7 +428,7 @@ class Chart
      *
      * @return int[]
      */
-    public function getTopLeftOffset()
+    public function getTopLeftOffset(): array
     {
         return [
             'X' => $this->topLeftXOffset,
@@ -499,11 +437,9 @@ class Chart
     }
 
     /**
-     * @param int $xOffset
-     *
      * @return $this
      */
-    public function setTopLeftXOffset($xOffset)
+    public function setTopLeftXOffset(int $xOffset): static
     {
         $this->topLeftXOffset = $xOffset;
 
@@ -516,11 +452,9 @@ class Chart
     }
 
     /**
-     * @param int $yOffset
-     *
      * @return $this
      */
-    public function setTopLeftYOffset($yOffset)
+    public function setTopLeftYOffset(int $yOffset): static
     {
         $this->topLeftYOffset = $yOffset;
 
@@ -535,13 +469,9 @@ class Chart
     /**
      * Set the Bottom Right position of the chart.
      *
-     * @param string $cellAddress
-     * @param int $xOffset
-     * @param int $yOffset
-     *
      * @return $this
      */
-    public function setBottomRightPosition($cellAddress = '', $xOffset = null, $yOffset = null)
+    public function setBottomRightPosition(string $cellAddress = '', ?int $xOffset = null, ?int $yOffset = null): static
     {
         $this->bottomRightCellRef = $cellAddress;
         if ($xOffset !== null) {
@@ -559,7 +489,7 @@ class Chart
      *
      * @return array an associative array containing the cell address, X-Offset and Y-Offset from the top left of that cell
      */
-    public function getBottomRightPosition()
+    public function getBottomRightPosition(): array
     {
         return [
             'cell' => $this->bottomRightCellRef,
@@ -573,7 +503,7 @@ class Chart
      *
      * @return $this
      */
-    public function setBottomRightCell(string $cellAddress = '')
+    public function setBottomRightCell(string $cellAddress = ''): static
     {
         $this->bottomRightCellRef = $cellAddress;
 
@@ -591,12 +521,9 @@ class Chart
     /**
      * Set the offset position within the Bottom Right cell for the chart.
      *
-     * @param ?int $xOffset
-     * @param ?int $yOffset
-     *
      * @return $this
      */
-    public function setBottomRightOffset($xOffset, $yOffset)
+    public function setBottomRightOffset(?int $xOffset, ?int $yOffset): static
     {
         if ($xOffset !== null) {
             $this->setBottomRightXOffset($xOffset);
@@ -614,7 +541,7 @@ class Chart
      *
      * @return int[]
      */
-    public function getBottomRightOffset()
+    public function getBottomRightOffset(): array
     {
         return [
             'X' => $this->bottomRightXOffset,
@@ -623,11 +550,9 @@ class Chart
     }
 
     /**
-     * @param int $xOffset
-     *
      * @return $this
      */
-    public function setBottomRightXOffset($xOffset)
+    public function setBottomRightXOffset(int $xOffset): static
     {
         $this->bottomRightXOffset = $xOffset;
 
@@ -640,11 +565,9 @@ class Chart
     }
 
     /**
-     * @param int $yOffset
-     *
      * @return $this
      */
-    public function setBottomRightYOffset($yOffset)
+    public function setBottomRightYOffset(int $yOffset): static
     {
         $this->bottomRightYOffset = $yOffset;
 
@@ -666,11 +589,11 @@ class Chart
     /**
      * Render the chart to given file (or stream).
      *
-     * @param string $outputDestination Name of the file render to
+     * @param ?string $outputDestination Name of the file render to
      *
      * @return bool true on success
      */
-    public function render($outputDestination = null)
+    public function render(?string $outputDestination = null): bool
     {
         if ($outputDestination == 'php://output') {
             $outputDestination = null;
@@ -686,7 +609,7 @@ class Chart
 
         $renderer = new $libraryName($this);
 
-        return $renderer->render($outputDestination); // @phpstan-ignore-line
+        return $renderer->render($outputDestination);
     }
 
     public function getRotX(): ?int
@@ -786,4 +709,62 @@ class Chart
 
         return $this;
     }
+
+    public function getBorderLines(): GridLines
+    {
+        return $this->borderLines;
+    }
+
+    public function setBorderLines(GridLines $borderLines): self
+    {
+        $this->borderLines = $borderLines;
+
+        return $this;
+    }
+
+    public function getFillColor(): ChartColor
+    {
+        return $this->fillColor;
+    }
+
+    public function setRenderedWidth(?float $width): self
+    {
+        $this->renderedWidth = $width;
+
+        return $this;
+    }
+
+    public function getRenderedWidth(): ?float
+    {
+        return $this->renderedWidth;
+    }
+
+    public function setRenderedHeight(?float $height): self
+    {
+        $this->renderedHeight = $height;
+
+        return $this;
+    }
+
+    public function getRenderedHeight(): ?float
+    {
+        return $this->renderedHeight;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $this->worksheet = null;
+        $this->title = ($this->title === null) ? null : clone $this->title;
+        $this->legend = ($this->legend === null) ? null : clone $this->legend;
+        $this->xAxisLabel = ($this->xAxisLabel === null) ? null : clone $this->xAxisLabel;
+        $this->yAxisLabel = ($this->yAxisLabel === null) ? null : clone $this->yAxisLabel;
+        $this->plotArea = ($this->plotArea === null) ? null : clone $this->plotArea;
+        $this->xAxis = clone $this->xAxis;
+        $this->yAxis = clone $this->yAxis;
+        $this->borderLines = clone $this->borderLines;
+        $this->fillColor = clone $this->fillColor;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php
index 87f3102..d6306de 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/ChartColor.php
@@ -7,25 +7,19 @@ class ChartColor
     const EXCEL_COLOR_TYPE_STANDARD = 'prstClr';
     const EXCEL_COLOR_TYPE_SCHEME = 'schemeClr';
     const EXCEL_COLOR_TYPE_RGB = 'srgbClr';
-    /** @deprecated 1.24 use EXCEL_COLOR_TYPE_RGB instead */
-    const EXCEL_COLOR_TYPE_ARGB = 'srgbClr';
     const EXCEL_COLOR_TYPES = [
-        self::EXCEL_COLOR_TYPE_ARGB,
+        self::EXCEL_COLOR_TYPE_RGB,
         self::EXCEL_COLOR_TYPE_SCHEME,
         self::EXCEL_COLOR_TYPE_STANDARD,
     ];
 
-    /** @var string */
-    private $value = '';
+    private string $value = '';
 
-    /** @var string */
-    private $type = '';
+    private string $type = '';
 
-    /** @var ?int */
-    private $alpha;
+    private ?int $alpha = null;
 
-    /** @var ?int */
-    private $brightness;
+    private ?int $brightness = null;
 
     /**
      * @param string|string[] $value
@@ -87,17 +81,13 @@ class ChartColor
         return $this;
     }
 
-    /**
-     * @param null|float|int|string $alpha
-     * @param null|float|int|string $brightness
-     */
-    public function setColorProperties(?string $color, $alpha = null, ?string $type = null, $brightness = null): self
+    public function setColorProperties(?string $color, null|float|int|string $alpha = null, ?string $type = null, null|float|int|string $brightness = null): self
     {
         if (empty($type) && !empty($color)) {
-            if (substr($color, 0, 1) === '*') {
+            if (str_starts_with($color, '*')) {
                 $type = 'schemeClr';
                 $color = substr($color, 1);
-            } elseif (substr($color, 0, 1) === '/') {
+            } elseif (str_starts_with($color, '/')) {
                 $type = 'prstClr';
                 $color = substr($color, 1);
             } elseif (preg_match('/^[0-9A-Fa-f]{6}$/', $color) === 1) {
@@ -141,12 +131,8 @@ class ChartColor
 
     /**
      * Get Color Property.
-     *
-     * @param string $propertyName
-     *
-     * @return null|int|string
      */
-    public function getColorProperty($propertyName)
+    public function getColorProperty(string $propertyName): null|int|string
     {
         $retVal = null;
         if ($propertyName === 'value') {
@@ -167,10 +153,7 @@ class ChartColor
         return (string) (100 - $alpha) . '000';
     }
 
-    /**
-     * @param float|int|string $alpha
-     */
-    public static function alphaFromXml($alpha): int
+    public static function alphaFromXml(float|int|string $alpha): int
     {
         return 100 - ((int) $alpha / 1000);
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
index 5d33e96..c7a9282 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeries.php
@@ -46,89 +46,83 @@ class DataSeries
 
     /**
      * Series Plot Type.
-     *
-     * @var string
      */
-    private $plotType;
+    private ?string $plotType;
 
     /**
      * Plot Grouping Type.
-     *
-     * @var string
      */
-    private $plotGrouping;
+    private ?string $plotGrouping;
 
     /**
      * Plot Direction.
-     *
-     * @var string
      */
-    private $plotDirection;
+    private string $plotDirection;
 
     /**
      * Plot Style.
-     *
-     * @var null|string
      */
-    private $plotStyle;
+    private ?string $plotStyle;
 
     /**
      * Order of plots in Series.
      *
      * @var int[]
      */
-    private $plotOrder = [];
+    private array $plotOrder;
 
     /**
      * Plot Label.
      *
      * @var DataSeriesValues[]
      */
-    private $plotLabel = [];
+    private array $plotLabel;
 
     /**
      * Plot Category.
      *
      * @var DataSeriesValues[]
      */
-    private $plotCategory = [];
+    private array $plotCategory;
 
     /**
      * Smooth Line. Must be specified for both DataSeries and DataSeriesValues.
-     *
-     * @var bool
      */
-    private $smoothLine;
+    private bool $smoothLine;
 
     /**
      * Plot Values.
      *
      * @var DataSeriesValues[]
      */
-    private $plotValues = [];
+    private array $plotValues;
 
     /**
      * Plot Bubble Sizes.
      *
      * @var DataSeriesValues[]
      */
-    private $plotBubbleSizes = [];
+    private array $plotBubbleSizes = [];
 
     /**
      * Create a new DataSeries.
      *
-     * @param null|mixed $plotType
-     * @param null|mixed $plotGrouping
      * @param int[] $plotOrder
      * @param DataSeriesValues[] $plotLabel
      * @param DataSeriesValues[] $plotCategory
      * @param DataSeriesValues[] $plotValues
-     * @param null|string $plotDirection
-     * @param bool $smoothLine
-     * @param null|string $plotStyle
      */
-    public function __construct($plotType = null, $plotGrouping = null, array $plotOrder = [], array $plotLabel = [], array $plotCategory = [], array $plotValues = [], $plotDirection = null, $smoothLine = false, $plotStyle = null)
-    {
+    public function __construct(
+        null|string $plotType = null,
+        null|string $plotGrouping = null,
+        array $plotOrder = [],
+        array $plotLabel = [],
+        array $plotCategory = [],
+        array $plotValues = [],
+        ?string $plotDirection = null,
+        bool $smoothLine = false,
+        ?string $plotStyle = null
+    ) {
         $this->plotType = $plotType;
         $this->plotGrouping = $plotGrouping;
         $this->plotOrder = $plotOrder;
@@ -144,7 +138,7 @@ class DataSeries
         }
         $this->plotCategory = $plotCategory;
 
-        $this->smoothLine = $smoothLine;
+        $this->smoothLine = (bool) $smoothLine;
         $this->plotStyle = $plotStyle;
 
         if ($plotDirection === null) {
@@ -155,10 +149,8 @@ class DataSeries
 
     /**
      * Get Plot Type.
-     *
-     * @return string
      */
-    public function getPlotType()
+    public function getPlotType(): ?string
     {
         return $this->plotType;
     }
@@ -166,11 +158,9 @@ class DataSeries
     /**
      * Set Plot Type.
      *
-     * @param string $plotType
-     *
      * @return $this
      */
-    public function setPlotType($plotType)
+    public function setPlotType(string $plotType): static
     {
         $this->plotType = $plotType;
 
@@ -179,10 +169,8 @@ class DataSeries
 
     /**
      * Get Plot Grouping Type.
-     *
-     * @return string
      */
-    public function getPlotGrouping()
+    public function getPlotGrouping(): ?string
     {
         return $this->plotGrouping;
     }
@@ -190,11 +178,9 @@ class DataSeries
     /**
      * Set Plot Grouping Type.
      *
-     * @param string $groupingType
-     *
      * @return $this
      */
-    public function setPlotGrouping($groupingType)
+    public function setPlotGrouping(string $groupingType): static
     {
         $this->plotGrouping = $groupingType;
 
@@ -203,10 +189,8 @@ class DataSeries
 
     /**
      * Get Plot Direction.
-     *
-     * @return string
      */
-    public function getPlotDirection()
+    public function getPlotDirection(): string
     {
         return $this->plotDirection;
     }
@@ -214,11 +198,9 @@ class DataSeries
     /**
      * Set Plot Direction.
      *
-     * @param string $plotDirection
-     *
      * @return $this
      */
-    public function setPlotDirection($plotDirection)
+    public function setPlotDirection(string $plotDirection): static
     {
         $this->plotDirection = $plotDirection;
 
@@ -230,7 +212,7 @@ class DataSeries
      *
      * @return int[]
      */
-    public function getPlotOrder()
+    public function getPlotOrder(): array
     {
         return $this->plotOrder;
     }
@@ -240,7 +222,7 @@ class DataSeries
      *
      * @return DataSeriesValues[]
      */
-    public function getPlotLabels()
+    public function getPlotLabels(): array
     {
         return $this->plotLabel;
     }
@@ -248,11 +230,9 @@ class DataSeries
     /**
      * Get Plot Label by Index.
      *
-     * @param mixed $index
-     *
      * @return DataSeriesValues|false
      */
-    public function getPlotLabelByIndex($index)
+    public function getPlotLabelByIndex(int $index): bool|DataSeriesValues
     {
         $keys = array_keys($this->plotLabel);
         if (in_array($index, $keys)) {
@@ -267,7 +247,7 @@ class DataSeries
      *
      * @return DataSeriesValues[]
      */
-    public function getPlotCategories()
+    public function getPlotCategories(): array
     {
         return $this->plotCategory;
     }
@@ -275,11 +255,9 @@ class DataSeries
     /**
      * Get Plot Category by Index.
      *
-     * @param mixed $index
-     *
      * @return DataSeriesValues|false
      */
-    public function getPlotCategoryByIndex($index)
+    public function getPlotCategoryByIndex(int $index): bool|DataSeriesValues
     {
         $keys = array_keys($this->plotCategory);
         if (in_array($index, $keys)) {
@@ -293,10 +271,8 @@ class DataSeries
 
     /**
      * Get Plot Style.
-     *
-     * @return null|string
      */
-    public function getPlotStyle()
+    public function getPlotStyle(): ?string
     {
         return $this->plotStyle;
     }
@@ -304,11 +280,9 @@ class DataSeries
     /**
      * Set Plot Style.
      *
-     * @param null|string $plotStyle
-     *
      * @return $this
      */
-    public function setPlotStyle($plotStyle)
+    public function setPlotStyle(?string $plotStyle): static
     {
         $this->plotStyle = $plotStyle;
 
@@ -320,7 +294,7 @@ class DataSeries
      *
      * @return DataSeriesValues[]
      */
-    public function getPlotValues()
+    public function getPlotValues(): array
     {
         return $this->plotValues;
     }
@@ -328,11 +302,9 @@ class DataSeries
     /**
      * Get Plot Values by Index.
      *
-     * @param mixed $index
-     *
      * @return DataSeriesValues|false
      */
-    public function getPlotValuesByIndex($index)
+    public function getPlotValuesByIndex(int $index): bool|DataSeriesValues
     {
         $keys = array_keys($this->plotValues);
         if (in_array($index, $keys)) {
@@ -366,20 +338,16 @@ class DataSeries
 
     /**
      * Get Number of Plot Series.
-     *
-     * @return int
      */
-    public function getPlotSeriesCount()
+    public function getPlotSeriesCount(): int
     {
         return count($this->plotValues);
     }
 
     /**
      * Get Smooth Line.
-     *
-     * @return bool
      */
-    public function getSmoothLine()
+    public function getSmoothLine(): bool
     {
         return $this->smoothLine;
     }
@@ -387,11 +355,9 @@ class DataSeries
     /**
      * Set Smooth Line.
      *
-     * @param bool $smoothLine
-     *
      * @return $this
      */
-    public function setSmoothLine($smoothLine)
+    public function setSmoothLine(bool $smoothLine): static
     {
         $this->smoothLine = $smoothLine;
 
@@ -416,4 +382,31 @@ class DataSeries
             }
         }
     }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $plotLabels = $this->plotLabel;
+        $this->plotLabel = [];
+        foreach ($plotLabels as $plotLabel) {
+            $this->plotLabel[] = $plotLabel;
+        }
+        $plotCategories = $this->plotCategory;
+        $this->plotCategory = [];
+        foreach ($plotCategories as $plotCategory) {
+            $this->plotCategory[] = clone $plotCategory;
+        }
+        $plotValues = $this->plotValues;
+        $this->plotValues = [];
+        foreach ($plotValues as $plotValue) {
+            $this->plotValues[] = clone $plotValue;
+        }
+        $plotBubbleSizes = $this->plotBubbleSizes;
+        $this->plotBubbleSizes = [];
+        foreach ($plotBubbleSizes as $plotBubbleSize) {
+            $this->plotBubbleSizes[] = clone $plotBubbleSize;
+        }
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
index c86f556..70f90bf 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/DataSeriesValues.php
@@ -19,58 +19,42 @@ class DataSeriesValues extends Properties
 
     /**
      * Series Data Type.
-     *
-     * @var string
      */
-    private $dataType;
+    private string $dataType;
 
     /**
      * Series Data Source.
-     *
-     * @var ?string
      */
-    private $dataSource;
+    private ?string $dataSource;
 
     /**
      * Format Code.
-     *
-     * @var string
      */
-    private $formatCode;
+    private ?string $formatCode;
 
     /**
      * Series Point Marker.
-     *
-     * @var string
      */
-    private $pointMarker;
+    private ?string $pointMarker;
 
-    /** @var ChartColor */
-    private $markerFillColor;
+    private ChartColor $markerFillColor;
 
-    /** @var ChartColor */
-    private $markerBorderColor;
+    private ChartColor $markerBorderColor;
 
     /**
      * Series Point Size.
-     *
-     * @var int
      */
-    private $pointSize = 3;
+    private int $pointSize = 3;
 
     /**
      * Point Count (The number of datapoints in the dataseries).
-     *
-     * @var int
      */
-    private $pointCount = 0;
+    private int $pointCount;
 
     /**
      * Data Values.
-     *
-     * @var mixed[]
      */
-    private $dataValues = [];
+    private ?array $dataValues;
 
     /**
      * Fill color (can be array with colors if dataseries have custom colors).
@@ -79,32 +63,30 @@ class DataSeriesValues extends Properties
      */
     private $fillColor;
 
-    /** @var bool */
-    private $scatterLines = true;
+    private bool $scatterLines = true;
 
-    /** @var bool */
-    private $bubble3D = false;
+    private bool $bubble3D = false;
 
-    /** @var ?Layout */
-    private $labelLayout;
+    private ?Layout $labelLayout = null;
 
     /** @var TrendLine[] */
-    private $trendLines = [];
+    private array $trendLines = [];
 
     /**
      * Create a new DataSeriesValues object.
      *
-     * @param string $dataType
-     * @param string $dataSource
-     * @param null|mixed $formatCode
-     * @param int $pointCount
-     * @param mixed $dataValues
-     * @param null|mixed $marker
      * @param null|ChartColor|ChartColor[]|string|string[] $fillColor
-     * @param string $pointSize
      */
-    public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null, $pointSize = '3')
-    {
+    public function __construct(
+        string $dataType = self::DATASERIES_TYPE_NUMBER,
+        ?string $dataSource = null,
+        ?string $formatCode = null,
+        int $pointCount = 0,
+        ?array $dataValues = [],
+        ?string $marker = null,
+        null|ChartColor|array|string $fillColor = null,
+        int|string $pointSize = 3
+    ) {
         parent::__construct();
         $this->markerFillColor = new ChartColor();
         $this->markerBorderColor = new ChartColor();
@@ -124,10 +106,8 @@ class DataSeriesValues extends Properties
 
     /**
      * Get Series Data Type.
-     *
-     * @return string
      */
-    public function getDataType()
+    public function getDataType(): string
     {
         return $this->dataType;
     }
@@ -144,7 +124,7 @@ class DataSeriesValues extends Properties
      *
      * @return $this
      */
-    public function setDataType($dataType)
+    public function setDataType(string $dataType): static
     {
         if (!in_array($dataType, self::DATA_TYPE_VALUES)) {
             throw new Exception('Invalid datatype for chart data series values');
@@ -156,10 +136,8 @@ class DataSeriesValues extends Properties
 
     /**
      * Get Series Data Source (formula).
-     *
-     * @return ?string
      */
-    public function getDataSource()
+    public function getDataSource(): ?string
     {
         return $this->dataSource;
     }
@@ -167,11 +145,9 @@ class DataSeriesValues extends Properties
     /**
      * Set Series Data Source (formula).
      *
-     * @param ?string $dataSource
-     *
      * @return $this
      */
-    public function setDataSource($dataSource)
+    public function setDataSource(?string $dataSource): static
     {
         $this->dataSource = $dataSource;
 
@@ -180,10 +156,8 @@ class DataSeriesValues extends Properties
 
     /**
      * Get Point Marker.
-     *
-     * @return string
      */
-    public function getPointMarker()
+    public function getPointMarker(): ?string
     {
         return $this->pointMarker;
     }
@@ -191,11 +165,9 @@ class DataSeriesValues extends Properties
     /**
      * Set Point Marker.
      *
-     * @param string $marker
-     *
      * @return $this
      */
-    public function setPointMarker($marker)
+    public function setPointMarker(string $marker): static
     {
         $this->pointMarker = $marker;
 
@@ -225,7 +197,7 @@ class DataSeriesValues extends Properties
      *
      * @return $this
      */
-    public function setPointSize(int $size = 3)
+    public function setPointSize(int $size = 3): static
     {
         $this->pointSize = $size;
 
@@ -234,10 +206,8 @@ class DataSeriesValues extends Properties
 
     /**
      * Get Series Format Code.
-     *
-     * @return string
      */
-    public function getFormatCode()
+    public function getFormatCode(): ?string
     {
         return $this->formatCode;
     }
@@ -245,11 +215,9 @@ class DataSeriesValues extends Properties
     /**
      * Set Series Format Code.
      *
-     * @param string $formatCode
-     *
      * @return $this
      */
-    public function setFormatCode($formatCode)
+    public function setFormatCode(string $formatCode): static
     {
         $this->formatCode = $formatCode;
 
@@ -258,10 +226,8 @@ class DataSeriesValues extends Properties
 
     /**
      * Get Series Point Count.
-     *
-     * @return int
      */
-    public function getPointCount()
+    public function getPointCount(): int
     {
         return $this->pointCount;
     }
@@ -279,10 +245,10 @@ class DataSeriesValues extends Properties
     private function stringToChartColor(string $fillString): ChartColor
     {
         $value = $type = '';
-        if (substr($fillString, 0, 1) === '*') {
+        if (str_starts_with($fillString, '*')) {
             $type = 'schemeClr';
             $value = substr($fillString, 1);
-        } elseif (substr($fillString, 0, 1) === '/') {
+        } elseif (str_starts_with($fillString, '/')) {
             $type = 'prstClr';
             $value = substr($fillString, 1);
         } elseif ($fillString !== '') {
@@ -316,7 +282,7 @@ class DataSeriesValues extends Properties
      *
      * @return string|string[] HEX color or array with HEX colors
      */
-    public function getFillColor()
+    public function getFillColor(): string|array
     {
         if ($this->fillColor === null) {
             return '';
@@ -338,9 +304,9 @@ class DataSeriesValues extends Properties
      *
      * @param ChartColor|ChartColor[]|string|string[] $color HEX color or array with HEX colors
      *
-     * @return   DataSeriesValues
+     * @return   $this
      */
-    public function setFillColor($color)
+    public function setFillColor($color): static
     {
         if (is_array($color)) {
             $this->fillColor = [];
@@ -367,7 +333,7 @@ class DataSeriesValues extends Properties
      *
      * @return bool true if validation was successful
      */
-    private function validateColor($color)
+    private function validateColor(string $color): bool
     {
         if (!preg_match('/^[a-f0-9]{6}$/i', $color)) {
             throw new Exception(sprintf('Invalid hex color for chart series (color: "%s")', $color));
@@ -378,10 +344,8 @@ class DataSeriesValues extends Properties
 
     /**
      * Get line width for series.
-     *
-     * @return null|float|int
      */
-    public function getLineWidth()
+    public function getLineWidth(): null|float|int
     {
         return $this->lineStyleProperties['width'];
     }
@@ -389,11 +353,9 @@ class DataSeriesValues extends Properties
     /**
      * Set line width for the series.
      *
-     * @param null|float|int $width
-     *
      * @return $this
      */
-    public function setLineWidth($width)
+    public function setLineWidth(null|float|int $width): static
     {
         $this->lineStyleProperties['width'] = $width;
 
@@ -402,10 +364,8 @@ class DataSeriesValues extends Properties
 
     /**
      * Identify if the Data Series is a multi-level or a simple series.
-     *
-     * @return null|bool
      */
-    public function isMultiLevelSeries()
+    public function isMultiLevelSeries(): ?bool
     {
         if (!empty($this->dataValues)) {
             return is_array(array_values($this->dataValues)[0]);
@@ -416,13 +376,11 @@ class DataSeriesValues extends Properties
 
     /**
      * Return the level count of a multi-level Data Series.
-     *
-     * @return int
      */
-    public function multiLevelCount()
+    public function multiLevelCount(): int
     {
         $levelCount = 0;
-        foreach ($this->dataValues as $dataValueSet) {
+        foreach (($this->dataValues ?? []) as $dataValueSet) {
             $levelCount = max($levelCount, count($dataValueSet));
         }
 
@@ -431,21 +389,20 @@ class DataSeriesValues extends Properties
 
     /**
      * Get Series Data Values.
-     *
-     * @return mixed[]
      */
-    public function getDataValues()
+    public function getDataValues(): ?array
     {
         return $this->dataValues;
     }
 
     /**
      * Get the first Series Data value.
-     *
-     * @return mixed
      */
-    public function getDataValue()
+    public function getDataValue(): mixed
     {
+        if ($this->dataValues === null) {
+            return null;
+        }
         $count = count($this->dataValues);
         if ($count == 0) {
             return null;
@@ -459,11 +416,9 @@ class DataSeriesValues extends Properties
     /**
      * Set Series Data Values.
      *
-     * @param array $dataValues
-     *
      * @return $this
      */
-    public function setDataValues($dataValues)
+    public function setDataValues(array $dataValues): static
     {
         $this->dataValues = Functions::flattenArray($dataValues);
         $this->pointCount = count($dataValues);
@@ -492,16 +447,18 @@ class DataSeriesValues extends Properties
                 unset($dataValue);
             } else {
                 [$worksheet, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
-                $dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange));
+                $dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange ?? ''));
                 if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
                     $this->dataValues = Functions::flattenArray($newDataValues);
                 } else {
-                    $newArray = array_values(array_shift(/** @scrutinizer ignore-type */ $newDataValues));
+                    /** @var array<int, array> */
+                    $newDataValuesx = $newDataValues;
+                    $newArray = array_values(array_shift($newDataValuesx) ?? []);
                     foreach ($newArray as $i => $newDataSet) {
                         $newArray[$i] = [$newDataSet];
                     }
 
-                    foreach ($newDataValues as $newDataSet) {
+                    foreach ($newDataValuesx as $newDataSet) {
                         $i = 0;
                         foreach ($newDataSet as $newDataVal) {
                             array_unshift($newArray[$i++], $newDataVal);
@@ -540,17 +497,13 @@ class DataSeriesValues extends Properties
 
     /**
      * Smooth Line. Must be specified for both DataSeries and DataSeriesValues.
-     *
-     * @var bool
      */
-    private $smoothLine;
+    private bool $smoothLine = false;
 
     /**
      * Get Smooth Line.
-     *
-     * @return bool
      */
-    public function getSmoothLine()
+    public function getSmoothLine(): bool
     {
         return $this->smoothLine;
     }
@@ -558,11 +511,9 @@ class DataSeriesValues extends Properties
     /**
      * Set Smooth Line.
      *
-     * @param bool $smoothLine
-     *
      * @return $this
      */
-    public function setSmoothLine($smoothLine)
+    public function setSmoothLine(bool $smoothLine): static
     {
         $this->smoothLine = $smoothLine;
 
@@ -592,4 +543,29 @@ class DataSeriesValues extends Properties
     {
         return $this->trendLines;
     }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        parent::__clone();
+        $this->markerFillColor = clone $this->markerFillColor;
+        $this->markerBorderColor = clone $this->markerBorderColor;
+        if (is_array($this->fillColor)) {
+            $fillColor = $this->fillColor;
+            $this->fillColor = [];
+            foreach ($fillColor as $color) {
+                $this->fillColor[] = clone $color;
+            }
+        } elseif ($this->fillColor instanceof ChartColor) {
+            $this->fillColor = clone $this->fillColor;
+        }
+        $this->labelLayout = ($this->labelLayout === null) ? null : clone $this->labelLayout;
+        $trendLines = $this->trendLines;
+        $this->trendLines = [];
+        foreach ($trendLines as $trendLine) {
+            $this->trendLines[] = clone $trendLine;
+        }
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
index 0018d79..570892e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
@@ -2,133 +2,102 @@
 
 namespace PhpOffice\PhpSpreadsheet\Chart;
 
+use PhpOffice\PhpSpreadsheet\Style\Font;
+
 class Layout
 {
     /**
      * layoutTarget.
-     *
-     * @var ?string
      */
-    private $layoutTarget;
+    private ?string $layoutTarget = null;
 
     /**
      * X Mode.
-     *
-     * @var ?string
      */
-    private $xMode;
+    private ?string $xMode = null;
 
     /**
      * Y Mode.
-     *
-     * @var ?string
      */
-    private $yMode;
+    private ?string $yMode = null;
 
     /**
      * X-Position.
-     *
-     * @var ?float
      */
-    private $xPos;
+    private ?float $xPos = null;
 
     /**
      * Y-Position.
-     *
-     * @var ?float
      */
-    private $yPos;
+    private ?float $yPos = null;
 
     /**
      * width.
-     *
-     * @var ?float
      */
-    private $width;
+    private ?float $width = null;
 
     /**
      * height.
-     *
-     * @var ?float
      */
-    private $height;
+    private ?float $height = null;
 
     /**
      * Position - t=top.
-     *
-     * @var string
      */
-    private $dLblPos = '';
+    private string $dLblPos = '';
 
-    /** @var string */
-    private $numFmtCode = '';
+    private string $numFmtCode = '';
 
-    /** @var bool */
-    private $numFmtLinked = false;
+    private bool $numFmtLinked = false;
 
     /**
      * show legend key
      * Specifies that legend keys should be shown in data labels.
-     *
-     * @var ?bool
      */
-    private $showLegendKey;
+    private ?bool $showLegendKey = null;
 
     /**
      * show value
      * Specifies that the value should be shown in a data label.
-     *
-     * @var ?bool
      */
-    private $showVal;
+    private ?bool $showVal = null;
 
     /**
      * show category name
      * Specifies that the category name should be shown in the data label.
-     *
-     * @var ?bool
      */
-    private $showCatName;
+    private ?bool $showCatName = null;
 
     /**
      * show data series name
      * Specifies that the series name should be shown in the data label.
-     *
-     * @var ?bool
      */
-    private $showSerName;
+    private ?bool $showSerName = null;
 
     /**
      * show percentage
      * Specifies that the percentage should be shown in the data label.
-     *
-     * @var ?bool
      */
-    private $showPercent;
+    private ?bool $showPercent = null;
 
     /**
      * show bubble size.
-     *
-     * @var ?bool
      */
-    private $showBubbleSize;
+    private ?bool $showBubbleSize = null;
 
     /**
      * show leader lines
      * Specifies that leader lines should be shown for the data label.
-     *
-     * @var ?bool
      */
-    private $showLeaderLines;
+    private ?bool $showLeaderLines = null;
 
-    /** @var ?ChartColor */
-    private $labelFillColor;
+    private ?ChartColor $labelFillColor = null;
 
-    /** @var ?ChartColor */
-    private $labelBorderColor;
+    private ?ChartColor $labelBorderColor = null;
 
-    /** @var ?ChartColor */
-    private $labelFontColor;
+    private ?Font $labelFont = null;
+
+    private ?Properties $labelEffects = null;
 
     /**
      * Create a new Layout.
@@ -172,7 +141,18 @@ class Layout
         $this->initBoolean($layout, 'numFmtLinked');
         $this->initColor($layout, 'labelFillColor');
         $this->initColor($layout, 'labelBorderColor');
-        $this->initColor($layout, 'labelFontColor');
+        $labelFont = $layout['labelFont'] ?? null;
+        if ($labelFont instanceof Font) {
+            $this->labelFont = $labelFont;
+        }
+        $labelFontColor = $layout['labelFontColor'] ?? null;
+        if ($labelFontColor instanceof ChartColor) {
+            $this->setLabelFontColor($labelFontColor);
+        }
+        $labelEffects = $layout['labelEffects'] ?? null;
+        if ($labelEffects instanceof Properties) {
+            $this->labelEffects = $labelEffects;
+        }
     }
 
     private function initBoolean(array $layout, string $name): void
@@ -191,10 +171,8 @@ class Layout
 
     /**
      * Get Layout Target.
-     *
-     * @return ?string
      */
-    public function getLayoutTarget()
+    public function getLayoutTarget(): ?string
     {
         return $this->layoutTarget;
     }
@@ -202,11 +180,9 @@ class Layout
     /**
      * Set Layout Target.
      *
-     * @param ?string $target
-     *
      * @return $this
      */
-    public function setLayoutTarget($target)
+    public function setLayoutTarget(?string $target): static
     {
         $this->layoutTarget = $target;
 
@@ -215,10 +191,8 @@ class Layout
 
     /**
      * Get X-Mode.
-     *
-     * @return ?string
      */
-    public function getXMode()
+    public function getXMode(): ?string
     {
         return $this->xMode;
     }
@@ -226,11 +200,9 @@ class Layout
     /**
      * Set X-Mode.
      *
-     * @param ?string $mode
-     *
      * @return $this
      */
-    public function setXMode($mode)
+    public function setXMode(?string $mode): static
     {
         $this->xMode = (string) $mode;
 
@@ -239,10 +211,8 @@ class Layout
 
     /**
      * Get Y-Mode.
-     *
-     * @return ?string
      */
-    public function getYMode()
+    public function getYMode(): ?string
     {
         return $this->yMode;
     }
@@ -250,11 +220,9 @@ class Layout
     /**
      * Set Y-Mode.
      *
-     * @param ?string $mode
-     *
      * @return $this
      */
-    public function setYMode($mode)
+    public function setYMode(?string $mode): static
     {
         $this->yMode = (string) $mode;
 
@@ -263,10 +231,8 @@ class Layout
 
     /**
      * Get X-Position.
-     *
-     * @return null|float|int
      */
-    public function getXPosition()
+    public function getXPosition(): null|float|int
     {
         return $this->xPos;
     }
@@ -274,23 +240,19 @@ class Layout
     /**
      * Set X-Position.
      *
-     * @param ?float $position
-     *
      * @return $this
      */
-    public function setXPosition($position)
+    public function setXPosition(float $position): static
     {
-        $this->xPos = (float) $position;
+        $this->xPos = $position;
 
         return $this;
     }
 
     /**
      * Get Y-Position.
-     *
-     * @return null|float
      */
-    public function getYPosition()
+    public function getYPosition(): ?float
     {
         return $this->yPos;
     }
@@ -298,23 +260,19 @@ class Layout
     /**
      * Set Y-Position.
      *
-     * @param ?float $position
-     *
      * @return $this
      */
-    public function setYPosition($position)
+    public function setYPosition(float $position): static
     {
-        $this->yPos = (float) $position;
+        $this->yPos = $position;
 
         return $this;
     }
 
     /**
      * Get Width.
-     *
-     * @return ?float
      */
-    public function getWidth()
+    public function getWidth(): ?float
     {
         return $this->width;
     }
@@ -322,11 +280,9 @@ class Layout
     /**
      * Set Width.
      *
-     * @param ?float $width
-     *
      * @return $this
      */
-    public function setWidth($width)
+    public function setWidth(?float $width): static
     {
         $this->width = $width;
 
@@ -335,10 +291,8 @@ class Layout
 
     /**
      * Get Height.
-     *
-     * @return null|float
      */
-    public function getHeight()
+    public function getHeight(): ?float
     {
         return $this->height;
     }
@@ -346,11 +300,9 @@ class Layout
     /**
      * Set Height.
      *
-     * @param ?float $height
-     *
      * @return $this
      */
-    public function setHeight($height)
+    public function setHeight(?float $height): static
     {
         $this->height = $height;
 
@@ -493,14 +445,32 @@ class Layout
         return $this;
     }
 
+    public function getLabelFont(): ?Font
+    {
+        return $this->labelFont;
+    }
+
+    public function getLabelEffects(): ?Properties
+    {
+        return $this->labelEffects;
+    }
+
     public function getLabelFontColor(): ?ChartColor
     {
-        return $this->labelFontColor;
+        if ($this->labelFont === null) {
+            return null;
+        }
+
+        return $this->labelFont->getChartColor();
     }
 
     public function setLabelFontColor(?ChartColor $chartColor): self
     {
-        $this->labelFontColor = $chartColor;
+        if ($this->labelFont === null) {
+            $this->labelFont = new Font();
+            $this->labelFont->setSize(null, true);
+        }
+        $this->labelFont->setChartColorFromObject($chartColor);
 
         return $this;
     }
@@ -540,4 +510,15 @@ class Layout
 
         return $this;
     }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $this->labelFillColor = ($this->labelFillColor === null) ? null : clone $this->labelFillColor;
+        $this->labelBorderColor = ($this->labelBorderColor === null) ? null : clone $this->labelBorderColor;
+        $this->labelFont = ($this->labelFont === null) ? null : clone $this->labelFont;
+        $this->labelEffects = ($this->labelEffects === null) ? null : clone $this->labelEffects;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php
index e499e77..7736fb7 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Legend.php
@@ -29,44 +29,46 @@ class Legend
 
     /**
      * Legend position.
-     *
-     * @var string
      */
-    private $position = self::POSITION_RIGHT;
+    private string $position = self::POSITION_RIGHT;
 
     /**
      * Allow overlay of other elements?
-     *
-     * @var bool
      */
-    private $overlay = true;
+    private bool $overlay = true;
 
     /**
      * Legend Layout.
-     *
-     * @var ?Layout
      */
-    private $layout;
+    private ?Layout $layout;
+
+    private GridLines $borderLines;
+
+    private ChartColor $fillColor;
+
+    private ?AxisText $legendText = null;
 
     /**
      * Create a new Legend.
-     *
-     * @param string $position
-     * @param bool $overlay
      */
-    public function __construct($position = self::POSITION_RIGHT, ?Layout $layout = null, $overlay = false)
+    public function __construct(string $position = self::POSITION_RIGHT, ?Layout $layout = null, bool $overlay = false)
     {
         $this->setPosition($position);
         $this->layout = $layout;
         $this->setOverlay($overlay);
+        $this->borderLines = new GridLines();
+        $this->fillColor = new ChartColor();
+    }
+
+    public function getFillColor(): ChartColor
+    {
+        return $this->fillColor;
     }
 
     /**
      * Get legend position as an excel string value.
-     *
-     * @return string
      */
-    public function getPosition()
+    public function getPosition(): string
     {
         return $this->position;
     }
@@ -75,10 +77,8 @@ class Legend
      * Get legend position using an excel string value.
      *
      * @param string $position see self::POSITION_*
-     *
-     * @return bool
      */
-    public function setPosition($position)
+    public function setPosition(string $position): bool
     {
         if (!in_array($position, self::POSITION_XLREF)) {
             return false;
@@ -91,12 +91,9 @@ class Legend
 
     /**
      * Get legend position as an Excel internal numeric value.
-     *
-     * @return false|int
      */
-    public function getPositionXL()
+    public function getPositionXL(): false|int
     {
-        // Scrutinizer thinks the following could return string. It is wrong.
         return array_search($this->position, self::POSITION_XLREF);
     }
 
@@ -104,10 +101,8 @@ class Legend
      * Set legend position using an Excel internal numeric value.
      *
      * @param int $positionXL see self::XL_LEGEND_POSITION_*
-     *
-     * @return bool
      */
-    public function setPositionXL($positionXL)
+    public function setPositionXL(int $positionXL): bool
     {
         if (!isset(self::POSITION_XLREF[$positionXL])) {
             return false;
@@ -120,31 +115,60 @@ class Legend
 
     /**
      * Get allow overlay of other elements?
-     *
-     * @return bool
      */
-    public function getOverlay()
+    public function getOverlay(): bool
     {
         return $this->overlay;
     }
 
     /**
      * Set allow overlay of other elements?
-     *
-     * @param bool $overlay
      */
-    public function setOverlay($overlay): void
+    public function setOverlay(bool $overlay): void
     {
         $this->overlay = $overlay;
     }
 
     /**
      * Get Layout.
-     *
-     * @return ?Layout
      */
-    public function getLayout()
+    public function getLayout(): ?Layout
     {
         return $this->layout;
     }
+
+    public function getLegendText(): ?AxisText
+    {
+        return $this->legendText;
+    }
+
+    public function setLegendText(?AxisText $legendText): self
+    {
+        $this->legendText = $legendText;
+
+        return $this;
+    }
+
+    public function getBorderLines(): GridLines
+    {
+        return $this->borderLines;
+    }
+
+    public function setBorderLines(GridLines $borderLines): self
+    {
+        $this->borderLines = $borderLines;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $this->layout = ($this->layout === null) ? null : clone $this->layout;
+        $this->legendText = ($this->legendText === null) ? null : clone $this->legendText;
+        $this->borderLines = clone $this->borderLines;
+        $this->fillColor = clone $this->fillColor;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
index ccde4bb..228afad 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
@@ -8,10 +8,8 @@ class PlotArea
 {
     /**
      * No fill in plot area (show Excel gridlines through chart).
-     *
-     * @var bool
      */
-    private $noFill = false;
+    private bool $noFill = false;
 
     /**
      * PlotArea Gradient Stop list.
@@ -21,28 +19,24 @@ class PlotArea
      *
      * @var array[]
      */
-    private $gradientFillStops = [];
+    private array $gradientFillStops = [];
 
     /**
      * PlotArea Gradient Angle.
-     *
-     * @var ?float
      */
-    private $gradientFillAngle;
+    private ?float $gradientFillAngle = null;
 
     /**
      * PlotArea Layout.
-     *
-     * @var ?Layout
      */
-    private $layout;
+    private ?Layout $layout;
 
     /**
      * Plot Series.
      *
      * @var DataSeries[]
      */
-    private $plotSeries = [];
+    private array $plotSeries;
 
     /**
      * Create a new PlotArea.
@@ -70,10 +64,8 @@ class PlotArea
 
     /**
      * Get Number of Plot Series.
-     *
-     * @return int
      */
-    public function getPlotSeriesCount()
+    public function getPlotSeriesCount(): int|float
     {
         $seriesCount = 0;
         foreach ($this->plotSeries as $plot) {
@@ -88,19 +80,15 @@ class PlotArea
      *
      * @return DataSeries[]
      */
-    public function getPlotGroup()
+    public function getPlotGroup(): array
     {
         return $this->plotSeries;
     }
 
     /**
      * Get Plot Series by Index.
-     *
-     * @param mixed $index
-     *
-     * @return DataSeries
      */
-    public function getPlotGroupByIndex($index)
+    public function getPlotGroupByIndex(int $index): DataSeries
     {
         return $this->plotSeries[$index];
     }
@@ -112,7 +100,7 @@ class PlotArea
      *
      * @return $this
      */
-    public function setPlotSeries(array $plotSeries)
+    public function setPlotSeries(array $plotSeries): static
     {
         $this->plotSeries = $plotSeries;
 
@@ -156,11 +144,64 @@ class PlotArea
 
     /**
      * Get gradientFillStops.
-     *
-     * @return array
      */
-    public function getGradientFillStops()
+    public function getGradientFillStops(): array
     {
         return $this->gradientFillStops;
     }
+
+    private ?int $gapWidth = null;
+
+    private bool $useUpBars = false;
+
+    private bool $useDownBars = false;
+
+    public function getGapWidth(): ?int
+    {
+        return $this->gapWidth;
+    }
+
+    public function setGapWidth(?int $gapWidth): self
+    {
+        $this->gapWidth = $gapWidth;
+
+        return $this;
+    }
+
+    public function getUseUpBars(): bool
+    {
+        return $this->useUpBars;
+    }
+
+    public function setUseUpBars(bool $useUpBars): self
+    {
+        $this->useUpBars = $useUpBars;
+
+        return $this;
+    }
+
+    public function getUseDownBars(): bool
+    {
+        return $this->useDownBars;
+    }
+
+    public function setUseDownBars(bool $useDownBars): self
+    {
+        $this->useDownBars = $useDownBars;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $this->layout = ($this->layout === null) ? null : clone $this->layout;
+        $plotSeries = $this->plotSeries;
+        $this->plotSeries = [];
+        foreach ($plotSeries as $series) {
+            $this->plotSeries[] = clone $series;
+        }
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
index edb7754..3e02a96 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
@@ -10,31 +10,20 @@ namespace PhpOffice\PhpSpreadsheet\Chart;
  */
 abstract class Properties
 {
-    /** @deprecated 1.24 use constant from ChartColor instead */
-    const EXCEL_COLOR_TYPE_STANDARD = ChartColor::EXCEL_COLOR_TYPE_STANDARD;
-    /** @deprecated 1.24 use constant from ChartColor instead */
-    const EXCEL_COLOR_TYPE_SCHEME = ChartColor::EXCEL_COLOR_TYPE_SCHEME;
-    /** @deprecated 1.24 use constant from ChartColor instead */
-    const EXCEL_COLOR_TYPE_ARGB = ChartColor::EXCEL_COLOR_TYPE_ARGB;
-
-    const
-        AXIS_LABELS_LOW = 'low';
+    const AXIS_LABELS_LOW = 'low';
     const AXIS_LABELS_HIGH = 'high';
     const AXIS_LABELS_NEXT_TO = 'nextTo';
     const AXIS_LABELS_NONE = 'none';
 
-    const
-        TICK_MARK_NONE = 'none';
+    const TICK_MARK_NONE = 'none';
     const TICK_MARK_INSIDE = 'in';
     const TICK_MARK_OUTSIDE = 'out';
     const TICK_MARK_CROSS = 'cross';
 
-    const
-        HORIZONTAL_CROSSES_AUTOZERO = 'autoZero';
+    const HORIZONTAL_CROSSES_AUTOZERO = 'autoZero';
     const HORIZONTAL_CROSSES_MAXIMUM = 'max';
 
-    const
-        FORMAT_CODE_GENERAL = 'General';
+    const FORMAT_CODE_GENERAL = 'General';
     const FORMAT_CODE_NUMBER = '#,##0.00';
     const FORMAT_CODE_CURRENCY = '$#,##0.00';
     const FORMAT_CODE_ACCOUNTING = '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)';
@@ -47,12 +36,10 @@ abstract class Properties
     const FORMAT_CODE_TEXT = '@';
     const FORMAT_CODE_SPECIAL = '00000';
 
-    const
-        ORIENTATION_NORMAL = 'minMax';
+    const ORIENTATION_NORMAL = 'minMax';
     const ORIENTATION_REVERSED = 'maxMin';
 
-    const
-        LINE_STYLE_COMPOUND_SIMPLE = 'sng';
+    const LINE_STYLE_COMPOUND_SIMPLE = 'sng';
     const LINE_STYLE_COMPOUND_DOUBLE = 'dbl';
     const LINE_STYLE_COMPOUND_THICKTHIN = 'thickThin';
     const LINE_STYLE_COMPOUND_THINTHICK = 'thinThick';
@@ -60,8 +47,6 @@ abstract class Properties
     const LINE_STYLE_DASH_SOLID = 'solid';
     const LINE_STYLE_DASH_ROUND_DOT = 'sysDot';
     const LINE_STYLE_DASH_SQUARE_DOT = 'sysDash';
-    /** @deprecated 1.24 use LINE_STYLE_DASH_SQUARE_DOT instead */
-    const LINE_STYLE_DASH_SQUERE_DOT = 'sysDash';
     const LINE_STYPE_DASH_DASH = 'dash';
     const LINE_STYLE_DASH_DASH_DOT = 'dashDot';
     const LINE_STYLE_DASH_LONG_DASH = 'lgDash';
@@ -89,8 +74,7 @@ abstract class Properties
     const LINE_STYLE_ARROW_SIZE_8 = 8;
     const LINE_STYLE_ARROW_SIZE_9 = 9;
 
-    const
-        SHADOW_PRESETS_NOSHADOW = null;
+    const SHADOW_PRESETS_NOSHADOW = null;
     const SHADOW_PRESETS_OUTER_BOTTTOM_RIGHT = 1;
     const SHADOW_PRESETS_OUTER_BOTTOM = 2;
     const SHADOW_PRESETS_OUTER_BOTTOM_LEFT = 3;
@@ -119,25 +103,19 @@ abstract class Properties
     const ANGLE_MULTIPLIER = 60000; // direction and size-kx size-ky
     const PERCENTAGE_MULTIPLIER = 100000; // size sx and sy
 
-    /** @var bool */
-    protected $objectState = false; // used only for minor gridlines
+    protected bool $objectState = false; // used only for minor gridlines
 
-    /** @var ?float */
-    protected $glowSize;
+    protected ?float $glowSize = null;
 
-    /** @var ChartColor */
-    protected $glowColor;
+    protected ChartColor $glowColor;
 
-    /** @var array */
-    protected $softEdges = [
+    protected array $softEdges = [
         'size' => null,
     ];
 
-    /** @var array */
-    protected $shadowProperties = self::PRESETS_OPTIONS[0];
+    protected array $shadowProperties = self::PRESETS_OPTIONS[0];
 
-    /** @var ChartColor */
-    protected $shadowColor;
+    protected ChartColor $shadowColor;
 
     public function __construct()
     {
@@ -151,10 +129,8 @@ abstract class Properties
 
     /**
      * Get Object State.
-     *
-     * @return bool
      */
-    public function getObjectState()
+    public function getObjectState(): bool
     {
         return $this->objectState;
     }
@@ -201,10 +177,7 @@ abstract class Properties
         return ((float) $value) / self::PERCENTAGE_MULTIPLIER;
     }
 
-    /**
-     * @param null|float|int|string $alpha
-     */
-    protected function setColorProperties(?string $color, $alpha, ?string $colorType): array
+    protected function setColorProperties(?string $color, null|float|int|string $alpha, ?string $colorType): array
     {
         return [
             'type' => $colorType,
@@ -428,13 +401,8 @@ abstract class Properties
 
     /**
      * Get value of array element.
-     *
-     * @param mixed $properties
-     * @param mixed $elements
-     *
-     * @return mixed
      */
-    protected function getArrayElementsValue($properties, $elements)
+    protected function getArrayElementsValue(array $properties, array|int|string $elements): mixed
     {
         $reference = &$properties;
         if (!is_array($elements)) {
@@ -450,13 +418,8 @@ abstract class Properties
 
     /**
      * Set Glow Properties.
-     *
-     * @param float $size
-     * @param ?string $colorValue
-     * @param ?int $colorAlpha
-     * @param ?string $colorType
      */
-    public function setGlowProperties($size, $colorValue = null, $colorAlpha = null, $colorType = null): void
+    public function setGlowProperties(float $size, ?string $colorValue = null, ?int $colorAlpha = null, ?string $colorType = null): void
     {
         $this
             ->activateObject()
@@ -472,12 +435,8 @@ abstract class Properties
 
     /**
      * Get Glow Property.
-     *
-     * @param array|string $property
-     *
-     * @return null|array|float|int|string
      */
-    public function getGlowProperty($property)
+    public function getGlowProperty(array|string $property): null|array|float|int|string
     {
         $retVal = null;
         if ($property === 'size') {
@@ -497,12 +456,8 @@ abstract class Properties
 
     /**
      * Get Glow Color Property.
-     *
-     * @param string $propertyName
-     *
-     * @return null|int|string
      */
-    public function getGlowColor($propertyName)
+    public function getGlowColor(string $propertyName): null|int|string
     {
         return $this->glowColor->getColorProperty($propertyName);
     }
@@ -514,10 +469,8 @@ abstract class Properties
 
     /**
      * Get Glow Size.
-     *
-     * @return ?float
      */
-    public function getGlowSize()
+    public function getGlowSize(): ?float
     {
         return $this->glowSize;
     }
@@ -525,11 +478,9 @@ abstract class Properties
     /**
      * Set Glow Size.
      *
-     * @param ?float $size
-     *
      * @return $this
      */
-    protected function setGlowSize($size)
+    protected function setGlowSize(?float $size)
     {
         $this->glowSize = $size;
 
@@ -538,10 +489,8 @@ abstract class Properties
 
     /**
      * Set Soft Edges Size.
-     *
-     * @param ?float $size
      */
-    public function setSoftEdges($size): void
+    public function setSoftEdges(?float $size): void
     {
         if ($size !== null) {
             $this->activateObject();
@@ -551,18 +500,13 @@ abstract class Properties
 
     /**
      * Get Soft Edges Size.
-     *
-     * @return string
      */
-    public function getSoftEdgesSize()
+    public function getSoftEdgesSize(): ?float
     {
         return $this->softEdges['size'];
     }
 
-    /**
-     * @param mixed $value
-     */
-    public function setShadowProperty(string $propertyName, $value): self
+    public function setShadowProperty(string $propertyName, mixed $value): self
     {
         $this->activateObject();
         if ($propertyName === 'color' && is_array($value)) {
@@ -576,16 +520,8 @@ abstract class Properties
 
     /**
      * Set Shadow Properties.
-     *
-     * @param int $presets
-     * @param string $colorValue
-     * @param string $colorType
-     * @param null|float|int|string $colorAlpha
-     * @param null|float $blur
-     * @param null|int $angle
-     * @param null|float $distance
      */
-    public function setShadowProperties($presets, $colorValue = null, $colorType = null, $colorAlpha = null, $blur = null, $angle = null, $distance = null): void
+    public function setShadowProperties(int $presets, ?string $colorValue = null, ?string $colorType = null, null|float|int|string $colorAlpha = null, ?float $blur = null, ?int $angle = null, ?float $distance = null): void
     {
         $this->activateObject()->setShadowPresetsProperties((int) $presets);
         if ($presets === 0) {
@@ -611,11 +547,9 @@ abstract class Properties
     /**
      * Set Shadow Presets Properties.
      *
-     * @param int $presets
-     *
      * @return $this
      */
-    protected function setShadowPresetsProperties($presets)
+    protected function setShadowPresetsProperties(int $presets)
     {
         $this->shadowProperties['presets'] = $presets;
         $this->setShadowPropertiesMapValues($this->getShadowPresetsMap($presets));
@@ -628,11 +562,9 @@ abstract class Properties
     /**
      * Set Shadow Properties Values.
      *
-     * @param mixed $reference
-     *
      * @return $this
      */
-    protected function setShadowPropertiesMapValues(array $propertiesMap, &$reference = null)
+    protected function setShadowPropertiesMapValues(array $propertiesMap, ?array &$reference = null)
     {
         $base_reference = $reference;
         foreach ($propertiesMap as $property_key => $property_val) {
@@ -656,11 +588,9 @@ abstract class Properties
     /**
      * Set Shadow Blur.
      *
-     * @param ?float $blur
-     *
      * @return $this
      */
-    protected function setShadowBlur($blur)
+    protected function setShadowBlur(?float $blur)
     {
         if ($blur !== null) {
             $this->shadowProperties['blur'] = $blur;
@@ -672,11 +602,9 @@ abstract class Properties
     /**
      * Set Shadow Angle.
      *
-     * @param null|float|int|string $angle
-     *
      * @return $this
      */
-    protected function setShadowAngle($angle)
+    protected function setShadowAngle(null|float|int|string $angle)
     {
         if (is_numeric($angle)) {
             $this->shadowProperties['direction'] = $angle;
@@ -688,11 +616,9 @@ abstract class Properties
     /**
      * Set Shadow Distance.
      *
-     * @param ?float $distance
-     *
      * @return $this
      */
-    protected function setShadowDistance($distance)
+    protected function setShadowDistance(?float $distance)
     {
         if ($distance !== null) {
             $this->shadowProperties['distance'] = $distance;
@@ -710,10 +636,8 @@ abstract class Properties
      * Get Shadow Property.
      *
      * @param string|string[] $elements
-     *
-     * @return array|string
      */
-    public function getShadowProperty($elements)
+    public function getShadowProperty($elements): array|string|null
     {
         if ($elements === 'color') {
             return [
@@ -722,8 +646,16 @@ abstract class Properties
                 'alpha' => $this->shadowColor->getAlpha(),
             ];
         }
+        $retVal = $this->getArrayElementsValue($this->shadowProperties, $elements);
+        if (is_scalar($retVal)) {
+            $retVal = (string) $retVal;
+        } elseif ($retVal !== null && !is_array($retVal)) {
+            // @codeCoverageIgnoreStart
+            throw new Exception('Unexpected value for shadowProperty');
+            // @codeCoverageIgnoreEnd
+        }
 
-        return $this->getArrayElementsValue($this->shadowProperties, $elements);
+        return $retVal;
     }
 
     public function getShadowArray(): array
@@ -736,11 +668,9 @@ abstract class Properties
         return $array;
     }
 
-    /** @var ChartColor */
-    protected $lineColor;
+    protected ChartColor $lineColor;
 
-    /** @var array */
-    protected $lineStyleProperties = [
+    protected array $lineStyleProperties = [
         'width' => null, //'9525',
         'compound' => '', //self::LINE_STYLE_COMPOUND_SIMPLE,
         'dash' => '', //self::LINE_STYLE_DASH_SOLID,
@@ -779,12 +709,8 @@ abstract class Properties
 
     /**
      * Set Line Color Properties.
-     *
-     * @param string $value
-     * @param ?int $alpha
-     * @param ?string $colorType
      */
-    public function setLineColorProperties($value, $alpha = null, $colorType = null): void
+    public function setLineColorProperties(?string $value, ?int $alpha = null, ?string $colorType = null): void
     {
         $this->activateObject();
         $this->lineColor->setColorPropertiesArray(
@@ -798,35 +724,30 @@ abstract class Properties
 
     /**
      * Get Line Color Property.
-     *
-     * @param string $propertyName
-     *
-     * @return null|int|string
      */
-    public function getLineColorProperty($propertyName)
+    public function getLineColorProperty(string $propertyName): null|int|string
     {
         return $this->lineColor->getColorProperty($propertyName);
     }
 
     /**
      * Set Line Style Properties.
-     *
-     * @param null|float|int|string $lineWidth
-     * @param string $compoundType
-     * @param string $dashType
-     * @param string $capType
-     * @param string $joinType
-     * @param string $headArrowType
-     * @param string $headArrowSize
-     * @param string $endArrowType
-     * @param string $endArrowSize
-     * @param string $headArrowWidth
-     * @param string $headArrowLength
-     * @param string $endArrowWidth
-     * @param string $endArrowLength
      */
-    public function setLineStyleProperties($lineWidth = null, $compoundType = '', $dashType = '', $capType = '', $joinType = '', $headArrowType = '', $headArrowSize = '', $endArrowType = '', $endArrowSize = '', $headArrowWidth = '', $headArrowLength = '', $endArrowWidth = '', $endArrowLength = ''): void
-    {
+    public function setLineStyleProperties(
+        null|float|int|string $lineWidth = null,
+        ?string $compoundType = '',
+        ?string $dashType = '',
+        ?string $capType = '',
+        ?string $joinType = '',
+        ?string $headArrowType = '',
+        int $headArrowSize = 0,
+        ?string $endArrowType = '',
+        int $endArrowSize = 0,
+        ?string $headArrowWidth = '',
+        ?string $headArrowLength = '',
+        ?string $endArrowWidth = '',
+        ?string $endArrowLength = ''
+    ): void {
         $this->activateObject();
         if (is_numeric($lineWidth)) {
             $this->lineStyleProperties['width'] = $lineWidth;
@@ -846,7 +767,7 @@ abstract class Properties
         if ($headArrowType !== '') {
             $this->lineStyleProperties['arrow']['head']['type'] = $headArrowType;
         }
-        if (array_key_exists($headArrowSize, self::ARROW_SIZES)) {
+        if (isset(self::ARROW_SIZES[$headArrowSize])) {
             $this->lineStyleProperties['arrow']['head']['size'] = $headArrowSize;
             $this->lineStyleProperties['arrow']['head']['w'] = self::ARROW_SIZES[$headArrowSize]['w'];
             $this->lineStyleProperties['arrow']['head']['len'] = self::ARROW_SIZES[$headArrowSize]['len'];
@@ -854,7 +775,7 @@ abstract class Properties
         if ($endArrowType !== '') {
             $this->lineStyleProperties['arrow']['end']['type'] = $endArrowType;
         }
-        if (array_key_exists($endArrowSize, self::ARROW_SIZES)) {
+        if (isset(self::ARROW_SIZES[$endArrowSize])) {
             $this->lineStyleProperties['arrow']['end']['size'] = $endArrowSize;
             $this->lineStyleProperties['arrow']['end']['w'] = self::ARROW_SIZES[$endArrowSize]['w'];
             $this->lineStyleProperties['arrow']['end']['len'] = self::ARROW_SIZES[$endArrowSize]['len'];
@@ -898,10 +819,7 @@ abstract class Properties
         return $this;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public function setLineStyleProperty(string $propertyName, $value): self
+    public function setLineStyleProperty(string $propertyName, mixed $value): self
     {
         $this->activateObject();
         $this->lineStyleProperties[$propertyName] = $value;
@@ -911,14 +829,19 @@ abstract class Properties
 
     /**
      * Get Line Style Property.
-     *
-     * @param array|string $elements
-     *
-     * @return string
      */
-    public function getLineStyleProperty($elements)
+    public function getLineStyleProperty(array|string $elements): ?string
     {
-        return $this->getArrayElementsValue($this->lineStyleProperties, $elements);
+        $retVal = $this->getArrayElementsValue($this->lineStyleProperties, $elements);
+        if (is_scalar($retVal)) {
+            $retVal = (string) $retVal;
+        } elseif ($retVal !== null) {
+            // @codeCoverageIgnoreStart
+            throw new Exception('Unexpected value for lineStyleProperty');
+            // @codeCoverageIgnoreEnd
+        }
+
+        return $retVal;
     }
 
     protected const ARROW_SIZES = [
@@ -935,51 +858,43 @@ abstract class Properties
 
     /**
      * Get Line Style Arrow Size.
-     *
-     * @param int $arraySelector
-     * @param string $arrayKaySelector
-     *
-     * @return string
      */
-    protected function getLineStyleArrowSize($arraySelector, $arrayKaySelector)
+    protected function getLineStyleArrowSize(int $arraySelector, string $arrayKaySelector): string
     {
         return self::ARROW_SIZES[$arraySelector][$arrayKaySelector] ?? '';
     }
 
     /**
      * Get Line Style Arrow Parameters.
-     *
-     * @param string $arrowSelector
-     * @param string $propertySelector
-     *
-     * @return string
      */
-    public function getLineStyleArrowParameters($arrowSelector, $propertySelector)
+    public function getLineStyleArrowParameters(string $arrowSelector, string $propertySelector): string
     {
         return $this->getLineStyleArrowSize($this->lineStyleProperties['arrow'][$arrowSelector]['size'], $propertySelector);
     }
 
     /**
      * Get Line Style Arrow Width.
-     *
-     * @param string $arrow
-     *
-     * @return string
      */
-    public function getLineStyleArrowWidth($arrow)
+    public function getLineStyleArrowWidth(string $arrow): ?string
     {
         return $this->getLineStyleProperty(['arrow', $arrow, 'w']);
     }
 
     /**
      * Get Line Style Arrow Excel Length.
-     *
-     * @param string $arrow
-     *
-     * @return string
      */
-    public function getLineStyleArrowLength($arrow)
+    public function getLineStyleArrowLength(string $arrow): ?string
     {
         return $this->getLineStyleProperty(['arrow', $arrow, 'len']);
     }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $this->lineColor = clone $this->lineColor;
+        $this->glowColor = clone $this->glowColor;
+        $this->shadowColor = clone $this->shadowColor;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php
index 3032f6b..ae28ff4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/IRenderer.php
@@ -14,9 +14,9 @@ interface IRenderer
     /**
      * Render the chart to given file (or stream).
      *
-     * @param string $filename Name of the file render to
+     * @param ?string $filename Name of the file render to
      *
      * @return bool true on success
      */
-    public function render($filename);
+    public function render(?string $filename): bool;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php
index cb9b544..151d5b5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php
@@ -26,9 +26,9 @@ use StockPlot;
  */
 abstract class JpGraphRendererBase implements IRenderer
 {
-    private static $width = 640;
+    private const DEFAULT_WIDTH = 640.0;
 
-    private static $height = 480;
+    private const DEFAULT_HEIGHT = 480.0;
 
     private static $colourSet = [
         'mediumpurple1', 'palegreen3', 'gold1', 'cadetblue1',
@@ -38,9 +38,9 @@ abstract class JpGraphRendererBase implements IRenderer
         'goldenrod2',
     ];
 
-    private static $markSet;
+    private static array $markSet;
 
-    private $chart;
+    private Chart $chart;
 
     private $graph;
 
@@ -70,6 +70,16 @@ abstract class JpGraphRendererBase implements IRenderer
         ];
     }
 
+    private function getGraphWidth(): float
+    {
+        return $this->chart->getRenderedWidth() ?? self::DEFAULT_WIDTH;
+    }
+
+    private function getGraphHeight(): float
+    {
+        return $this->chart->getRenderedHeight() ?? self::DEFAULT_HEIGHT;
+    }
+
     /**
      * This method should be overriden in descendants to do real JpGraph library initialization.
      */
@@ -102,7 +112,7 @@ abstract class JpGraphRendererBase implements IRenderer
         return $seriesPlot;
     }
 
-    private function formatDataSetLabels($groupID, $datasetLabels, $rotation = '')
+    private function formatDataSetLabels(int $groupID, array $datasetLabels, $rotation = '')
     {
         $datasetLabelFormatCode = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0)->getFormatCode() ?? '';
         //    Retrieve any label formatting code
@@ -129,7 +139,7 @@ abstract class JpGraphRendererBase implements IRenderer
         return $datasetLabels;
     }
 
-    private function percentageSumCalculation($groupID, $seriesCount)
+    private function percentageSumCalculation(int $groupID, $seriesCount)
     {
         $sumValues = [];
         //    Adjust our values to a percentage value across all series in the group
@@ -151,7 +161,7 @@ abstract class JpGraphRendererBase implements IRenderer
         return $sumValues;
     }
 
-    private function percentageAdjustValues($dataValues, $sumValues)
+    private function percentageAdjustValues(array $dataValues, array $sumValues)
     {
         foreach ($dataValues as $k => $dataValue) {
             $dataValues[$k] = $dataValue / $sumValues[$k] * 100;
@@ -219,9 +229,9 @@ abstract class JpGraphRendererBase implements IRenderer
         }
     }
 
-    private function renderCartesianPlotArea($type = 'textlin'): void
+    private function renderCartesianPlotArea(string $type = 'textlin'): void
     {
-        $this->graph = new Graph(self::$width, self::$height);
+        $this->graph = new Graph($this->getGraphWidth(), $this->getGraphHeight());
         $this->graph->SetScale($type);
 
         $this->renderTitle();
@@ -258,20 +268,30 @@ abstract class JpGraphRendererBase implements IRenderer
 
     private function renderPiePlotArea(): void
     {
-        $this->graph = new PieGraph(self::$width, self::$height);
+        $this->graph = new PieGraph($this->getGraphWidth(), $this->getGraphHeight());
 
         $this->renderTitle();
     }
 
     private function renderRadarPlotArea(): void
     {
-        $this->graph = new RadarGraph(self::$width, self::$height);
+        $this->graph = new RadarGraph($this->getGraphWidth(), $this->getGraphHeight());
         $this->graph->SetScale('lin');
 
         $this->renderTitle();
     }
 
-    private function renderPlotLine($groupID, $filled = false, $combination = false): void
+    private function getDataLabel(int $groupId, int $index): mixed
+    {
+        $plotLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupId)->getPlotLabelByIndex($index);
+        if (!$plotLabel) {
+            return '';
+        }
+
+        return $plotLabel->getDataValue();
+    }
+
+    private function renderPlotLine(int $groupID, bool $filled = false, bool $combination = false): void
     {
         $grouping = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotGrouping();
 
@@ -324,8 +344,8 @@ abstract class JpGraphRendererBase implements IRenderer
                 //    Set the appropriate plot marker
                 $this->formatPointMarker($seriesPlot, $marker);
             }
-            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($index)->getDataValue();
-            $seriesPlot->SetLegend($dataLabel);
+
+            $seriesPlot->SetLegend($this->getDataLabel($groupID, $index));
 
             $seriesPlots[] = $seriesPlot;
         }
@@ -338,7 +358,7 @@ abstract class JpGraphRendererBase implements IRenderer
         $this->graph->Add($groupPlot);
     }
 
-    private function renderPlotBar($groupID, $dimensions = '2d'): void
+    private function renderPlotBar(int $groupID, ?string $dimensions = '2d'): void
     {
         $rotation = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotDirection();
         //    Rotate for bar rather than column chart
@@ -398,12 +418,8 @@ abstract class JpGraphRendererBase implements IRenderer
             if ($dimensions == '3d') {
                 $seriesPlot->SetShadow();
             }
-            if (!$this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)) {
-                $dataLabel = '';
-            } else {
-                $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($j)->getDataValue();
-            }
-            $seriesPlot->SetLegend($dataLabel);
+
+            $seriesPlot->SetLegend($this->getDataLabel($groupID, $j));
 
             $seriesPlots[] = $seriesPlot;
         }
@@ -426,7 +442,7 @@ abstract class JpGraphRendererBase implements IRenderer
         $this->graph->Add($groupPlot);
     }
 
-    private function renderPlotScatter($groupID, $bubble): void
+    private function renderPlotScatter(int $groupID, bool $bubble): void
     {
         $scatterStyle = $bubbleSize = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
 
@@ -434,11 +450,31 @@ abstract class JpGraphRendererBase implements IRenderer
 
         //    Loop through each data series in turn
         for ($i = 0; $i < $seriesCount; ++$i) {
-            $dataValuesY = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i)->getDataValues();
+            $plotCategoryByIndex = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex($i);
+            if ($plotCategoryByIndex === false) {
+                $plotCategoryByIndex = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotCategoryByIndex(0);
+            }
+            $dataValuesY = $plotCategoryByIndex->getDataValues();
             $dataValuesX = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getDataValues();
 
-            foreach ($dataValuesY as $k => $dataValueY) {
-                $dataValuesY[$k] = $k;
+            $redoDataValuesY = true;
+            if ($bubble) {
+                if (!$bubbleSize) {
+                    $bubbleSize = '10';
+                }
+                $redoDataValuesY = false;
+                foreach ($dataValuesY as $dataValueY) {
+                    if (!is_int($dataValueY) && !is_float($dataValueY)) {
+                        $redoDataValuesY = true;
+
+                        break;
+                    }
+                }
+            }
+            if ($redoDataValuesY) {
+                foreach ($dataValuesY as $k => $dataValueY) {
+                    $dataValuesY[$k] = $k;
+                }
             }
 
             $seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY);
@@ -447,7 +483,7 @@ abstract class JpGraphRendererBase implements IRenderer
                 $seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
             } elseif ($scatterStyle == 'smoothMarker') {
                 $spline = new Spline($dataValuesY, $dataValuesX);
-                [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20);
+                [$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * $this->getGraphWidth() / 20);
                 $lplot = new LinePlot($splineDataX, $splineDataY);
                 $lplot->SetColor(self::$colourSet[self::$plotColour]);
 
@@ -462,14 +498,13 @@ abstract class JpGraphRendererBase implements IRenderer
                 $marker = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotValuesByIndex($i)->getPointMarker();
                 $this->formatPointMarker($seriesPlot, $marker);
             }
-            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
-            $seriesPlot->SetLegend($dataLabel);
+            $seriesPlot->SetLegend($this->getDataLabel($groupID, $i));
 
             $this->graph->Add($seriesPlot);
         }
     }
 
-    private function renderPlotRadar($groupID): void
+    private function renderPlotRadar(int $groupID): void
     {
         $radarStyle = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotStyle();
 
@@ -483,7 +518,7 @@ abstract class JpGraphRendererBase implements IRenderer
 
             $dataValues = [];
             foreach ($dataValuesY as $k => $dataValueY) {
-                $dataValues[$k] = implode(' ', array_reverse($dataValueY));
+                $dataValues[$k] = is_array($dataValueY) ? implode(' ', array_reverse($dataValueY)) : $dataValueY;
             }
             $tmp = array_shift($dataValues);
             $dataValues[] = $tmp;
@@ -494,19 +529,18 @@ abstract class JpGraphRendererBase implements IRenderer
 
             $seriesPlot = new RadarPlot(array_reverse($dataValuesX));
 
-            $dataLabel = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotLabelByIndex($i)->getDataValue();
             $seriesPlot->SetColor(self::$colourSet[self::$plotColour++]);
             if ($radarStyle == 'filled') {
                 $seriesPlot->SetFillColor(self::$colourSet[self::$plotColour]);
             }
             $this->formatPointMarker($seriesPlot, $marker);
-            $seriesPlot->SetLegend($dataLabel);
+            $seriesPlot->SetLegend($this->getDataLabel($groupID, $i));
 
             $this->graph->Add($seriesPlot);
         }
     }
 
-    private function renderPlotContour($groupID): void
+    private function renderPlotContour(int $groupID): void
     {
         $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
 
@@ -522,7 +556,7 @@ abstract class JpGraphRendererBase implements IRenderer
         $this->graph->Add($seriesPlot);
     }
 
-    private function renderPlotStock($groupID): void
+    private function renderPlotStock(int $groupID): void
     {
         $seriesCount = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotSeriesCount();
         $plotOrder = $this->chart->getPlotArea()->getPlotGroupByIndex($groupID)->getPlotOrder();
@@ -584,7 +618,7 @@ abstract class JpGraphRendererBase implements IRenderer
         }
     }
 
-    private function renderBarChart($groupCount, $dimensions = '2d'): void
+    private function renderBarChart($groupCount, ?string $dimensions = '2d'): void
     {
         $this->renderCartesianPlotArea();
 
@@ -611,7 +645,7 @@ abstract class JpGraphRendererBase implements IRenderer
         }
     }
 
-    private function renderPieChart($groupCount, $dimensions = '2d', $doughnut = false, $multiplePlots = false): void
+    private function renderPieChart($groupCount, ?string $dimensions = '2d', bool $doughnut = false, bool $multiplePlots = false): void
     {
         $this->renderPiePlotArea();
 
@@ -708,7 +742,7 @@ abstract class JpGraphRendererBase implements IRenderer
         }
     }
 
-    private function renderCombinationChart($groupCount, $outputDestination)
+    private function renderCombinationChart($groupCount, $outputDestination): bool
     {
         $this->renderCartesianPlotArea();
 
@@ -755,7 +789,7 @@ abstract class JpGraphRendererBase implements IRenderer
         return true;
     }
 
-    public function render($outputDestination)
+    public function render(?string $outputDestination): bool
     {
         self::$plotColour = 0;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php
index e1f0f90..9697920 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/MtJpGraphRenderer.php
@@ -2,13 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Chart\Renderer;
 
+use mitoteam\jpgraph\MtJpGraph;
+
 /**
- * Jpgraph is not oficially maintained in Composer.
+ * Jpgraph is not officially maintained by Composer at packagist.org.
  *
  * This renderer implementation uses package
  * https://packagist.org/packages/mitoteam/jpgraph
  *
- * This package is up to date for August 2022 and has PHP 8.1 support.
+ * This package is up to date for June 2023 and has PHP 8.2 support.
  */
 class MtJpGraphRenderer extends JpGraphRendererBase
 {
@@ -19,7 +21,7 @@ class MtJpGraphRenderer extends JpGraphRendererBase
             return;
         }
 
-        \mitoteam\jpgraph\MtJpGraph::load([
+        MtJpGraph::load([
             'bar',
             'contour',
             'line',
@@ -29,7 +31,7 @@ class MtJpGraphRenderer extends JpGraphRendererBase
             'regstat',
             'scatter',
             'stock',
-        ]);
+        ], true); // enable Extended mode
 
         $loaded = true;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
index 9b0540d..29ba17b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
@@ -3,46 +3,63 @@
 namespace PhpOffice\PhpSpreadsheet\Chart;
 
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Font;
 
 class Title
 {
+    public const TITLE_CELL_REFERENCE
+        = '/^(.*)!' // beginning of string, everything up to ! is match[1]
+        . '[$]([A-Z]{1,3})' // absolute column string match[2]
+        . '[$](\d{1,7})$/i'; // absolute row string match[3]
+
     /**
      * Title Caption.
      *
-     * @var array|RichText|string
+     * @var array<RichText|string>|RichText|string
      */
-    private $caption = '';
+    private array|RichText|string $caption;
+
+    /**
+     * Allow overlay of other elements?
+     */
+    private bool $overlay = true;
 
     /**
      * Title Layout.
-     *
-     * @var ?Layout
      */
-    private $layout;
+    private ?Layout $layout;
+
+    private string $cellReference = '';
+
+    private ?Font $font = null;
 
     /**
      * Create a new Title.
-     *
-     * @param array|RichText|string $caption
      */
-    public function __construct($caption = '', ?Layout $layout = null)
+    public function __construct(array|RichText|string $caption = '', ?Layout $layout = null, bool $overlay = false)
     {
         $this->caption = $caption;
         $this->layout = $layout;
+        $this->setOverlay($overlay);
     }
 
     /**
      * Get caption.
-     *
-     * @return array|RichText|string
      */
-    public function getCaption()
+    public function getCaption(): array|RichText|string
     {
         return $this->caption;
     }
 
-    public function getCaptionText(): string
+    public function getCaptionText(?Spreadsheet $spreadsheet = null): string
     {
+        if ($spreadsheet !== null) {
+            $caption = $this->getCalculatedTitle($spreadsheet);
+            if ($caption !== null) {
+                return $caption;
+            }
+        }
         $caption = $this->caption;
         if (is_string($caption)) {
             return $caption;
@@ -52,7 +69,7 @@ class Title
         }
         $retVal = '';
         foreach ($caption as $textx) {
-            /** @var RichText|string */
+            /** @var RichText|string $text */
             $text = $textx;
             if ($text instanceof RichText) {
                 $retVal .= $text->getPlainText();
@@ -67,19 +84,88 @@ class Title
     /**
      * Set caption.
      *
-     * @param array|RichText|string $caption
-     *
      * @return $this
      */
-    public function setCaption($caption)
+    public function setCaption(array|RichText|string $caption): static
     {
         $this->caption = $caption;
 
         return $this;
     }
 
+    /**
+     * Get allow overlay of other elements?
+     */
+    public function getOverlay(): bool
+    {
+        return $this->overlay;
+    }
+
+    /**
+     * Set allow overlay of other elements?
+     */
+    public function setOverlay(bool $overlay): self
+    {
+        $this->overlay = $overlay;
+
+        return $this;
+    }
+
     public function getLayout(): ?Layout
     {
         return $this->layout;
     }
+
+    public function setCellReference(string $cellReference): self
+    {
+        $this->cellReference = $cellReference;
+
+        return $this;
+    }
+
+    public function getCellReference(): string
+    {
+        return $this->cellReference;
+    }
+
+    public function getCalculatedTitle(?Spreadsheet $spreadsheet): ?string
+    {
+        preg_match(self::TITLE_CELL_REFERENCE, $this->cellReference, $matches);
+        if (count($matches) === 0 || $spreadsheet === null) {
+            return null;
+        }
+        $sheetName = preg_replace("/^'(.*)'$/", '$1', $matches[1]) ?? '';
+
+        return $spreadsheet->getSheetByName($sheetName)?->getCell($matches[2] . $matches[3])?->getFormattedValue();
+    }
+
+    public function getFont(): ?Font
+    {
+        return $this->font;
+    }
+
+    public function setFont(?Font $font): self
+    {
+        $this->font = $font;
+
+        return $this;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $this->layout = ($this->layout === null) ? null : clone $this->layout;
+        $this->font = ($this->font === null) ? null : clone $this->font;
+        if (is_array($this->caption)) {
+            $captions = $this->caption;
+            $this->caption = [];
+            foreach ($captions as $caption) {
+                $this->caption[] = is_object($caption) ? (clone $caption) : $caption;
+            }
+        } else {
+            $this->caption = is_object($this->caption) ? (clone $this->caption) : $this->caption;
+        }
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php
index 75a5896..5814fea 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/TrendLine.php
@@ -19,32 +19,23 @@ class TrendLine extends Properties
         self::TRENDLINE_MOVING_AVG,
     ];
 
-    /** @var string */
-    private $trendLineType = 'linear'; // TRENDLINE_LINEAR
+    private string $trendLineType = 'linear'; // TRENDLINE_LINEAR
 
-    /** @var int */
-    private $order = 2;
+    private int $order = 2;
 
-    /** @var int */
-    private $period = 3;
+    private int $period = 3;
 
-    /** @var bool */
-    private $dispRSqr = false;
+    private bool $dispRSqr = false;
 
-    /** @var bool */
-    private $dispEq = false;
+    private bool $dispEq = false;
 
-    /** @var string */
-    private $name = '';
+    private string $name = '';
 
-    /** @var float */
-    private $backward = 0.0;
+    private float $backward = 0.0;
 
-    /** @var float */
-    private $forward = 0.0;
+    private float $forward = 0.0;
 
-    /** @var float */
-    private $intercept = 0.0;
+    private float $intercept = 0.0;
 
     /**
      * Create a new TrendLine object.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
index 9a9df22..c117453 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
@@ -2,7 +2,6 @@
 
 namespace PhpOffice\PhpSpreadsheet\Collection;
 
-use Generator;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
@@ -14,38 +13,27 @@ class Cells
 {
     protected const MAX_COLUMN_ID = 16384;
 
-    /**
-     * @var CacheInterface
-     */
-    private $cache;
+    private CacheInterface $cache;
 
     /**
      * Parent worksheet.
-     *
-     * @var null|Worksheet
      */
-    private $parent;
+    private ?Worksheet $parent;
 
     /**
      * The currently active Cell.
-     *
-     * @var null|Cell
      */
-    private $currentCell;
+    private ?Cell $currentCell = null;
 
     /**
      * Coordinate of the currently active Cell.
-     *
-     * @var null|string
      */
-    private $currentCoordinate;
+    private ?string $currentCoordinate = null;
 
     /**
      * Flag indicating whether the currently active Cell requires saving.
-     *
-     * @var bool
      */
-    private $currentCellIsDirty = false;
+    private bool $currentCellIsDirty = false;
 
     /**
      * An index of existing cells. int pointer to the coordinate (0-base-indexed row * 16,384 + 1-base indexed column)
@@ -53,14 +41,12 @@ class Cells
      *
      * @var int[]
      */
-    private $index = [];
+    private array $index = [];
 
     /**
      * Prefix used to uniquely identify cache data for this worksheet.
-     *
-     * @var string
      */
-    private $cachePrefix;
+    private string $cachePrefix;
 
     /**
      * Initialise this new cell collection.
@@ -79,10 +65,8 @@ class Cells
 
     /**
      * Return the parent worksheet for this cell collection.
-     *
-     * @return null|Worksheet
      */
-    public function getParent()
+    public function getParent(): ?Worksheet
     {
         return $this->parent;
     }
@@ -92,7 +76,7 @@ class Cells
      *
      * @param string $cellCoordinate Coordinate of the cell to check
      */
-    public function has($cellCoordinate): bool
+    public function has(string $cellCoordinate): bool
     {
         return ($cellCoordinate === $this->currentCoordinate) || isset($this->index[$cellCoordinate]);
     }
@@ -112,7 +96,7 @@ class Cells
      *
      * @param string $cellCoordinate Coordinate of the cell to delete
      */
-    public function delete($cellCoordinate): void
+    public function delete(string $cellCoordinate): void
     {
         if ($cellCoordinate === $this->currentCoordinate && $this->currentCell !== null) {
             $this->currentCell->detach();
@@ -132,7 +116,7 @@ class Cells
      *
      * @return string[]
      */
-    public function getCoordinates()
+    public function getCoordinates(): array
     {
         return array_keys($this->index);
     }
@@ -142,7 +126,7 @@ class Cells
      *
      * @return string[]
      */
-    public function getSortedCoordinates()
+    public function getSortedCoordinates(): array
     {
         asort($this->index);
 
@@ -150,11 +134,21 @@ class Cells
     }
 
     /**
-     * Return the cell coordinate of the currently active cell object.
+     * Get a sorted list of all cell coordinates currently held in the collection by index (16384*row+column).
      *
-     * @return null|string
+     * @return int[]
      */
-    public function getCurrentCoordinate()
+    public function getSortedCoordinatesInt(): array
+    {
+        asort($this->index);
+
+        return array_values($this->index);
+    }
+
+    /**
+     * Return the cell coordinate of the currently active cell object.
+     */
+    public function getCurrentCoordinate(): ?string
     {
         return $this->currentCoordinate;
     }
@@ -188,14 +182,14 @@ class Cells
      *
      * @return array Highest column name and highest row number
      */
-    public function getHighestRowAndColumn()
+    public function getHighestRowAndColumn(): array
     {
         // Lookup highest column and highest row
         $maxRow = $maxColumn = 1;
         foreach ($this->index as $coordinate) {
-            $row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
+            $row = (int) floor(($coordinate - 1) / self::MAX_COLUMN_ID) + 1;
             $maxRow = ($maxRow > $row) ? $maxRow : $row;
-            $column = $coordinate % self::MAX_COLUMN_ID;
+            $column = ($coordinate % self::MAX_COLUMN_ID) ?: self::MAX_COLUMN_ID;
             $maxColumn = ($maxColumn > $column) ? $maxColumn : $column;
         }
 
@@ -213,7 +207,7 @@ class Cells
      *
      * @return string Highest column name
      */
-    public function getHighestColumn($row = null)
+    public function getHighestColumn($row = null): string
     {
         if ($row === null) {
             return $this->getHighestRowAndColumn()['column'];
@@ -231,7 +225,7 @@ class Cells
             if ($coordinate < $fromRow || $coordinate >= $toRow) {
                 continue;
             }
-            $column = $coordinate % self::MAX_COLUMN_ID;
+            $column = ($coordinate % self::MAX_COLUMN_ID) ?: self::MAX_COLUMN_ID;
             $maxColumn = $maxColumn > $column ? $maxColumn : $column;
         }
 
@@ -246,7 +240,7 @@ class Cells
      *
      * @return int Highest row number
      */
-    public function getHighestRow($column = null)
+    public function getHighestRow(?string $column = null): int
     {
         if ($column === null) {
             return $this->getHighestRowAndColumn()['row'];
@@ -270,7 +264,7 @@ class Cells
      *
      * @return string Unique Reference
      */
-    private function getUniqueID()
+    private function getUniqueID(): string
     {
         $cacheType = Settings::getCache();
 
@@ -281,10 +275,8 @@ class Cells
 
     /**
      * Clone the cell collection.
-     *
-     * @return self
      */
-    public function cloneCellCollection(Worksheet $worksheet)
+    public function cloneCellCollection(Worksheet $worksheet): static
     {
         $this->storeCurrentCell();
         $newCollection = clone $this;
@@ -296,7 +288,7 @@ class Cells
             $newCollection->index[$key] = $value;
             $stored = $newCollection->cache->set(
                 $newCollection->cachePrefix . $key,
-                clone $this->cache->get($this->cachePrefix . $key)
+                clone $this->getCache($key)
             );
             if ($stored === false) {
                 $this->destructIfNeeded($newCollection, 'Failed to copy cells in cache');
@@ -335,7 +327,7 @@ class Cells
      *
      * @param string $column Column ID to remove
      */
-    public function removeColumn($column): void
+    public function removeColumn(string $column): void
     {
         $this->storeCurrentCell();
 
@@ -356,7 +348,7 @@ class Cells
     private function storeCurrentCell(): void
     {
         if ($this->currentCellIsDirty && isset($this->currentCoordinate, $this->currentCell)) {
-            $this->currentCell->/** @scrutinizer ignore-call */ detach();
+            $this->currentCell->detach();
 
             $stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell);
             if ($stored === false) {
@@ -381,10 +373,8 @@ class Cells
      *
      * @param string $cellCoordinate Coordinate of the cell to update
      * @param Cell $cell Cell to update
-     *
-     * @return Cell
      */
-    public function add($cellCoordinate, Cell $cell)
+    public function add(string $cellCoordinate, Cell $cell): Cell
     {
         if ($cellCoordinate !== $this->currentCoordinate) {
             $this->storeCurrentCell();
@@ -408,7 +398,7 @@ class Cells
      *
      * @return null|Cell Cell that was found, or null if not found
      */
-    public function get($cellCoordinate)
+    public function get(string $cellCoordinate): ?Cell
     {
         if ($cellCoordinate === $this->currentCoordinate) {
             return $this->currentCell;
@@ -420,11 +410,7 @@ class Cells
             return null;
         }
 
-        // Check if the entry that has been requested actually exists in the cache
-        $cell = $this->cache->get($this->cachePrefix . $cellCoordinate);
-        if ($cell === null) {
-            throw new PhpSpreadsheetException("Cell entry {$cellCoordinate} no longer exists in cache. This probably means that the cache was cleared by someone else.");
-        }
+        $cell = $this->getcache($cellCoordinate);
 
         // Set current entry to the requested entry
         $this->currentCoordinate = $cellCoordinate;
@@ -462,17 +448,28 @@ class Cells
     public function __destruct()
     {
         $this->cache->deleteMultiple($this->getAllCacheKeys());
+        $this->parent = null;
     }
 
     /**
      * Returns all known cache keys.
      *
-     * @return Generator|string[]
+     * @return iterable<string>
      */
-    private function getAllCacheKeys()
+    private function getAllCacheKeys(): iterable
     {
         foreach ($this->index as $coordinate => $value) {
             yield $this->cachePrefix . $coordinate;
         }
     }
+
+    private function getCache(string $cellCoordinate): Cell
+    {
+        $cell = $this->cache->get($this->cachePrefix . $cellCoordinate);
+        if (!($cell instanceof Cell)) {
+            throw new PhpSpreadsheetException("Cell entry {$cellCoordinate} no longer exists in cache. This probably means that the cache was cleared by someone else.");
+        }
+
+        return $cell;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php
index a0eb6ec..b8918c9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php
@@ -2,13 +2,12 @@
 
 namespace PhpOffice\PhpSpreadsheet\Collection\Memory;
 
-use DateInterval;
 use Psr\SimpleCache\CacheInterface;
 
 /**
  * This is the default implementation for in-memory cell collection.
  *
- * Alternatives implementation should leverage off-memory, non-volatile storage
+ * Alternative implementation should leverage off-memory, non-volatile storage
  * to reduce overall memory usage.
  */
 class SimpleCache1 implements CacheInterface
@@ -16,36 +15,23 @@ class SimpleCache1 implements CacheInterface
     /**
      * @var array Cell Cache
      */
-    private $cache = [];
+    private array $cache = [];
 
-    /**
-     * @return bool
-     */
-    public function clear()
+    public function clear(): bool
     {
         $this->cache = [];
 
         return true;
     }
 
-    /**
-     * @param string $key
-     *
-     * @return bool
-     */
-    public function delete($key)
+    public function delete($key): bool
     {
         unset($this->cache[$key]);
 
         return true;
     }
 
-    /**
-     * @param iterable $keys
-     *
-     * @return bool
-     */
-    public function deleteMultiple($keys)
+    public function deleteMultiple($keys): bool
     {
         foreach ($keys as $key) {
             $this->delete($key);
@@ -54,13 +40,7 @@ class SimpleCache1 implements CacheInterface
         return true;
     }
 
-    /**
-     * @param string $key
-     * @param mixed  $default
-     *
-     * @return mixed
-     */
-    public function get($key, $default = null)
+    public function get($key, $default = null): mixed
     {
         if ($this->has($key)) {
             return $this->cache[$key];
@@ -69,13 +49,7 @@ class SimpleCache1 implements CacheInterface
         return $default;
     }
 
-    /**
-     * @param iterable $keys
-     * @param mixed    $default
-     *
-     * @return iterable
-     */
-    public function getMultiple($keys, $default = null)
+    public function getMultiple($keys, $default = null): iterable
     {
         $results = [];
         foreach ($keys as $key) {
@@ -85,37 +59,19 @@ class SimpleCache1 implements CacheInterface
         return $results;
     }
 
-    /**
-     * @param string $key
-     *
-     * @return bool
-     */
-    public function has($key)
+    public function has($key): bool
     {
         return array_key_exists($key, $this->cache);
     }
 
-    /**
-     * @param string                 $key
-     * @param mixed                  $value
-     * @param null|DateInterval|int $ttl
-     *
-     * @return bool
-     */
-    public function set($key, $value, $ttl = null)
+    public function set($key, $value, $ttl = null): bool
     {
         $this->cache[$key] = $value;
 
         return true;
     }
 
-    /**
-     * @param iterable               $values
-     * @param null|DateInterval|int $ttl
-     *
-     * @return bool
-     */
-    public function setMultiple($values, $ttl = null)
+    public function setMultiple($values, $ttl = null): bool
     {
         foreach ($values as $key => $value) {
             $this->set($key, $value);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
index 34f6447..7b6c460 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php
@@ -8,15 +8,12 @@ use Psr\SimpleCache\CacheInterface;
 /**
  * This is the default implementation for in-memory cell collection.
  *
- * Alternatives implementation should leverage off-memory, non-volatile storage
+ * Alternative implementation should leverage off-memory, non-volatile storage
  * to reduce overall memory usage.
  */
 class SimpleCache3 implements CacheInterface
 {
-    /**
-     * @var array Cell Cache
-     */
-    private $cache = [];
+    private array $cache = [];
 
     public function clear(): bool
     {
@@ -25,20 +22,14 @@ class SimpleCache3 implements CacheInterface
         return true;
     }
 
-    /**
-     * @param string $key
-     */
-    public function delete($key): bool
+    public function delete(string $key): bool
     {
         unset($this->cache[$key]);
 
         return true;
     }
 
-    /**
-     * @param iterable $keys
-     */
-    public function deleteMultiple($keys): bool
+    public function deleteMultiple(iterable $keys): bool
     {
         foreach ($keys as $key) {
             $this->delete($key);
@@ -47,11 +38,7 @@ class SimpleCache3 implements CacheInterface
         return true;
     }
 
-    /**
-     * @param string $key
-     * @param mixed  $default
-     */
-    public function get($key, $default = null): mixed
+    public function get(string $key, mixed $default = null): mixed
     {
         if ($this->has($key)) {
             return $this->cache[$key];
@@ -60,11 +47,7 @@ class SimpleCache3 implements CacheInterface
         return $default;
     }
 
-    /**
-     * @param iterable $keys
-     * @param mixed    $default
-     */
-    public function getMultiple($keys, $default = null): iterable
+    public function getMultiple(iterable $keys, mixed $default = null): iterable
     {
         $results = [];
         foreach ($keys as $key) {
@@ -74,31 +57,19 @@ class SimpleCache3 implements CacheInterface
         return $results;
     }
 
-    /**
-     * @param string $key
-     */
-    public function has($key): bool
+    public function has(string $key): bool
     {
         return array_key_exists($key, $this->cache);
     }
 
-    /**
-     * @param string                 $key
-     * @param mixed                  $value
-     * @param null|DateInterval|int $ttl
-     */
-    public function set($key, $value, $ttl = null): bool
+    public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool
     {
         $this->cache[$key] = $value;
 
         return true;
     }
 
-    /**
-     * @param iterable               $values
-     * @param null|DateInterval|int $ttl
-     */
-    public function setMultiple($values, $ttl = null): bool
+    public function setMultiple(iterable $values, null|int|DateInterval $ttl = null): bool
     {
         foreach ($values as $key => $value) {
             $this->set($key, $value);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
index abadc7d..2e18270 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Comment.php
@@ -9,78 +9,67 @@ use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
 use PhpOffice\PhpSpreadsheet\Style\Alignment;
 use PhpOffice\PhpSpreadsheet\Style\Color;
 use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
+use Stringable;
 
-class Comment implements IComparable
+class Comment implements IComparable, Stringable
 {
     /**
      * Author.
-     *
-     * @var string
      */
-    private $author;
+    private string $author;
 
     /**
      * Rich text comment.
-     *
-     * @var RichText
      */
-    private $text;
+    private RichText $text;
 
     /**
      * Comment width (CSS style, i.e. XXpx or YYpt).
-     *
-     * @var string
      */
-    private $width = '96pt';
+    private string $width = '96pt';
 
     /**
      * Left margin (CSS style, i.e. XXpx or YYpt).
-     *
-     * @var string
      */
-    private $marginLeft = '59.25pt';
+    private string $marginLeft = '59.25pt';
 
     /**
      * Top margin (CSS style, i.e. XXpx or YYpt).
-     *
-     * @var string
      */
-    private $marginTop = '1.5pt';
+    private string $marginTop = '1.5pt';
 
     /**
      * Visible.
-     *
-     * @var bool
      */
-    private $visible = false;
+    private bool $visible = false;
 
     /**
      * Comment height (CSS style, i.e. XXpx or YYpt).
-     *
-     * @var string
      */
-    private $height = '55.5pt';
+    private string $height = '55.5pt';
 
     /**
      * Comment fill color.
-     *
-     * @var Color
      */
-    private $fillColor;
+    private Color $fillColor;
 
     /**
      * Alignment.
-     *
-     * @var string
      */
-    private $alignment;
+    private string $alignment;
 
     /**
      * Background image in comment.
-     *
-     * @var Drawing
      */
-    private $backgroundImage;
+    private Drawing $backgroundImage;
+
+    public const TEXTBOX_DIRECTION_RTL = 'rtl';
+    public const TEXTBOX_DIRECTION_LTR = 'ltr';
+    // MS uses 'auto' in xml but 'context' in UI
+    public const TEXTBOX_DIRECTION_AUTO = 'auto';
+    public const TEXTBOX_DIRECTION_CONTEXT = 'auto';
+
+    private string $textboxDirection = '';
 
     /**
      * Create a new Comment.
@@ -251,9 +240,6 @@ class Comment implements IComparable
         return $this->fillColor;
     }
 
-    /**
-     * Set Alignment.
-     */
     public function setAlignment(string $alignment): self
     {
         $this->alignment = $alignment;
@@ -261,31 +247,41 @@ class Comment implements IComparable
         return $this;
     }
 
-    /**
-     * Get Alignment.
-     */
     public function getAlignment(): string
     {
         return $this->alignment;
     }
 
+    public function setTextboxDirection(string $textboxDirection): self
+    {
+        $this->textboxDirection = $textboxDirection;
+
+        return $this;
+    }
+
+    public function getTextboxDirection(): string
+    {
+        return $this->textboxDirection;
+    }
+
     /**
      * Get hash code.
      */
     public function getHashCode(): string
     {
         return md5(
-            $this->author .
-            $this->text->getHashCode() .
-            $this->width .
-            $this->height .
-            $this->marginLeft .
-            $this->marginTop .
-            ($this->visible ? 1 : 0) .
-            $this->fillColor->getHashCode() .
-            $this->alignment .
-            ($this->hasBackgroundImage() ? $this->backgroundImage->getHashCode() : '') .
-            __CLASS__
+            $this->author
+            . $this->text->getHashCode()
+            . $this->width
+            . $this->height
+            . $this->marginLeft
+            . $this->marginTop
+            . ($this->visible ? 1 : 0)
+            . $this->fillColor->getHashCode()
+            . $this->alignment
+            . $this->textboxDirection
+            . ($this->hasBackgroundImage() ? $this->backgroundImage->getHashCode() : '')
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php
index 0f4df26..686e56e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/DefinedName.php
@@ -10,45 +10,33 @@ abstract class DefinedName
 
     /**
      * Name.
-     *
-     * @var string
      */
-    protected $name;
+    protected string $name;
 
     /**
      * Worksheet on which the defined name can be resolved.
-     *
-     * @var Worksheet
      */
-    protected $worksheet;
+    protected ?Worksheet $worksheet;
 
     /**
      * Value of the named object.
-     *
-     * @var string
      */
-    protected $value;
+    protected string $value;
 
     /**
      * Is the defined named local? (i.e. can only be used on $this->worksheet).
-     *
-     * @var bool
      */
-    protected $localOnly;
+    protected bool $localOnly;
 
     /**
      * Scope.
-     *
-     * @var Worksheet
      */
-    protected $scope;
+    protected ?Worksheet $scope;
 
     /**
      * Whether this is a named range or a named formula.
-     *
-     * @var bool
      */
-    protected $isFormula;
+    protected bool $isFormula;
 
     /**
      * Create a new Defined Name.
@@ -78,6 +66,12 @@ abstract class DefinedName
         $this->isFormula = self::testIfFormula($this->value);
     }
 
+    public function __destruct()
+    {
+        $this->worksheet = null;
+        $this->scope = null;
+    }
+
     /**
      * Create a new defined name, either a range or a formula.
      */
@@ -99,7 +93,7 @@ abstract class DefinedName
 
     public static function testIfFormula(string $value): bool
     {
-        if (substr($value, 0, 1) === '=') {
+        if (str_starts_with($value, '=')) {
             $value = substr($value, 1);
         }
 
@@ -112,8 +106,8 @@ abstract class DefinedName
             //    Only test in alternate array entries (the non-quoted blocks)
             $segMatcher = $segMatcher === false;
             if (
-                $segMatcher &&
-                (preg_match('/' . self::REGEXP_IDENTIFY_FORMULA . '/miu', $subVal))
+                $segMatcher
+                && (preg_match('/' . self::REGEXP_IDENTIFY_FORMULA . '/miu', $subVal))
             ) {
                 return true;
             }
@@ -141,17 +135,19 @@ abstract class DefinedName
 
             // Re-attach
             if ($this->worksheet !== null) {
-                $this->worksheet->getParent()->removeNamedRange($this->name, $this->worksheet);
+                $this->worksheet->getParentOrThrow()->removeNamedRange($this->name, $this->worksheet);
             }
             $this->name = $name;
 
             if ($this->worksheet !== null) {
-                $this->worksheet->getParent()->addNamedRange($this);
+                $this->worksheet->getParentOrThrow()->addDefinedName($this);
             }
 
-            // New title
-            $newTitle = $this->name;
-            ReferenceHelper::getInstance()->updateNamedFormulae($this->worksheet->getParent(), $oldTitle, $newTitle);
+            if ($this->worksheet !== null) {
+                // New title
+                $newTitle = $this->name;
+                ReferenceHelper::getInstance()->updateNamedFormulae($this->worksheet->getParentOrThrow(), $oldTitle, $newTitle);
+            }
         }
 
         return $this;
@@ -247,13 +243,13 @@ abstract class DefinedName
         if ($sheetName === '') {
             $worksheet2 = $worksheet;
         } else {
-            $worksheet2 = $worksheet->getParent()->getSheetByName($sheetName);
+            $worksheet2 = $worksheet->getParentOrThrow()->getSheetByName($sheetName);
             if ($worksheet2 === null) {
                 return null;
             }
         }
 
-        return $worksheet->getParent()->getDefinedName($definedName, $worksheet2);
+        return $worksheet->getParentOrThrow()->getDefinedName($definedName, $worksheet2);
     }
 
     /**
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
index afdeea9..5762169 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
@@ -25,87 +25,69 @@ class Properties
 
     /**
      * Creator.
-     *
-     * @var string
      */
-    private $creator = 'Unknown Creator';
+    private string $creator = 'Unknown Creator';
 
     /**
      * LastModifiedBy.
-     *
-     * @var string
      */
-    private $lastModifiedBy;
+    private string $lastModifiedBy;
 
     /**
      * Created.
-     *
-     * @var float|int
      */
-    private $created;
+    private float|int $created;
 
     /**
      * Modified.
-     *
-     * @var float|int
      */
-    private $modified;
+    private float|int $modified;
 
     /**
      * Title.
-     *
-     * @var string
      */
-    private $title = 'Untitled Spreadsheet';
+    private string $title = 'Untitled Spreadsheet';
 
     /**
      * Description.
-     *
-     * @var string
      */
-    private $description = '';
+    private string $description = '';
 
     /**
      * Subject.
-     *
-     * @var string
      */
-    private $subject = '';
+    private string $subject = '';
 
     /**
      * Keywords.
-     *
-     * @var string
      */
-    private $keywords = '';
+    private string $keywords = '';
 
     /**
      * Category.
-     *
-     * @var string
      */
-    private $category = '';
+    private string $category = '';
 
     /**
      * Manager.
-     *
-     * @var string
      */
-    private $manager = '';
+    private string $manager = '';
 
     /**
      * Company.
-     *
-     * @var string
      */
-    private $company = '';
+    private string $company = '';
 
     /**
      * Custom Properties.
      *
-     * @var array{value: mixed, type: string}[]
+     * @var array{value: null|bool|float|int|string, type: string}[]
      */
-    private $customProperties = [];
+    private array $customProperties = [];
+
+    private string $hyperlinkBase = '';
+
+    private string $viewport = '';
 
     /**
      * Create a new Document Properties instance.
@@ -158,14 +140,9 @@ class Properties
         return $this;
     }
 
-    /**
-     * @param null|float|int|string $timestamp
-     *
-     * @return float|int
-     */
-    private static function intOrFloatTimestamp($timestamp)
+    private static function intOrFloatTimestamp(null|bool|float|int|string $timestamp): float|int
     {
-        if ($timestamp === null) {
+        if ($timestamp === null || is_bool($timestamp)) {
             $timestamp = (float) (new DateTime())->format('U');
         } elseif (is_string($timestamp)) {
             if (is_numeric($timestamp)) {
@@ -183,10 +160,8 @@ class Properties
 
     /**
      * Get Created.
-     *
-     * @return float|int
      */
-    public function getCreated()
+    public function getCreated(): float|int
     {
         return $this->created;
     }
@@ -194,11 +169,9 @@ class Properties
     /**
      * Set Created.
      *
-     * @param null|float|int|string $timestamp
-     *
      * @return $this
      */
-    public function setCreated($timestamp): self
+    public function setCreated(null|float|int|string $timestamp): self
     {
         $this->created = self::intOrFloatTimestamp($timestamp);
 
@@ -207,10 +180,8 @@ class Properties
 
     /**
      * Get Modified.
-     *
-     * @return float|int
      */
-    public function getModified()
+    public function getModified(): float|int
     {
         return $this->modified;
     }
@@ -218,11 +189,9 @@ class Properties
     /**
      * Set Modified.
      *
-     * @param null|float|int|string $timestamp
-     *
      * @return $this
      */
-    public function setModified($timestamp): self
+    public function setModified(null|float|int|string $timestamp): self
     {
         $this->modified = self::intOrFloatTimestamp($timestamp);
 
@@ -389,10 +358,8 @@ class Properties
 
     /**
      * Get a Custom Property Value.
-     *
-     * @return mixed
      */
-    public function getCustomPropertyValue(string $propertyName)
+    public function getCustomPropertyValue(string $propertyName): bool|int|float|string|null
     {
         if (isset($this->customProperties[$propertyName])) {
             return $this->customProperties[$propertyName]['value'];
@@ -403,18 +370,13 @@ class Properties
 
     /**
      * Get a Custom Property Type.
-     *
-     * @return null|string
      */
-    public function getCustomPropertyType(string $propertyName)
+    public function getCustomPropertyType(string $propertyName): ?string
     {
         return $this->customProperties[$propertyName]['type'] ?? null;
     }
 
-    /**
-     * @param mixed $propertyValue
-     */
-    private function identifyPropertyType($propertyValue): string
+    private function identifyPropertyType(bool|int|float|string|null $propertyValue): string
     {
         if (is_float($propertyValue)) {
             return self::PROPERTY_TYPE_FLOAT;
@@ -432,28 +394,20 @@ class Properties
     /**
      * Set a Custom Property.
      *
-     * @param mixed $propertyValue
-     * @param string $propertyType
-     *      'i'    : Integer
-     *   'f' : Floating Point
-     *   's' : String
-     *   'd' : Date/Time
-     *   'b' : Boolean
+     * @param ?string $propertyType see `self::VALID_PROPERTY_TYPE_LIST`
      *
      * @return $this
      */
-    public function setCustomProperty(string $propertyName, $propertyValue = '', $propertyType = null): self
+    public function setCustomProperty(string $propertyName, bool|int|float|string|null $propertyValue = '', ?string $propertyType = null): self
     {
         if (($propertyType === null) || (!in_array($propertyType, self::VALID_PROPERTY_TYPE_LIST))) {
             $propertyType = $this->identifyPropertyType($propertyValue);
         }
 
-        if (!is_object($propertyValue)) {
-            $this->customProperties[$propertyName] = [
-                'value' => self::convertProperty($propertyValue, $propertyType),
-                'type' => $propertyType,
-            ];
-        }
+        $this->customProperties[$propertyName] = [
+            'value' => self::convertProperty($propertyValue, $propertyType),
+            'type' => $propertyType,
+        ];
 
         return $this;
     }
@@ -494,24 +448,16 @@ class Properties
 
     /**
      * Convert property to form desired by Excel.
-     *
-     * @param mixed $propertyValue
-     *
-     * @return mixed
      */
-    public static function convertProperty($propertyValue, string $propertyType)
+    public static function convertProperty(bool|int|float|string|null $propertyValue, string $propertyType): bool|int|float|string|null
     {
         return self::SPECIAL_TYPES[$propertyType] ?? self::convertProperty2($propertyValue, $propertyType);
     }
 
     /**
      * Convert property to form desired by Excel.
-     *
-     * @param mixed $propertyValue
-     *
-     * @return mixed
      */
-    private static function convertProperty2($propertyValue, string $type)
+    private static function convertProperty2(bool|int|float|string|null $propertyValue, string $type): bool|int|float|string|null
     {
         $propertyType = self::convertPropertyType($type);
         switch ($propertyType) {
@@ -534,4 +480,30 @@ class Properties
     {
         return self::PROPERTY_TYPE_ARRAY[$propertyType] ?? self::PROPERTY_TYPE_UNKNOWN;
     }
+
+    public function getHyperlinkBase(): string
+    {
+        return $this->hyperlinkBase;
+    }
+
+    public function setHyperlinkBase(string $hyperlinkBase): self
+    {
+        $this->hyperlinkBase = $hyperlinkBase;
+
+        return $this;
+    }
+
+    public function getViewport(): string
+    {
+        return $this->viewport;
+    }
+
+    public const SUGGESTED_VIEWPORT = 'width=device-width, initial-scale=1';
+
+    public function setViewport(string $viewport): self
+    {
+        $this->viewport = $viewport;
+
+        return $this;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
index 279aed4..31a32be 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
@@ -8,38 +8,28 @@ class Security
 {
     /**
      * LockRevision.
-     *
-     * @var bool
      */
-    private $lockRevision = false;
+    private bool $lockRevision = false;
 
     /**
      * LockStructure.
-     *
-     * @var bool
      */
-    private $lockStructure = false;
+    private bool $lockStructure = false;
 
     /**
      * LockWindows.
-     *
-     * @var bool
      */
-    private $lockWindows = false;
+    private bool $lockWindows = false;
 
     /**
      * RevisionsPassword.
-     *
-     * @var string
      */
-    private $revisionsPassword = '';
+    private string $revisionsPassword = '';
 
     /**
      * WorkbookPassword.
-     *
-     * @var string
      */
-    private $workbookPassword = '';
+    private string $workbookPassword = '';
 
     /**
      * Create a new Document Security instance.
@@ -53,9 +43,9 @@ class Security
      */
     public function isSecurityEnabled(): bool
     {
-        return  $this->lockRevision ||
-                $this->lockStructure ||
-                $this->lockWindows;
+        return $this->lockRevision
+                || $this->lockStructure
+                || $this->lockWindows;
     }
 
     public function getLockRevision(): bool
@@ -108,12 +98,11 @@ class Security
     /**
      * Set RevisionsPassword.
      *
-     * @param string $password
      * @param bool $alreadyHashed If the password has already been hashed, set this to true
      *
      * @return $this
      */
-    public function setRevisionsPassword(?string $password, bool $alreadyHashed = false)
+    public function setRevisionsPassword(?string $password, bool $alreadyHashed = false): static
     {
         if ($password !== null) {
             if (!$alreadyHashed) {
@@ -133,12 +122,11 @@ class Security
     /**
      * Set WorkbookPassword.
      *
-     * @param string $password
      * @param bool $alreadyHashed If the password has already been hashed, set this to true
      *
      * @return $this
      */
-    public function setWorkbookPassword(?string $password, bool $alreadyHashed = false)
+    public function setWorkbookPassword(?string $password, bool $alreadyHashed = false): static
     {
         if ($password !== null) {
             if (!$alreadyHashed) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php
index 9c5ab30..3491580 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Exception.php
@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet;
 
-class Exception extends \Exception
+use RuntimeException;
+
+class Exception extends RuntimeException
 {
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php
index 6de7d03..94c4836 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/HashTable.php
@@ -12,21 +12,21 @@ class HashTable
      *
      * @var array<string, T>
      */
-    protected $items = [];
+    protected array $items = [];
 
     /**
      * HashTable key map.
      *
      * @var array<int, string>
      */
-    protected $keyMap = [];
+    protected array $keyMap = [];
 
     /**
      * Create a new HashTable.
      *
      * @param T[] $source Optional source array to create HashTable from
      */
-    public function __construct($source = null)
+    public function __construct(?array $source = [])
     {
         if ($source !== null) {
             // Create HashTable
@@ -101,22 +101,17 @@ class HashTable
 
     /**
      * Count.
-     *
-     * @return int
      */
-    public function count()
+    public function count(): int
     {
         return count($this->items);
     }
 
     /**
      * Get index for hash code.
-     *
-     * @return false|int Index
      */
-    public function getIndexForHashCode(string $hashCode)
+    public function getIndexForHashCode(string $hashCode): false|int
     {
-        // Scrutinizer thinks the following could return string. It is wrong.
         return array_search($hashCode, $this->keyMap, true);
     }
 
@@ -125,7 +120,7 @@ class HashTable
      *
      * @return null|T
      */
-    public function getByIndex(int $index)
+    public function getByIndex(int $index): ?IComparable
     {
         if (isset($this->keyMap[$index])) {
             return $this->getByHashCode($this->keyMap[$index]);
@@ -139,7 +134,7 @@ class HashTable
      *
      * @return null|T
      */
-    public function getByHashCode(string $hashCode)
+    public function getByHashCode(string $hashCode): ?IComparable
     {
         if (isset($this->items[$hashCode])) {
             return $this->items[$hashCode];
@@ -153,7 +148,7 @@ class HashTable
      *
      * @return T[]
      */
-    public function toArray()
+    public function toArray(): array
     {
         return $this->items;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php
index ff07ce5..a729dfd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php
@@ -48,20 +48,15 @@ class Dimension
      *                If this is a height, then size is measured in pixels ()
      *                   or in points () if $unit is null.
      */
-    protected $size;
+    protected float|int $size;
 
-    /**
-     * @var null|string
-     */
-    protected $unit;
+    protected ?string $unit = null;
 
     /**
      * Phpstan bug has been fixed; this function allows us to
      * pass Phpstan whether fixed or not.
-     *
-     * @param mixed $value
      */
-    private static function stanBugFixed($value): array
+    private static function stanBugFixed(array|int|null $value): array
     {
         return is_array($value) ? $value : [null, null];
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php
new file mode 100644
index 0000000..4e98c79
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Helper;
+
+use PhpOffice\PhpSpreadsheet\Exception;
+
+/**
+ * Assist downloading files when samples are run in browser.
+ * Never run as part of unit tests, which are command line.
+ *
+ * @codeCoverageIgnore
+ */
+class Downloader
+{
+    protected string $filepath;
+
+    protected string $filename;
+
+    protected string $filetype;
+
+    protected const CONTENT_TYPES = [
+        'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+        'xls' => 'application/vnd.ms-excel',
+        'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+        'csv' => 'text/csv',
+        'html' => 'text/html',
+        'pdf' => 'application/pdf',
+    ];
+
+    public function __construct(string $folder, string $filename, ?string $filetype = null)
+    {
+        if ((is_dir($folder) === false) || (is_readable($folder) === false)) {
+            throw new Exception("Folder {$folder} is not accessable");
+        }
+        $filepath = "{$folder}/{$filename}";
+        $this->filepath = (string) realpath($filepath);
+        $this->filename = basename($filepath);
+        if ((file_exists($this->filepath) === false) || (is_readable($this->filepath) === false)) {
+            throw new Exception("{$this->filename} not found, or cannot be read");
+        }
+
+        $filetype ??= pathinfo($filename, PATHINFO_EXTENSION);
+        if (array_key_exists(strtolower($filetype), self::CONTENT_TYPES) === false) {
+            throw new Exception("Invalid filetype: {$filetype} cannot be downloaded");
+        }
+        $this->filetype = strtolower($filetype);
+    }
+
+    public function download(): void
+    {
+        $this->headers();
+
+        readfile($this->filepath);
+    }
+
+    public function headers(): void
+    {
+        // I cannot tell what this ob_clean is paired with.
+        // I have never seen a problem with it, but someone has - issue 3739.
+        // Perhaps it should be removed altogether,
+        // but making it conditional seems harmless, and safer.
+        if ((int) ob_get_length() > 0) {
+            ob_clean();
+        }
+
+        $this->contentType();
+        $this->contentDisposition();
+        $this->cacheHeaders();
+        $this->fileSize();
+
+        flush();
+    }
+
+    protected function contentType(): void
+    {
+        header('Content-Type: ' . self::CONTENT_TYPES[$this->filetype]);
+    }
+
+    protected function contentDisposition(): void
+    {
+        header('Content-Disposition: attachment;filename="' . $this->filename . '"');
+    }
+
+    protected function cacheHeaders(): void
+    {
+        header('Cache-Control: max-age=0');
+        // If you're serving to IE 9, then the following may be needed
+        header('Cache-Control: max-age=1');
+
+        // If you're serving to IE over SSL, then the following may be needed
+        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
+        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
+        header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
+        header('Pragma: public'); // HTTP/1.0
+    }
+
+    protected function fileSize(): void
+    {
+        header('Content-Length: ' . filesize($this->filepath));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php
new file mode 100644
index 0000000..fb1b9a0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Helper;
+
+class Handler
+{
+    private static string $invalidHex = 'Y';
+
+    // A bunch of methods to show that we continue
+    // to capture messages even using PhpUnit 10.
+    public static function suppressed(): bool
+    {
+        return @trigger_error('hello');
+    }
+
+    public static function deprecated(): string
+    {
+        return (string) hexdec(self::$invalidHex);
+    }
+
+    public static function notice(string $value): void
+    {
+        date_default_timezone_set($value);
+    }
+
+    public static function warning(): bool
+    {
+        return file_get_contents(__FILE__ . 'noexist') !== false;
+    }
+
+    public static function userDeprecated(): bool
+    {
+        return trigger_error('hello', E_USER_DEPRECATED);
+    }
+
+    public static function userNotice(): bool
+    {
+        return trigger_error('userNotice', E_USER_NOTICE);
+    }
+
+    public static function userWarning(): bool
+    {
+        return trigger_error('userWarning', E_USER_WARNING);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php
index b8ed386..e690ae6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Helper;
 
+use DOMAttr;
 use DOMDocument;
 use DOMElement;
 use DOMNode;
@@ -532,77 +533,67 @@ class Html
         'yellowgreen' => '9acd32',
     ];
 
-    /** @var ?string */
-    private $face;
+    private ?string $face = null;
 
-    /** @var ?string */
-    private $size;
+    private ?string $size = null;
 
-    /** @var ?string */
-    private $color;
+    private ?string $color = null;
 
-    /** @var bool */
-    private $bold = false;
+    private bool $bold = false;
 
-    /** @var bool */
-    private $italic = false;
+    private bool $italic = false;
 
-    /** @var bool */
-    private $underline = false;
+    private bool $underline = false;
 
-    /** @var bool */
-    private $superscript = false;
+    private bool $superscript = false;
 
-    /** @var bool */
-    private $subscript = false;
+    private bool $subscript = false;
 
-    /** @var bool */
-    private $strikethrough = false;
+    private bool $strikethrough = false;
 
-    private const START_TAG_CALLBACKS = [
-        'font' => 'startFontTag',
-        'b' => 'startBoldTag',
-        'strong' => 'startBoldTag',
-        'i' => 'startItalicTag',
-        'em' => 'startItalicTag',
-        'u' => 'startUnderlineTag',
-        'ins' => 'startUnderlineTag',
-        'del' => 'startStrikethruTag',
-        'sup' => 'startSuperscriptTag',
-        'sub' => 'startSubscriptTag',
+    /** @var callable[] */
+    private array $startTagCallbacks = [
+        'font' => [self::class, 'startFontTag'],
+        'b' => [self::class, 'startBoldTag'],
+        'strong' => [self::class, 'startBoldTag'],
+        'i' => [self::class, 'startItalicTag'],
+        'em' => [self::class, 'startItalicTag'],
+        'u' => [self::class, 'startUnderlineTag'],
+        'ins' => [self::class, 'startUnderlineTag'],
+        'del' => [self::class, 'startStrikethruTag'],
+        'sup' => [self::class, 'startSuperscriptTag'],
+        'sub' => [self::class, 'startSubscriptTag'],
     ];
 
-    private const END_TAG_CALLBACKS = [
-        'font' => 'endFontTag',
-        'b' => 'endBoldTag',
-        'strong' => 'endBoldTag',
-        'i' => 'endItalicTag',
-        'em' => 'endItalicTag',
-        'u' => 'endUnderlineTag',
-        'ins' => 'endUnderlineTag',
-        'del' => 'endStrikethruTag',
-        'sup' => 'endSuperscriptTag',
-        'sub' => 'endSubscriptTag',
-        'br' => 'breakTag',
-        'p' => 'breakTag',
-        'h1' => 'breakTag',
-        'h2' => 'breakTag',
-        'h3' => 'breakTag',
-        'h4' => 'breakTag',
-        'h5' => 'breakTag',
-        'h6' => 'breakTag',
+    /** @var callable[] */
+    private array $endTagCallbacks = [
+        'font' => [self::class, 'endFontTag'],
+        'b' => [self::class, 'endBoldTag'],
+        'strong' => [self::class, 'endBoldTag'],
+        'i' => [self::class, 'endItalicTag'],
+        'em' => [self::class, 'endItalicTag'],
+        'u' => [self::class, 'endUnderlineTag'],
+        'ins' => [self::class, 'endUnderlineTag'],
+        'del' => [self::class, 'endStrikethruTag'],
+        'sup' => [self::class, 'endSuperscriptTag'],
+        'sub' => [self::class, 'endSubscriptTag'],
+        'br' => [self::class, 'breakTag'],
+        'p' => [self::class, 'breakTag'],
+        'h1' => [self::class, 'breakTag'],
+        'h2' => [self::class, 'breakTag'],
+        'h3' => [self::class, 'breakTag'],
+        'h4' => [self::class, 'breakTag'],
+        'h5' => [self::class, 'breakTag'],
+        'h6' => [self::class, 'breakTag'],
     ];
 
-    /** @var array */
-    private $stack = [];
+    private array $stack = [];
 
-    /** @var string */
-    private $stringData = '';
+    public string $stringData = '';
 
-    /**
-     * @var RichText
-     */
-    private $richTextObject;
+    private RichText $richTextObject;
+
+    private bool $preserveWhiteSpace = false;
 
     private function initialise(): void
     {
@@ -616,12 +607,8 @@ class Html
 
     /**
      * Parse HTML formatting and return the resulting RichText.
-     *
-     * @param string $html
-     *
-     * @return RichText
      */
-    public function toRichTextObject($html)
+    public function toRichTextObject(string $html, bool $preserveWhiteSpace = false): RichText
     {
         $this->initialise();
 
@@ -630,13 +617,14 @@ class Html
         //    Load the HTML file into the DOM object
         //  Note the use of error suppression, because typically this will be an html fragment, so not fully valid markup
         $prefix = '<?xml encoding="UTF-8">';
-        /** @scrutinizer ignore-unhandled */
         @$dom->loadHTML($prefix . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
         //    Discard excess white space
         $dom->preserveWhiteSpace = false;
 
         $this->richTextObject = new RichText();
+        $this->preserveWhiteSpace = $preserveWhiteSpace;
         $this->parseElements($dom);
+        $this->preserveWhiteSpace = false;
 
         // Clean any further spurious whitespace
         $this->cleanWhitespace();
@@ -703,7 +691,7 @@ class Html
     {
         preg_match_all('/\d+/', $rgbValue, $values);
         foreach ($values[0] as &$value) {
-            $value = str_pad(dechex($value), 2, '0', STR_PAD_LEFT);
+            $value = str_pad(dechex((int) $value), 2, '0', STR_PAD_LEFT);
         }
 
         return implode('', $values[0]);
@@ -714,18 +702,20 @@ class Html
         return self::COLOUR_MAP[$colorName] ?? '';
     }
 
-    private function startFontTag(DOMElement $tag): void
+    protected function startFontTag(DOMElement $tag): void
     {
         $attrs = $tag->attributes;
         if ($attrs !== null) {
+            /** @var DOMAttr $attribute */
             foreach ($attrs as $attribute) {
                 $attributeName = strtolower($attribute->name);
+                $attributeName = preg_replace('/^html:/', '', $attributeName) ?? $attributeName; // in case from Xml spreadsheet
                 $attributeValue = $attribute->value;
 
                 if ($attributeName == 'color') {
                     if (preg_match('/rgb\s*\(/', $attributeValue)) {
                         $this->$attributeName = $this->rgbToColour($attributeValue);
-                    } elseif (strpos(trim($attributeValue), '#') === 0) {
+                    } elseif (str_starts_with(trim($attributeValue), '#')) {
                         $this->$attributeName = ltrim($attributeValue, '#');
                     } else {
                         $this->$attributeName = static::colourNameLookup($attributeValue);
@@ -737,97 +727,108 @@ class Html
         }
     }
 
-    private function endFontTag(): void
+    protected function endFontTag(): void
     {
         $this->face = $this->size = $this->color = null;
     }
 
-    private function startBoldTag(): void
+    protected function startBoldTag(): void
     {
         $this->bold = true;
     }
 
-    private function endBoldTag(): void
+    protected function endBoldTag(): void
     {
         $this->bold = false;
     }
 
-    private function startItalicTag(): void
+    protected function startItalicTag(): void
     {
         $this->italic = true;
     }
 
-    private function endItalicTag(): void
+    protected function endItalicTag(): void
     {
         $this->italic = false;
     }
 
-    private function startUnderlineTag(): void
+    protected function startUnderlineTag(): void
     {
         $this->underline = true;
     }
 
-    private function endUnderlineTag(): void
+    protected function endUnderlineTag(): void
     {
         $this->underline = false;
     }
 
-    private function startSubscriptTag(): void
+    protected function startSubscriptTag(): void
     {
         $this->subscript = true;
     }
 
-    private function endSubscriptTag(): void
+    protected function endSubscriptTag(): void
     {
         $this->subscript = false;
     }
 
-    private function startSuperscriptTag(): void
+    protected function startSuperscriptTag(): void
     {
         $this->superscript = true;
     }
 
-    private function endSuperscriptTag(): void
+    protected function endSuperscriptTag(): void
     {
         $this->superscript = false;
     }
 
-    private function startStrikethruTag(): void
+    protected function startStrikethruTag(): void
     {
         $this->strikethrough = true;
     }
 
-    private function endStrikethruTag(): void
+    protected function endStrikethruTag(): void
     {
         $this->strikethrough = false;
     }
 
-    private function breakTag(): void
+    public function breakTag(): void
     {
         $this->stringData .= "\n";
     }
 
     private function parseTextNode(DOMText $textNode): void
     {
-        $domText = (string) preg_replace(
-            '/\s+/u',
-            ' ',
-            str_replace(["\r", "\n"], ' ', $textNode->nodeValue ?? '')
-        );
+        if ($this->preserveWhiteSpace) {
+            $domText = $textNode->nodeValue ?? '';
+        } else {
+            $domText = (string) preg_replace(
+                '/\s+/u',
+                ' ',
+                str_replace(["\r", "\n"], ' ', $textNode->nodeValue ?? '')
+            );
+        }
         $this->stringData .= $domText;
         $this->buildTextRun();
     }
 
-    /**
-     * @param string $callbackTag
-     */
-    private function handleCallback(DOMElement $element, $callbackTag, array $callbacks): void
+    public function addStartTagCallback(string $tag, callable $callback): void
+    {
+        $this->startTagCallbacks[$tag] = $callback;
+    }
+
+    public function addEndTagCallback(string $tag, callable $callback): void
+    {
+        $this->endTagCallbacks[$tag] = $callback;
+    }
+
+    /** @param callable[] $callbacks */
+    private function handleCallback(DOMElement $element, string $callbackTag, array $callbacks): void
     {
         if (isset($callbacks[$callbackTag])) {
             $elementHandler = $callbacks[$callbackTag];
-            if (method_exists($this, $elementHandler)) {
-                /** @phpstan-ignore-next-line */
-                call_user_func([$this, $elementHandler], $element);
+            if (is_callable($elementHandler)) {
+                call_user_func($elementHandler, $element, $this);
             }
         }
     }
@@ -837,12 +838,12 @@ class Html
         $callbackTag = strtolower($element->nodeName);
         $this->stack[] = $callbackTag;
 
-        $this->handleCallback($element, $callbackTag, self::START_TAG_CALLBACKS);
+        $this->handleCallback($element, $callbackTag, $this->startTagCallbacks);
 
         $this->parseElements($element);
         array_pop($this->stack);
 
-        $this->handleCallback($element, $callbackTag, self::END_TAG_CALLBACKS);
+        $this->handleCallback($element, $callbackTag, $this->endTagCallbacks);
     }
 
     private function parseElements(DOMNode $element): void
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
index 5ca546e..eded9ae 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
@@ -2,7 +2,10 @@
 
 namespace PhpOffice\PhpSpreadsheet\Helper;
 
+use PhpOffice\PhpSpreadsheet\Chart\Chart;
+use PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer;
 use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\Settings;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 use PhpOffice\PhpSpreadsheet\Writer\IWriter;
@@ -12,6 +15,7 @@ use RecursiveRegexIterator;
 use ReflectionClass;
 use RegexIterator;
 use RuntimeException;
+use Throwable;
 
 /**
  * Helper class to be used in sample code.
@@ -20,50 +24,40 @@ class Sample
 {
     /**
      * Returns whether we run on CLI or browser.
-     *
-     * @return bool
      */
-    public function isCli()
+    public function isCli(): bool
     {
         return PHP_SAPI === 'cli';
     }
 
     /**
      * Return the filename currently being executed.
-     *
-     * @return string
      */
-    public function getScriptFilename()
+    public function getScriptFilename(): string
     {
         return basename($_SERVER['SCRIPT_FILENAME'], '.php');
     }
 
     /**
      * Whether we are executing the index page.
-     *
-     * @return bool
      */
-    public function isIndex()
+    public function isIndex(): bool
     {
         return $this->getScriptFilename() === 'index';
     }
 
     /**
      * Return the page title.
-     *
-     * @return string
      */
-    public function getPageTitle()
+    public function getPageTitle(): string
     {
         return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename();
     }
 
     /**
      * Return the page heading.
-     *
-     * @return string
      */
-    public function getPageHeading()
+    public function getPageHeading(): string
     {
         return $this->isIndex() ? '' : '<h1>' . str_replace('_', ' ', $this->getScriptFilename()) . '</h1>';
     }
@@ -73,7 +67,7 @@ class Sample
      *
      * @return string[][] [$name => $path]
      */
-    public function getSamples()
+    public function getSamples(): array
     {
         // Populate samples
         $baseDir = realpath(__DIR__ . '/../../../samples');
@@ -87,17 +81,13 @@ class Sample
         $regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH);
 
         $files = [];
+        /** @var string[] $file */
         foreach ($regex as $file) {
             $file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0]));
-            if (is_array($file)) {
-                // @codeCoverageIgnoreStart
-                throw new RuntimeException('str_replace returned array');
-                // @codeCoverageIgnoreEnd
-            }
             $info = pathinfo($file);
             $category = str_replace('_', ' ', $info['dirname'] ?? '');
             $name = str_replace('_', ' ', (string) preg_replace('/(|\.php)/', '', $info['filename']));
-            if (!in_array($category, ['.', 'boostrap', 'templates'])) {
+            if (!in_array($category, ['.', 'bootstrap', 'templates']) && $name !== 'Header') {
                 if (!isset($files[$category])) {
                     $files[$category] = [];
                 }
@@ -117,21 +107,31 @@ class Sample
     /**
      * Write documents.
      *
-     * @param string $filename
      * @param string[] $writers
      */
-    public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls']): void
+    public function write(Spreadsheet $spreadsheet, string $filename, array $writers = ['Xlsx', 'Xls'], bool $withCharts = false, ?callable $writerCallback = null, bool $resetActiveSheet = true): void
     {
         // Set active sheet index to the first sheet, so Excel opens this as the first sheet
-        $spreadsheet->setActiveSheetIndex(0);
+        if ($resetActiveSheet) {
+            $spreadsheet->setActiveSheetIndex(0);
+        }
 
         // Write documents
         foreach ($writers as $writerType) {
             $path = $this->getFilename($filename, mb_strtolower($writerType));
             $writer = IOFactory::createWriter($spreadsheet, $writerType);
+            $writer->setIncludeCharts($withCharts);
+            if ($writerCallback !== null) {
+                $writerCallback($writer);
+            }
             $callStartTime = microtime(true);
             $writer->save($path);
-            $this->logWrite($writer, $path, /** @scrutinizer ignore-type */ $callStartTime);
+            $this->logWrite($writer, $path, $callStartTime);
+            if ($this->isCli() === false) {
+                // @codeCoverageIgnoreStart
+                echo '<a href="/download.php?type=' . pathinfo($path, PATHINFO_EXTENSION) . '&name=' . basename($path) . '">Download ' . basename($path) . '</a><br />';
+                // @codeCoverageIgnoreEnd
+            }
         }
 
         $this->logEndingNotes();
@@ -144,10 +144,8 @@ class Sample
 
     /**
      * Returns the temporary directory and make sure it exists.
-     *
-     * @return string
      */
-    private function getTemporaryFolder()
+    public function getTemporaryFolder(): string
     {
         $tempFolder = sys_get_temp_dir() . '/phpspreadsheet';
         if (!$this->isDirOrMkdir($tempFolder)) {
@@ -159,27 +157,18 @@ class Sample
 
     /**
      * Returns the filename that should be used for sample output.
-     *
-     * @param string $filename
-     * @param string $extension
-     *
-     * @return string
      */
-    public function getFilename($filename, $extension = 'xlsx')
+    public function getFilename(string $filename, string $extension = 'xlsx'): string
     {
         $originalExtension = pathinfo($filename, PATHINFO_EXTENSION);
 
-        return $this->getTemporaryFolder() . '/' . str_replace('.' . /** @scrutinizer ignore-type */ $originalExtension, '.' . $extension, basename($filename));
+        return $this->getTemporaryFolder() . '/' . str_replace('.' . $originalExtension, '.' . $extension, basename($filename));
     }
 
     /**
      * Return a random temporary file name.
-     *
-     * @param string $extension
-     *
-     * @return string
      */
-    public function getTemporaryFilename($extension = 'xlsx')
+    public function getTemporaryFilename(string $extension = 'xlsx'): string
     {
         $temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
         if ($temporaryFilename === false) {
@@ -195,7 +184,49 @@ class Sample
     public function log(string $message): void
     {
         $eol = $this->isCli() ? PHP_EOL : '<br />';
-        echo date('H:i:s ') . $message . $eol;
+        echo ($this->isCli() ? date('H:i:s ') : '') . $message . $eol;
+    }
+
+    /**
+     * Render chart as part of running chart samples in browser.
+     * Charts are not rendered in unit tests, which are command line.
+     *
+     * @codeCoverageIgnore
+     */
+    public function renderChart(Chart $chart, string $fileName, ?Spreadsheet $spreadsheet = null): void
+    {
+        if ($this->isCli() === true) {
+            return;
+        }
+        Settings::setChartRenderer(MtJpGraphRenderer::class);
+
+        $fileName = $this->getFilename($fileName, 'png');
+        $title = $chart->getTitle();
+        $caption = null;
+        if ($title !== null) {
+            $calculatedTitle = $title->getCalculatedTitle($spreadsheet);
+            if ($calculatedTitle !== null) {
+                $caption = $title->getCaption();
+                $title->setCaption($calculatedTitle);
+            }
+        }
+
+        try {
+            $chart->render($fileName);
+            $this->log('Rendered image: ' . $fileName);
+            $imageData = @file_get_contents($fileName);
+            if ($imageData !== false) {
+                echo '<div><img src="data:image/gif;base64,' . base64_encode($imageData) . '" /></div>';
+            } else {
+                $this->log('Unable to open chart' . PHP_EOL);
+            }
+        } catch (Throwable $e) {
+            $this->log('Error rendering chart: ' . $e->getMessage() . PHP_EOL);
+        }
+        if (isset($title, $caption)) {
+            $title->setCaption($caption);
+        }
+        Settings::unsetChartRenderer();
     }
 
     public function titles(string $category, string $functionName, ?string $description = null): void
@@ -219,10 +250,10 @@ class Sample
         ?string $descriptionCell = null
     ): void {
         if ($descriptionCell !== null) {
-            $this->log($worksheet->getCell($descriptionCell)->getValue());
+            $this->log($worksheet->getCell($descriptionCell)->getValueString());
         }
-        $this->log($worksheet->getCell($formulaCell)->getValue());
-        $this->log(sprintf('%s() Result is ', $functionName) . $worksheet->getCell($formulaCell)->getCalculatedValue());
+        $this->log($worksheet->getCell($formulaCell)->getValueString());
+        $this->log(sprintf('%s() Result is ', $functionName) . $worksheet->getCell($formulaCell)->getCalculatedValueString());
     }
 
     /**
@@ -236,29 +267,24 @@ class Sample
 
     /**
      * Log a line about the write operation.
-     *
-     * @param string $path
-     * @param float $callStartTime
      */
-    public function logWrite(IWriter $writer, $path, $callStartTime): void
+    public function logWrite(IWriter $writer, string $path, float $callStartTime): void
     {
         $callEndTime = microtime(true);
         $callTime = $callEndTime - $callStartTime;
         $reflection = new ReflectionClass($writer);
         $format = $reflection->getShortName();
-        $message = "Write {$format} format to <code>{$path}</code>  in " . sprintf('%.4f', $callTime) . ' seconds';
+
+        $codePath = $this->isCli() ? $path : "<code>$path</code>";
+        $message = "Write {$format} format to {$codePath}  in " . sprintf('%.4f', $callTime) . ' seconds';
 
         $this->log($message);
     }
 
     /**
      * Log a line about the read operation.
-     *
-     * @param string $format
-     * @param string $path
-     * @param float $callStartTime
      */
-    public function logRead($format, $path, $callStartTime): void
+    public function logRead(string $format, string $path, float $callStartTime): void
     {
         $callEndTime = microtime(true);
         $callTime = $callEndTime - $callStartTime;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php
index 12ba4ef..575ed89 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php
@@ -2,24 +2,17 @@
 
 namespace PhpOffice\PhpSpreadsheet\Helper;
 
-class Size
+use Stringable;
+
+class Size implements Stringable
 {
     const REGEXP_SIZE_VALIDATION = '/^(?P<size>\d*\.?\d+)(?P<unit>pt|px|em)?$/i';
 
-    /**
-     * @var bool
-     */
-    protected $valid;
+    protected bool $valid;
 
-    /**
-     * @var string
-     */
-    protected $size = '';
+    protected string $size = '';
 
-    /**
-     * @var string
-     */
-    protected $unit = '';
+    protected string $unit = '';
 
     public function __construct(string $size)
     {
@@ -45,7 +38,7 @@ class Size
         return $this->unit;
     }
 
-    public function __toString()
+    public function __toString(): string
     {
         return $this->size . $this->unit;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php
index ed146a5..f706b58 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php
@@ -4,30 +4,15 @@ namespace PhpOffice\PhpSpreadsheet\Helper;
 
 class TextGrid
 {
-    /**
-     * @var bool
-     */
-    private $isCli = true;
+    private bool $isCli;
 
-    /**
-     * @var array
-     */
-    protected $matrix;
+    protected array $matrix;
 
-    /**
-     * @var array
-     */
-    protected $rows;
+    protected array $rows;
 
-    /**
-     * @var array
-     */
-    protected $columns;
+    protected array $columns;
 
-    /**
-     * @var string
-     */
-    private $gridDisplay;
+    private string $gridDisplay;
 
     public function __construct(array $matrix, bool $isCli = true)
     {
@@ -48,17 +33,17 @@ class TextGrid
 
     public function render(): string
     {
-        $this->gridDisplay = $this->isCli ? '' : '<code>';
+        $this->gridDisplay = $this->isCli ? '' : '<pre>';
 
         $maxRow = max($this->rows);
-        $maxRowLength = strlen((string) $maxRow) + 1;
+        $maxRowLength = mb_strlen((string) $maxRow) + 1;
         $columnWidths = $this->getColumnWidths();
 
         $this->renderColumnHeader($maxRowLength, $columnWidths);
         $this->renderRows($maxRowLength, $columnWidths);
         $this->renderFooter($maxRowLength, $columnWidths);
 
-        $this->gridDisplay .= $this->isCli ? '' : '</code>';
+        $this->gridDisplay .= $this->isCli ? '' : '</pre>';
 
         return $this->gridDisplay;
     }
@@ -75,9 +60,9 @@ class TextGrid
     private function renderCells(array $rowData, array $columnWidths): void
     {
         foreach ($rowData as $column => $cell) {
-            $cell = ($this->isCli) ? (string) $cell : htmlentities((string) $cell);
+            $displayCell = ($this->isCli) ? (string) $cell : htmlentities((string) $cell);
             $this->gridDisplay .= '| ';
-            $this->gridDisplay .= str_pad($cell, $columnWidths[$column] + 1, ' ');
+            $this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($cell ?? '') + 1);
         }
     }
 
@@ -126,12 +111,12 @@ class TextGrid
 
         foreach ($columnData as $columnValue) {
             if (is_string($columnValue)) {
-                $columnWidth = max($columnWidth, strlen($columnValue));
+                $columnWidth = max($columnWidth, mb_strlen($columnValue));
             } elseif (is_bool($columnValue)) {
-                $columnWidth = max($columnWidth, strlen($columnValue ? 'TRUE' : 'FALSE'));
+                $columnWidth = max($columnWidth, mb_strlen($columnValue ? 'TRUE' : 'FALSE'));
             }
 
-            $columnWidth = max($columnWidth, strlen((string) $columnWidth));
+            $columnWidth = max($columnWidth, mb_strlen((string) $columnWidth));
         }
 
         return $columnWidth;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php
index c215847..64b451e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IComparable.php
@@ -9,5 +9,5 @@ interface IComparable
      *
      * @return string Hash code
      */
-    public function getHashCode();
+    public function getHashCode(): string;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
index e437a22..db8776d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php
@@ -30,7 +30,8 @@ abstract class IOFactory
     public const WRITER_CSV = 'Csv';
     public const WRITER_HTML = 'Html';
 
-    private static $readers = [
+    /** @var array<string, class-string<IReader>> */
+    private static array $readers = [
         self::READER_XLSX => Reader\Xlsx::class,
         self::READER_XLS => Reader\Xls::class,
         self::READER_XML => Reader\Xml::class,
@@ -41,7 +42,8 @@ abstract class IOFactory
         self::READER_CSV => Reader\Csv::class,
     ];
 
-    private static $writers = [
+    /** @var array<string, class-string<IWriter>> */
+    private static array $writers = [
         self::WRITER_XLS => Writer\Xls::class,
         self::WRITER_XLSX => Writer\Xlsx::class,
         self::WRITER_ODS => Writer\Ods::class,
@@ -88,7 +90,9 @@ abstract class IOFactory
      * @param string $filename The name of the spreadsheet file
      * @param int $flags the optional second parameter flags may be used to identify specific elements
      *                       that should be loaded, but which won't be loaded by default, using these values:
-     *                            IReader::LOAD_WITH_CHARTS - Include any charts that are defined in the loaded file
+     *                            IReader::LOAD_WITH_CHARTS - Include any charts that are defined in the loaded file.
+     *                            IReader::READ_DATA_ONLY - Read cell values only, not formatting or merge structure.
+     *                            IReader::IGNORE_EMPTY_CELLS - Don't load empty cells into the model.
      * @param string[] $readers An array of Readers to use to identify the file type. By default, load() will try
      *                             all possible Readers until it finds a match; but this allows you to pass in a
      *                             list of Readers so it will only try the subset that you specify here.
@@ -108,7 +112,7 @@ abstract class IOFactory
     public static function identify(string $filename, ?array $readers = null): string
     {
         $reader = self::createReaderForFile($filename, $readers);
-        $className = get_class($reader);
+        $className = $reader::class;
         $classType = explode('\\', $className);
         unset($reader);
 
@@ -133,9 +137,7 @@ abstract class IOFactory
             $readers = array_map('strtoupper', $readers);
             $testReaders = array_filter(
                 self::$readers,
-                function (string $readerType) use ($readers) {
-                    return in_array(strtoupper($readerType), $readers, true);
-                },
+                fn (string $readerType): bool => in_array(strtoupper($readerType), $readers, true),
                 ARRAY_FILTER_USE_KEY
             );
         }
@@ -176,39 +178,40 @@ abstract class IOFactory
             return null;
         }
 
-        switch (strtolower($pathinfo['extension'])) {
-            case 'xlsx': // Excel (OfficeOpenXML) Spreadsheet
-            case 'xlsm': // Excel (OfficeOpenXML) Macro Spreadsheet (macros will be discarded)
-            case 'xltx': // Excel (OfficeOpenXML) Template
-            case 'xltm': // Excel (OfficeOpenXML) Macro Template (macros will be discarded)
-                return 'Xlsx';
-            case 'xls': // Excel (BIFF) Spreadsheet
-            case 'xlt': // Excel (BIFF) Template
-                return 'Xls';
-            case 'ods': // Open/Libre Offic Calc
-            case 'ots': // Open/Libre Offic Calc Template
-                return 'Ods';
-            case 'slk':
-                return 'Slk';
-            case 'xml': // Excel 2003 SpreadSheetML
-                return 'Xml';
-            case 'gnumeric':
-                return 'Gnumeric';
-            case 'htm':
-            case 'html':
-                return 'Html';
-            case 'csv':
-                // Do nothing
-                // We must not try to use CSV reader since it loads
-                // all files including Excel files etc.
-                return null;
-            default:
-                return null;
-        }
+        return match (strtolower($pathinfo['extension'])) {
+            // Excel (OfficeOpenXML) Spreadsheet
+            'xlsx',
+            // Excel (OfficeOpenXML) Macro Spreadsheet (macros will be discarded)
+            'xlsm',
+            // Excel (OfficeOpenXML) Template
+            'xltx',
+            // Excel (OfficeOpenXML) Macro Template (macros will be discarded)
+            'xltm' => 'Xlsx',
+            // Excel (BIFF) Spreadsheet
+            'xls',
+            // Excel (BIFF) Template
+            'xlt' => 'Xls',
+            // Open/Libre Offic Calc
+            'ods',
+            // Open/Libre Offic Calc Template
+            'ots' => 'Ods',
+            'slk' => 'Slk',
+            // Excel 2003 SpreadSheetML
+            'xml' => 'Xml',
+            'gnumeric' => 'Gnumeric',
+            'htm', 'html' => 'Html',
+            // Do nothing
+            // We must not try to use CSV reader since it loads
+            // all files including Excel files etc.
+            'csv' => null,
+            default => null,
+        };
     }
 
     /**
      * Register a writer with its type and class name.
+     *
+     * @param class-string<IWriter> $writerClass
      */
     public static function registerWriter(string $writerType, string $writerClass): void
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
index db9c5f1..819ddea 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/NamedRange.php
@@ -46,7 +46,7 @@ class NamedRange extends DefinedName
     public function getCellsInRange(): array
     {
         $range = $this->value;
-        if (substr($range, 0, 1) === '=') {
+        if (str_starts_with($range, '=')) {
             $range = substr($range, 1);
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
index 42268d6..1408f7b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
@@ -14,97 +14,107 @@ abstract class BaseReader implements IReader
      * Read data only?
      * Identifies whether the Reader should only read data values for cells, and ignore any formatting information;
      *        or whether it should read both data and formatting.
-     *
-     * @var bool
      */
-    protected $readDataOnly = false;
+    protected bool $readDataOnly = false;
 
     /**
      * Read empty cells?
      * Identifies whether the Reader should read data values for cells all cells, or should ignore cells containing
      *         null value or empty string.
-     *
-     * @var bool
      */
-    protected $readEmptyCells = true;
+    protected bool $readEmptyCells = true;
 
     /**
      * Read charts that are defined in the workbook?
      * Identifies whether the Reader should read the definitions for any charts that exist in the workbook;.
-     *
-     * @var bool
      */
-    protected $includeCharts = false;
+    protected bool $includeCharts = false;
 
     /**
      * Restrict which sheets should be loaded?
      * This property holds an array of worksheet names to be loaded. If null, then all worksheets will be loaded.
+     * This property is ignored for Csv, Html, and Slk.
      *
      * @var null|string[]
      */
-    protected $loadSheetsOnly;
+    protected ?array $loadSheetsOnly = null;
+
+    /**
+     * Ignore rows with no cells?
+     * Identifies whether the Reader should ignore rows with no cells.
+     *        Currently implemented only for Xlsx.
+     */
+    protected bool $ignoreRowsWithNoCells = false;
 
     /**
      * IReadFilter instance.
-     *
-     * @var IReadFilter
      */
-    protected $readFilter;
+    protected IReadFilter $readFilter;
 
+    /** @var resource */
     protected $fileHandle;
 
-    /**
-     * @var XmlScanner
-     */
-    protected $securityScanner;
+    protected ?XmlScanner $securityScanner = null;
 
     public function __construct()
     {
         $this->readFilter = new DefaultReadFilter();
     }
 
-    public function getReadDataOnly()
+    public function getReadDataOnly(): bool
     {
         return $this->readDataOnly;
     }
 
-    public function setReadDataOnly($readCellValuesOnly)
+    public function setReadDataOnly(bool $readCellValuesOnly): self
     {
-        $this->readDataOnly = (bool) $readCellValuesOnly;
+        $this->readDataOnly = $readCellValuesOnly;
 
         return $this;
     }
 
-    public function getReadEmptyCells()
+    public function getReadEmptyCells(): bool
     {
         return $this->readEmptyCells;
     }
 
-    public function setReadEmptyCells($readEmptyCells)
+    public function setReadEmptyCells(bool $readEmptyCells): self
     {
-        $this->readEmptyCells = (bool) $readEmptyCells;
+        $this->readEmptyCells = $readEmptyCells;
 
         return $this;
     }
 
-    public function getIncludeCharts()
+    public function getIgnoreRowsWithNoCells(): bool
+    {
+        return $this->ignoreRowsWithNoCells;
+    }
+
+    public function setIgnoreRowsWithNoCells(bool $ignoreRowsWithNoCells): self
+    {
+        $this->ignoreRowsWithNoCells = $ignoreRowsWithNoCells;
+
+        return $this;
+    }
+
+    public function getIncludeCharts(): bool
     {
         return $this->includeCharts;
     }
 
-    public function setIncludeCharts($includeCharts)
+    public function setIncludeCharts(bool $includeCharts): self
     {
-        $this->includeCharts = (bool) $includeCharts;
+        $this->includeCharts = $includeCharts;
 
         return $this;
     }
 
-    public function getLoadSheetsOnly()
+    public function getLoadSheetsOnly(): ?array
     {
         return $this->loadSheetsOnly;
     }
 
-    public function setLoadSheetsOnly($sheetList)
+    public function setLoadSheetsOnly(string|array|null $sheetList): self
     {
         if ($sheetList === null) {
             return $this->setLoadAllSheets();
@@ -115,30 +125,39 @@ abstract class BaseReader implements IReader
         return $this;
     }
 
-    public function setLoadAllSheets()
+    public function setLoadAllSheets(): self
     {
         $this->loadSheetsOnly = null;
 
         return $this;
     }
 
-    public function getReadFilter()
+    public function getReadFilter(): IReadFilter
     {
         return $this->readFilter;
     }
 
-    public function setReadFilter(IReadFilter $readFilter)
+    public function setReadFilter(IReadFilter $readFilter): self
     {
         $this->readFilter = $readFilter;
 
         return $this;
     }
 
-    public function getSecurityScanner()
+    public function getSecurityScanner(): ?XmlScanner
     {
         return $this->securityScanner;
     }
 
+    public function getSecurityScannerOrThrow(): XmlScanner
+    {
+        if ($this->securityScanner === null) {
+            throw new ReaderException('Security scanner is unexpectedly null');
+        }
+
+        return $this->securityScanner;
+    }
+
     protected function processFlags(int $flags): void
     {
         if (((bool) ($flags & self::LOAD_WITH_CHARTS)) === true) {
@@ -147,9 +166,12 @@ abstract class BaseReader implements IReader
         if (((bool) ($flags & self::READ_DATA_ONLY)) === true) {
             $this->setReadDataOnly(true);
         }
-        if (((bool) ($flags & self::SKIP_EMPTY_CELLS)) === true) {
+        if (((bool) ($flags & self::SKIP_EMPTY_CELLS) || (bool) ($flags & self::IGNORE_EMPTY_CELLS)) === true) {
             $this->setReadEmptyCells(false);
         }
+        if (((bool) ($flags & self::IGNORE_ROWS_WITH_NO_CELLS)) === true) {
+            $this->setIgnoreRowsWithNoCells(true);
+        }
     }
 
     protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
@@ -193,4 +215,31 @@ abstract class BaseReader implements IReader
 
         $this->fileHandle = $fileHandle;
     }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     */
+    public function listWorksheetInfo(string $filename): array
+    {
+        throw new PhpSpreadsheetException('Reader classes must implement their own listWorksheetInfo() method');
+    }
+
+    /**
+     * Returns names of the worksheets from a file,
+     * possibly without parsing the whole file to a Spreadsheet object.
+     * Readers will often have a more efficient method with which
+     * they can override this method.
+     */
+    public function listWorksheetNames(string $filename): array
+    {
+        $returnArray = [];
+        $info = $this->listWorksheetInfo($filename);
+        foreach ($info as $infoArray) {
+            if (isset($infoArray['worksheetName'])) {
+                $returnArray[] = $infoArray['worksheetName'];
+            }
+        }
+
+        return $returnArray;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
index 4f128d6..ac4fa68 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
@@ -9,7 +9,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Csv\Delimiter;
 use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
-use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class Csv extends BaseReader
 {
@@ -32,52 +32,38 @@ class Csv extends BaseReader
 
     /**
      * Input encoding.
-     *
-     * @var string
      */
-    private $inputEncoding = 'UTF-8';
+    private string $inputEncoding = 'UTF-8';
 
     /**
      * Fallback encoding if guess strikes out.
-     *
-     * @var string
      */
-    private $fallbackEncoding = self::DEFAULT_FALLBACK_ENCODING;
+    private string $fallbackEncoding = self::DEFAULT_FALLBACK_ENCODING;
 
     /**
      * Delimiter.
-     *
-     * @var ?string
      */
-    private $delimiter;
+    private ?string $delimiter = null;
 
     /**
      * Enclosure.
-     *
-     * @var string
      */
-    private $enclosure = '"';
+    private string $enclosure = '"';
 
     /**
      * Sheet index to read.
-     *
-     * @var int
      */
-    private $sheetIndex = 0;
+    private int $sheetIndex = 0;
 
     /**
      * Load rows contiguously.
-     *
-     * @var bool
      */
-    private $contiguous = false;
+    private bool $contiguous = false;
 
     /**
      * The character that can escape the enclosure.
-     *
-     * @var string
      */
-    private $escapeCharacter = '\\';
+    private string $escapeCharacter = '\\';
 
     /**
      * Callback for setting defaults in construction.
@@ -88,23 +74,24 @@ class Csv extends BaseReader
 
     /**
      * Attempt autodetect line endings (deprecated after PHP8.1)?
-     *
-     * @var bool
      */
-    private $testAutodetect = true;
+    private bool $testAutodetect = true;
 
-    /**
-     * @var bool
-     */
-    protected $castFormattedNumberToNumeric = false;
+    protected bool $castFormattedNumberToNumeric = false;
 
-    /**
-     * @var bool
-     */
-    protected $preserveNumericFormatting = false;
+    protected bool $preserveNumericFormatting = false;
 
-    /** @var bool */
-    private $preserveNullString = false;
+    private bool $preserveNullString = false;
+
+    private bool $sheetNameIsFileName = false;
+
+    private string $getTrue = 'true';
+
+    private string $getFalse = 'false';
+
+    private string $thousandsSeparator = ',';
+
+    private string $decimalSeparator = '.';
 
     /**
      * Create a new CSV Reader instance.
@@ -238,13 +225,14 @@ class Csv extends BaseReader
         $worksheetInfo[0]['lastColumnIndex'] = 0;
         $worksheetInfo[0]['totalRows'] = 0;
         $worksheetInfo[0]['totalColumns'] = 0;
+        $delimiter = $this->delimiter ?? '';
 
         // Loop through each line of the file in turn
-        $rowData = fgetcsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
+        $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
         while (is_array($rowData)) {
             ++$worksheetInfo[0]['totalRows'];
             $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
-            $rowData = fgetcsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
+            $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
         }
 
         $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
@@ -285,7 +273,7 @@ class Csv extends BaseReader
         // Open file
         $fhandle = $this->canRead($filename);
         if (!$fhandle) {
-            throw new Exception($filename . ' is an Invalid Spreadsheet file.');
+            throw new ReaderException($filename . ' is an Invalid Spreadsheet file.');
         }
         if ($this->inputEncoding === self::GUESS_ENCODING) {
             $this->inputEncoding = self::guessEncoding($filename, $this->fallbackEncoding);
@@ -294,8 +282,9 @@ class Csv extends BaseReader
         if ($this->inputEncoding !== 'UTF-8') {
             fclose($this->fileHandle);
             $entireFile = file_get_contents($filename);
-            $this->fileHandle = fopen('php://memory', 'r+b');
-            if ($this->fileHandle !== false && $entireFile !== false) {
+            $fileHandle = fopen('php://memory', 'r+b');
+            if ($fileHandle !== false && $entireFile !== false) {
+                $this->fileHandle = $fileHandle;
                 $data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
                 fwrite($this->fileHandle, $data);
                 $this->skipBOM();
@@ -380,21 +369,33 @@ class Csv extends BaseReader
             $spreadsheet->createSheet();
         }
         $sheet = $spreadsheet->setActiveSheetIndex($this->sheetIndex);
+        if ($this->sheetNameIsFileName) {
+            $sheet->setTitle(substr(basename($filename, '.csv'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH));
+        }
 
         // Set our starting row based on whether we're in contiguous mode or not
         $currentRow = 1;
         $outRow = 0;
 
         // Loop through each line of the file in turn
-        $rowData = fgetcsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
+        $delimiter = $this->delimiter ?? '';
+        $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
         $valueBinder = Cell::getValueBinder();
         $preserveBooleanString = method_exists($valueBinder, 'getBooleanConversion') && $valueBinder->getBooleanConversion();
+        $this->getTrue = Calculation::getTRUE();
+        $this->getFalse = Calculation::getFALSE();
+        $this->thousandsSeparator = StringHelper::getThousandsSeparator();
+        $this->decimalSeparator = StringHelper::getDecimalSeparator();
         while (is_array($rowData)) {
             $noOutputYet = true;
             $columnLetter = 'A';
             foreach ($rowData as $rowDatum) {
-                $this->convertBoolean($rowDatum, $preserveBooleanString);
-                $numberFormatMask = $this->convertFormattedNumber($rowDatum);
+                if ($preserveBooleanString) {
+                    $rowDatum = $rowDatum ?? '';
+                } else {
+                    $this->convertBoolean($rowDatum);
+                }
+                $numberFormatMask = $this->castFormattedNumberToNumeric ? $this->convertFormattedNumber($rowDatum) : '';
                 if (($rowDatum !== '' || $this->preserveNullString) && $this->readFilter->readCell($columnLetter, $currentRow)) {
                     if ($this->contiguous) {
                         if ($noOutputYet) {
@@ -405,15 +406,17 @@ class Csv extends BaseReader
                         $outRow = $currentRow;
                     }
                     // Set basic styling for the value (Note that this could be overloaded by styling in a value binder)
-                    $sheet->getCell($columnLetter . $outRow)->getStyle()
-                        ->getNumberFormat()
-                        ->setFormatCode($numberFormatMask);
+                    if ($numberFormatMask !== '') {
+                        $sheet->getStyle($columnLetter . $outRow)
+                            ->getNumberFormat()
+                            ->setFormatCode($numberFormatMask);
+                    }
                     // Set cell value
                     $sheet->getCell($columnLetter . $outRow)->setValue($rowDatum);
                 }
                 ++$columnLetter;
             }
-            $rowData = fgetcsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
+            $rowData = fgetcsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
             ++$currentRow;
         }
 
@@ -428,15 +431,13 @@ class Csv extends BaseReader
 
     /**
      * Convert string true/false to boolean, and null to null-string.
-     *
-     * @param mixed $rowDatum
      */
-    private function convertBoolean(&$rowDatum, bool $preserveBooleanString): void
+    private function convertBoolean(mixed &$rowDatum): void
     {
-        if (is_string($rowDatum) && !$preserveBooleanString) {
-            if (strcasecmp(Calculation::getTRUE(), $rowDatum) === 0 || strcasecmp('true', $rowDatum) === 0) {
+        if (is_string($rowDatum)) {
+            if (strcasecmp($this->getTrue, $rowDatum) === 0 || strcasecmp('true', $rowDatum) === 0) {
                 $rowDatum = true;
-            } elseif (strcasecmp(Calculation::getFALSE(), $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) {
+            } elseif (strcasecmp($this->getFalse, $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) {
                 $rowDatum = false;
             }
         } else {
@@ -446,23 +447,21 @@ class Csv extends BaseReader
 
     /**
      * Convert numeric strings to int or float values.
-     *
-     * @param mixed $rowDatum
      */
-    private function convertFormattedNumber(&$rowDatum): string
+    private function convertFormattedNumber(mixed &$rowDatum): string
     {
-        $numberFormatMask = NumberFormat::FORMAT_GENERAL;
+        $numberFormatMask = '';
         if ($this->castFormattedNumberToNumeric === true && is_string($rowDatum)) {
             $numeric = str_replace(
-                [StringHelper::getThousandsSeparator(), StringHelper::getDecimalSeparator()],
+                [$this->thousandsSeparator, $this->decimalSeparator],
                 ['', '.'],
                 $rowDatum
             );
 
             if (is_numeric($numeric)) {
-                $decimalPos = strpos($rowDatum, StringHelper::getDecimalSeparator());
+                $decimalPos = strpos($rowDatum, $this->decimalSeparator);
                 if ($this->preserveNumericFormatting === true) {
-                    $numberFormatMask = (strpos($rowDatum, StringHelper::getThousandsSeparator()) !== false)
+                    $numberFormatMask = (str_contains($rowDatum, $this->thousandsSeparator))
                         ? '#,##0' : '0';
                     if ($decimalPos !== false) {
                         $decimals = strlen($rowDatum) - $decimalPos - 1;
@@ -548,14 +547,14 @@ class Csv extends BaseReader
         // Check if file exists
         try {
             $this->openFile($filename);
-        } catch (ReaderException $e) {
+        } catch (ReaderException) {
             return false;
         }
 
         fclose($this->fileHandle);
 
         // Trust file extension if any
-        $extension = strtolower(/** @scrutinizer ignore-type */ pathinfo($filename, PATHINFO_EXTENSION));
+        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
         if (in_array($extension, ['csv', 'tsv'])) {
             return true;
         }
@@ -567,6 +566,7 @@ class Csv extends BaseReader
             'text/csv',
             'text/plain',
             'inode/x-empty',
+            'text/html',
         ];
 
         return in_array($type, $supportedTypes, true);
@@ -600,7 +600,7 @@ class Csv extends BaseReader
     private static function guessEncodingTestBom(string &$encoding, string $first4, string $compare, string $setEncoding): void
     {
         if ($encoding === '') {
-            if ($compare === substr($first4, 0, strlen($compare))) {
+            if (str_starts_with($first4, $compare)) {
                 $encoding = $setEncoding;
             }
         }
@@ -642,4 +642,11 @@ class Csv extends BaseReader
     {
         return $this->preserveNullString;
     }
+
+    public function setSheetNameIsFileName(bool $sheetNameIsFileName): self
+    {
+        $this->sheetNameIsFileName = $sheetNameIsFileName;
+
+        return $this;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php
index 029d4a1..92ec0b5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php
@@ -9,20 +9,15 @@ class Delimiter
     /** @var resource */
     protected $fileHandle;
 
-    /** @var string */
-    protected $escapeCharacter;
+    protected string $escapeCharacter;
 
-    /** @var string */
-    protected $enclosure;
+    protected string $enclosure;
 
-    /** @var array */
-    protected $counts = [];
+    protected array $counts = [];
 
-    /** @var int */
-    protected $numberLines = 0;
+    protected int $numberLines = 0;
 
-    /** @var ?string */
-    protected $delimiter;
+    protected ?string $delimiter = null;
 
     /**
      * @param resource $fileHandle
@@ -92,9 +87,7 @@ class Delimiter
 
             $meanSquareDeviations[$delimiter] = array_reduce(
                 $series,
-                function ($sum, $value) use ($median) {
-                    return $sum + ($value - $median) ** 2;
-                }
+                fn ($sum, $value): int|float => $sum + ($value - $median) ** 2
             ) / count($series);
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
index 8fdb162..0c4b87b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
@@ -10,10 +10,8 @@ class DefaultReadFilter implements IReadFilter
      * @param string $columnAddress Column address (as a string value like "A", or "IV")
      * @param int $row Row number
      * @param string $worksheetName Optional worksheet name
-     *
-     * @return bool
      */
-    public function readCell($columnAddress, $row, $worksheetName = '')
+    public function readCell(string $columnAddress, int $row, string $worksheetName = ''): bool
     {
         return true;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
index b33af24..723e23b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
@@ -36,23 +36,17 @@ class Gnumeric extends BaseReader
 
     /**
      * Shared Expressions.
-     *
-     * @var array
      */
-    private $expressions = [];
+    private array $expressions = [];
 
     /**
      * Spreadsheet shared across all functions.
-     *
-     * @var Spreadsheet
      */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
-    /** @var ReferenceHelper */
-    private $referenceHelper;
+    private ReferenceHelper $referenceHelper;
 
-    /** @var array */
-    public static $mappings = [
+    public static array $mappings = [
         'dataType' => [
             '10' => DataType::TYPE_NULL,
             '20' => DataType::TYPE_BOOL,
@@ -80,17 +74,15 @@ class Gnumeric extends BaseReader
      */
     public function canRead(string $filename): bool
     {
-        // Check if gzlib functions are available
-        if (File::testFileNoThrow($filename) && function_exists('gzread')) {
-            // Read signature data (first 3 bytes)
-            $fh = fopen($filename, 'rb');
-            if ($fh !== false) {
-                $data = fread($fh, 2);
-                fclose($fh);
+        $data = null;
+        if (File::testFileNoThrow($filename)) {
+            $data = $this->gzfileGetContents($filename);
+            if (!str_contains($data, self::NAMESPACE_GNM)) {
+                $data = '';
             }
         }
 
-        return isset($data) && $data === chr(0x1F) . chr(0x8B);
+        return !empty($data);
     }
 
     private static function matchXml(XMLReader $xml, string $expectedLocalName): bool
@@ -102,17 +94,17 @@ class Gnumeric extends BaseReader
 
     /**
      * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetNames($filename)
+    public function listWorksheetNames(string $filename): array
     {
         File::assertFile($filename);
+        if (!$this->canRead($filename)) {
+            throw new Exception($filename . ' is an invalid Gnumeric file.');
+        }
 
         $xml = new XMLReader();
-        $xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($filename)), null, Settings::getLibXmlLoaderOptions());
+        $contents = $this->gzfileGetContents($filename);
+        $xml->xml($contents, null, Settings::getLibXmlLoaderOptions());
         $xml->setParserProperty(2, true);
 
         $worksheetNames = [];
@@ -131,17 +123,17 @@ class Gnumeric extends BaseReader
 
     /**
      * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetInfo($filename)
+    public function listWorksheetInfo(string $filename): array
     {
         File::assertFile($filename);
+        if (!$this->canRead($filename)) {
+            throw new Exception($filename . ' is an invalid Gnumeric file.');
+        }
 
         $xml = new XMLReader();
-        $xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($filename)), null, Settings::getLibXmlLoaderOptions());
+        $contents = $this->gzfileGetContents($filename);
+        $xml->xml($contents, null, Settings::getLibXmlLoaderOptions());
         $xml->setParserProperty(2, true);
 
         $worksheetInfo = [];
@@ -178,20 +170,25 @@ class Gnumeric extends BaseReader
         return $worksheetInfo;
     }
 
-    /**
-     * @param string $filename
-     *
-     * @return string
-     */
-    private function gzfileGetContents($filename)
+    private function gzfileGetContents(string $filename): string
     {
-        $file = @gzopen($filename, 'rb');
         $data = '';
-        if ($file !== false) {
-            while (!gzeof($file)) {
-                $data .= gzread($file, 1024);
+        $contents = @file_get_contents($filename);
+        if ($contents !== false) {
+            if (str_starts_with($contents, "\x1f\x8b")) {
+                // Check if gzlib functions are available
+                if (function_exists('gzdecode')) {
+                    $contents = @gzdecode($contents);
+                    if ($contents !== false) {
+                        $data = $contents;
+                    }
+                }
+            } else {
+                $data = $contents;
             }
-            gzclose($file);
+        }
+        if ($data !== '') {
+            $data = $this->getSecurityScannerOrThrow()->scan($data);
         }
 
         return $data;
@@ -217,10 +214,7 @@ class Gnumeric extends BaseReader
         }
     }
 
-    /**
-     * @param mixed $value
-     */
-    private static function testSimpleXml($value): SimpleXMLElement
+    private static function testSimpleXml(mixed $value): SimpleXMLElement
     {
         return ($value instanceof SimpleXMLElement) ? $value : new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><root></root>');
     }
@@ -245,10 +239,15 @@ class Gnumeric extends BaseReader
     {
         $this->spreadsheet = $spreadsheet;
         File::assertFile($filename);
+        if (!$this->canRead($filename)) {
+            throw new Exception($filename . ' is an invalid Gnumeric file.');
+        }
 
         $gFileData = $this->gzfileGetContents($filename);
 
-        $xml2 = simplexml_load_string($this->securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
+        /** @var XmlScanner */
+        $securityScanner = $this->securityScanner;
+        $xml2 = simplexml_load_string($securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
         $xml = self::testSimpleXml($xml2);
 
         $gnmXML = $xml->children(self::NAMESPACE_GNM);
@@ -362,7 +361,7 @@ class Gnumeric extends BaseReader
         //    Handle Merged Cells in this worksheet
         if ($sheet !== null && isset($sheet->MergedRegions)) {
             foreach ($sheet->MergedRegions->Merge as $mergeCells) {
-                if (strpos((string) $mergeCells, ':') !== false) {
+                if (str_contains((string) $mergeCells, ':')) {
                     $this->spreadsheet->getActiveSheet()->mergeCells($mergeCells, Worksheet::MERGE_CELL_CONTENT_HIDE);
                 }
             }
@@ -514,7 +513,7 @@ class Gnumeric extends BaseReader
             foreach ($gnmXML->Names->Name as $definedName) {
                 $name = (string) $definedName->name;
                 $value = (string) $definedName->value;
-                if (stripos($value, '#REF!') !== false) {
+                if (stripos($value, '#REF!') !== false || empty($value)) {
                     continue;
                 }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
index 204c730..f12b742 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php
@@ -10,10 +10,7 @@ use SimpleXMLElement;
 
 class PageSetup
 {
-    /**
-     * @var Spreadsheet
-     */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     public function __construct(Spreadsheet $spreadsheet)
     {
@@ -24,19 +21,30 @@ class PageSetup
     {
         if (isset($sheet->PrintInformation, $sheet->PrintInformation[0])) {
             $printInformation = $sheet->PrintInformation[0];
+            $setup = $this->spreadsheet->getActiveSheet()->getPageSetup();
 
-            $scale = (string) $printInformation->Scale->attributes()['percentage'];
+            $attributes = $printInformation->Scale->attributes();
+            if (isset($attributes['percentage'])) {
+                $setup->setScale((int) $attributes['percentage']);
+            }
             $pageOrder = (string) $printInformation->order;
+            if ($pageOrder === 'r_then_d') {
+                $setup->setPageOrder(WorksheetPageSetup::PAGEORDER_OVER_THEN_DOWN);
+            } elseif ($pageOrder === 'd_then_r') {
+                $setup->setPageOrder(WorksheetPageSetup::PAGEORDER_DOWN_THEN_OVER);
+            }
             $orientation = (string) $printInformation->orientation;
-            $horizontalCentered = (string) $printInformation->hcenter->attributes()['value'];
-            $verticalCentered = (string) $printInformation->vcenter->attributes()['value'];
-
-            $this->spreadsheet->getActiveSheet()->getPageSetup()
-                ->setPageOrder($pageOrder === 'r_then_d' ? WorksheetPageSetup::PAGEORDER_OVER_THEN_DOWN : WorksheetPageSetup::PAGEORDER_DOWN_THEN_OVER)
-                ->setScale((int) $scale)
-                ->setOrientation($orientation ?? WorksheetPageSetup::ORIENTATION_DEFAULT)
-                ->setHorizontalCentered((bool) $horizontalCentered)
-                ->setVerticalCentered((bool) $verticalCentered);
+            if ($orientation !== '') {
+                $setup->setOrientation($orientation);
+            }
+            $attributes = $printInformation->hcenter->attributes();
+            if (isset($attributes['value'])) {
+                $setup->setHorizontalCentered((bool) (string) $attributes['value']);
+            }
+            $attributes = $printInformation->vcenter->attributes();
+            if (isset($attributes['value'])) {
+                $setup->setVerticalCentered((bool) (string) $attributes['value']);
+            }
         }
 
         return $this;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php
index 23d4067..a60b453 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php
@@ -8,10 +8,7 @@ use SimpleXMLElement;
 
 class Properties
 {
-    /**
-     * @var Spreadsheet
-     */
-    protected $spreadsheet;
+    protected Spreadsheet $spreadsheet;
 
     public function __construct(Spreadsheet $spreadsheet)
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php
index dc44dcc..f901c4a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php
@@ -14,18 +14,11 @@ use SimpleXMLElement;
 
 class Styles
 {
-    /**
-     * @var Spreadsheet
-     */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
-    /**
-     * @var bool
-     */
-    protected $readDataOnly = false;
+    protected bool $readDataOnly;
 
-    /** @var array */
-    public static $mappings = [
+    public static array $mappings = [
         'borderStyle' => [
             '0' => Border::BORDER_NONE,
             '1' => Border::BORDER_THIN,
@@ -101,7 +94,6 @@ class Styles
     private function readStyles(SimpleXMLElement $styleRegion, int $maxRow, int $maxCol): void
     {
         foreach ($styleRegion as $style) {
-            /** @scrutinizer ignore-call */
             $styleAttributes = $style->attributes();
             if ($styleAttributes !== null && ($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) {
                 $cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow);
@@ -118,7 +110,7 @@ class Styles
                 if ($this->readDataOnly === false && $styleAttributes !== null) {
                     //    If readDataOnly is false, we set all formatting information
                     $styleArray['numberFormat']['formatCode'] = $formatCode;
-                    $styleArray = $this->readStyle($styleArray, $styleAttributes, /** @scrutinizer ignore-type */ $style);
+                    $styleArray = $this->readStyle($styleArray, $styleAttributes, $style);
                 }
                 $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
index 76f128e..2a2ffc4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
@@ -2,12 +2,18 @@
 
 namespace PhpOffice\PhpSpreadsheet\Reader;
 
+use DOMAttr;
 use DOMDocument;
 use DOMElement;
 use DOMNode;
 use DOMText;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Comment;
+use PhpOffice\PhpSpreadsheet\Document\Properties;
+use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
 use PhpOffice\PhpSpreadsheet\Helper\Dimension as CssDimension;
+use PhpOffice\PhpSpreadsheet\Helper\Html as HelperHtml;
 use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Style\Border;
@@ -26,26 +32,24 @@ class Html extends BaseReader
      */
     const TEST_SAMPLE_SIZE = 2048;
 
+    private const STARTS_WITH_BOM = '/^(?:\xfe\xff|\xff\xfe|\xEF\xBB\xBF)/';
+
+    private const DECLARES_CHARSET = '/ charset=/i';
+
     /**
      * Input encoding.
-     *
-     * @var string
      */
-    protected $inputEncoding = 'ANSI';
+    protected string $inputEncoding = 'ANSI';
 
     /**
      * Sheet index to read.
-     *
-     * @var int
      */
-    protected $sheetIndex = 0;
+    protected int $sheetIndex = 0;
 
     /**
      * Formats.
-     *
-     * @var array
      */
-    protected $formats = [
+    protected array $formats = [
         'h1' => [
             'font' => [
                 'bold' => true,
@@ -122,8 +126,7 @@ class Html extends BaseReader
         ], //    Italic
     ];
 
-    /** @var array */
-    protected $rowspan = [];
+    protected array $rowspan = [];
 
     /**
      * Create a new HTML Reader instance.
@@ -142,11 +145,12 @@ class Html extends BaseReader
         // Check if file exists
         try {
             $this->openFile($filename);
-        } catch (Exception $e) {
+        } catch (Exception) {
             return false;
         }
 
-        $beginning = $this->readBeginning();
+        $beginning = preg_replace(self::STARTS_WITH_BOM, '', $this->readBeginning()) ?? '';
+
         $startWithTag = self::startsWithTag($beginning);
         $containsTags = self::containsTags($beginning);
         $endsWithTag = self::endsWithTag($this->readEnding());
@@ -166,7 +170,8 @@ class Html extends BaseReader
     private function readEnding(): string
     {
         $meta = stream_get_meta_data($this->fileHandle);
-        $filename = $meta['uri'];
+        // Phpstan incorrectly flags following line for Php8.2-, corrected in 8.3
+        $filename = $meta['uri']; //@phpstan-ignore-line
 
         $size = (int) filesize($filename);
         if ($size === 0) {
@@ -185,12 +190,12 @@ class Html extends BaseReader
 
     private static function startsWithTag(string $data): bool
     {
-        return '<' === substr(trim($data), 0, 1);
+        return str_starts_with(trim($data), '<');
     }
 
     private static function endsWithTag(string $data): bool
     {
-        return '>' === substr(trim($data), -1, 1);
+        return str_ends_with(trim($data), '>');
     }
 
     private static function containsTags(string $data): bool
@@ -210,48 +215,13 @@ class Html extends BaseReader
         return $this->loadIntoExisting($filename, $spreadsheet);
     }
 
-    /**
-     * Set input encoding.
-     *
-     * @param string $inputEncoding Input encoding, eg: 'ANSI'
-     *
-     * @return $this
-     *
-     * @codeCoverageIgnore
-     *
-     * @deprecated no use is made of this property
-     */
-    public function setInputEncoding($inputEncoding)
-    {
-        $this->inputEncoding = $inputEncoding;
-
-        return $this;
-    }
-
-    /**
-     * Get input encoding.
-     *
-     * @return string
-     *
-     * @codeCoverageIgnore
-     *
-     * @deprecated no use is made of this property
-     */
-    public function getInputEncoding()
-    {
-        return $this->inputEncoding;
-    }
-
     //    Data Array used for testing only, should write to Spreadsheet object on completion of tests
 
-    /** @var array */
-    protected $dataArray = [];
+    protected array $dataArray = [];
 
-    /** @var int */
-    protected $tableLevel = 0;
+    protected int $tableLevel = 0;
 
-    /** @var array */
-    protected $nestedColumn = ['A'];
+    protected array $nestedColumn = ['A'];
 
     protected function setTableStartColumn(string $column): string
     {
@@ -278,20 +248,37 @@ class Html extends BaseReader
 
     /**
      * Flush cell.
-     *
-     * @param string $column
-     * @param int|string $row
-     * @param mixed $cellContent
      */
-    protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent): void
+    protected function flushCell(Worksheet $sheet, string $column, int|string $row, mixed &$cellContent, array $attributeArray): void
     {
         if (is_string($cellContent)) {
             //    Simple String content
             if (trim($cellContent) > '') {
                 //    Only actually write it if there's content in the string
                 //    Write to worksheet to be done here...
-                //    ... we return the cell so we can mess about with styles more easily
-                $sheet->setCellValue($column . $row, $cellContent);
+                //    ... we return the cell, so we can mess about with styles more easily
+
+                // Set cell value explicitly if there is data-type attribute
+                if (isset($attributeArray['data-type'])) {
+                    $datatype = $attributeArray['data-type'];
+                    if (in_array($datatype, [DataType::TYPE_STRING, DataType::TYPE_STRING2, DataType::TYPE_INLINE])) {
+                        //Prevent to Excel treat string with beginning equal sign or convert big numbers to scientific number
+                        if (str_starts_with($cellContent, '=')) {
+                            $sheet->getCell($column . $row)
+                                ->getStyle()
+                                ->setQuotePrefix(true);
+                        }
+                    }
+
+                    //catching the Exception and ignoring the invalid data types
+                    try {
+                        $sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
+                    } catch (SpreadsheetException) {
+                        $sheet->setCellValue($column . $row, $cellContent);
+                    }
+                } else {
+                    $sheet->setCellValue($column . $row, $cellContent);
+                }
                 $this->dataArray[$row][$column] = $cellContent;
             }
         } else {
@@ -305,7 +292,8 @@ class Html extends BaseReader
     private function processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child): void
     {
         $attributeArray = [];
-        foreach (($child->attributes ?? []) as $attribute) {
+        /** @var DOMAttr $attribute */
+        foreach ($child->attributes as $attribute) {
             $attributeArray[$attribute->name] = $attribute->value;
         }
 
@@ -324,7 +312,12 @@ class Html extends BaseReader
     {
         if ($child->nodeName === 'title') {
             $this->processDomElement($child, $sheet, $row, $column, $cellContent);
-            $sheet->setTitle($cellContent, true, true);
+
+            try {
+                $sheet->setTitle($cellContent, true, true);
+            } catch (SpreadsheetException) {
+                // leave default title if too long or illegal chars
+            }
             $cellContent = '';
         } else {
             $this->processDomElementSpanEtc($sheet, $row, $column, $cellContent, $child, $attributeArray);
@@ -340,6 +333,15 @@ class Html extends BaseReader
                 $sheet->getComment($column . $row)
                     ->getText()
                     ->createTextRun($child->textContent);
+                if (isset($attributeArray['dir']) && $attributeArray['dir'] === 'rtl') {
+                    $sheet->getComment($column . $row)->setTextboxDirection(Comment::TEXTBOX_DIRECTION_RTL);
+                }
+                if (isset($attributeArray['style'])) {
+                    $alignStyle = $attributeArray['style'];
+                    if (preg_match('/\\btext-align:\\s*(left|right|center|justify)\\b/', $alignStyle, $matches) === 1) {
+                        $sheet->getComment($column . $row)->setAlignment($matches[1]);
+                    }
+                }
             } else {
                 $this->processDomElement($child, $sheet, $row, $column, $cellContent);
             }
@@ -355,7 +357,7 @@ class Html extends BaseReader
     private function processDomElementHr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
     {
         if ($child->nodeName === 'hr') {
-            $this->flushCell($sheet, $column, $row, $cellContent);
+            $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
             ++$row;
             if (isset($this->formats[$child->nodeName])) {
                 $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
@@ -370,12 +372,12 @@ class Html extends BaseReader
     {
         if ($child->nodeName === 'br' || $child->nodeName === 'hr') {
             if ($this->tableLevel > 0) {
-                //    If we're inside a table, replace with a \n and set the cell to wrap
+                //    If we're inside a table, replace with a newline and set the cell to wrap
                 $cellContent .= "\n";
                 $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
             } else {
                 //    Otherwise flush our existing content and move the row cursor on
-                $this->flushCell($sheet, $column, $row, $cellContent);
+                $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
                 ++$row;
             }
         } else {
@@ -415,17 +417,17 @@ class Html extends BaseReader
     {
         if (in_array((string) $child->nodeName, self::H1_ETC, true)) {
             if ($this->tableLevel > 0) {
-                //    If we're inside a table, replace with a \n
+                //    If we're inside a table, replace with a newline
                 $cellContent .= $cellContent ? "\n" : '';
                 $sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
                 $this->processDomElement($child, $sheet, $row, $column, $cellContent);
             } else {
                 if ($cellContent > '') {
-                    $this->flushCell($sheet, $column, $row, $cellContent);
+                    $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
                     ++$row;
                 }
                 $this->processDomElement($child, $sheet, $row, $column, $cellContent);
-                $this->flushCell($sheet, $column, $row, $cellContent);
+                $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
 
                 if (isset($this->formats[$child->nodeName])) {
                     $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
@@ -443,16 +445,16 @@ class Html extends BaseReader
     {
         if ($child->nodeName === 'li') {
             if ($this->tableLevel > 0) {
-                //    If we're inside a table, replace with a \n
+                //    If we're inside a table, replace with a newline
                 $cellContent .= $cellContent ? "\n" : '';
                 $this->processDomElement($child, $sheet, $row, $column, $cellContent);
             } else {
                 if ($cellContent > '') {
-                    $this->flushCell($sheet, $column, $row, $cellContent);
+                    $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
                 }
                 ++$row;
                 $this->processDomElement($child, $sheet, $row, $column, $cellContent);
-                $this->flushCell($sheet, $column, $row, $cellContent);
+                $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
                 $column = 'A';
             }
         } else {
@@ -469,10 +471,13 @@ class Html extends BaseReader
         }
     }
 
+    private string $currentColumn = 'A';
+
     private function processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
     {
         if ($child->nodeName === 'table') {
-            $this->flushCell($sheet, $column, $row, $cellContent);
+            $this->currentColumn = 'A';
+            $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
             $column = $this->setTableStartColumn($column);
             if ($this->tableLevel > 1 && $row > 1) {
                 --$row;
@@ -491,7 +496,10 @@ class Html extends BaseReader
 
     private function processDomElementTr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
     {
-        if ($child->nodeName === 'tr') {
+        if ($child->nodeName === 'col') {
+            $this->applyInlineStyle($sheet, -1, $this->currentColumn, $attributeArray);
+            ++$this->currentColumn;
+        } elseif ($child->nodeName === 'tr') {
             $column = $this->getTableStartColumn();
             $cellContent = '';
             $this->processDomElement($child, $sheet, $row, $column, $cellContent);
@@ -574,7 +582,7 @@ class Html extends BaseReader
         // apply inline style
         $this->applyInlineStyle($sheet, $row, $column, $attributeArray);
 
-        $this->flushCell($sheet, $column, $row, $cellContent);
+        $this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
 
         $this->processDomElementBgcolor($sheet, $row, $column, $attributeArray);
         $this->processDomElementWidth($sheet, $column, $attributeArray);
@@ -619,13 +627,16 @@ class Html extends BaseReader
     {
         foreach ($element->childNodes as $child) {
             if ($child instanceof DOMText) {
-                $domText = (string) preg_replace('/\s+/u', ' ', trim($child->nodeValue ?? ''));
+                $domText = (string) preg_replace('/\s+/', ' ', trim($child->nodeValue ?? ''));
+                if ($domText === "\u{a0}") {
+                    $domText = '';
+                }
                 if (is_string($cellContent)) {
                     //    simply append the text if the cell content is a plain text string
                     $cellContent .= $domText;
                 }
                 //    but if we have a rich text run instead, we need to append it correctly
-                    //    TODO
+                //    TODO
             } elseif ($child instanceof DOMElement) {
                 $this->processDomElementBody($sheet, $row, $column, $cellContent, $child);
             }
@@ -634,12 +645,8 @@ class Html extends BaseReader
 
     /**
      * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
-     *
-     * @param string $filename
-     *
-     * @return Spreadsheet
      */
-    public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
+    public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
     {
         // Validate
         if (!$this->canRead($filename)) {
@@ -648,15 +655,11 @@ class Html extends BaseReader
 
         // Create a new DOM object
         $dom = new DOMDocument();
+
         // Reload the HTML file into the DOM object
         try {
-            $convert = $this->securityScanner->scanFile($filename);
-            $lowend = "\u{80}";
-            $highend = "\u{10ffff}";
-            $regexp = "/[$lowend-$highend]/u";
-            /** @var callable */
-            $callback = [self::class, 'replaceNonAscii'];
-            $convert = preg_replace_callback($regexp, $callback, $convert);
+            $convert = $this->getSecurityScannerOrThrow()->scanFile($filename);
+            $convert = self::replaceNonAsciiIfNeeded($convert);
             $loaded = ($convert === null) ? false : $dom->loadHTML($convert);
         } catch (Throwable $e) {
             $loaded = false;
@@ -664,33 +667,117 @@ class Html extends BaseReader
         if ($loaded === false) {
             throw new Exception('Failed to load ' . $filename . ' as a DOM Document', 0, $e ?? null);
         }
+        self::loadProperties($dom, $spreadsheet);
 
         return $this->loadDocument($dom, $spreadsheet);
     }
 
+    private static function loadProperties(DOMDocument $dom, Spreadsheet $spreadsheet): void
+    {
+        $properties = $spreadsheet->getProperties();
+        foreach ($dom->getElementsByTagName('meta') as $meta) {
+            $metaContent = (string) $meta->getAttribute('content');
+            if ($metaContent !== '') {
+                $metaName = (string) $meta->getAttribute('name');
+                switch ($metaName) {
+                    case 'author':
+                        $properties->setCreator($metaContent);
+
+                        break;
+                    case 'category':
+                        $properties->setCategory($metaContent);
+
+                        break;
+                    case 'company':
+                        $properties->setCompany($metaContent);
+
+                        break;
+                    case 'created':
+                        $properties->setCreated($metaContent);
+
+                        break;
+                    case 'description':
+                        $properties->setDescription($metaContent);
+
+                        break;
+                    case 'keywords':
+                        $properties->setKeywords($metaContent);
+
+                        break;
+                    case 'lastModifiedBy':
+                        $properties->setLastModifiedBy($metaContent);
+
+                        break;
+                    case 'manager':
+                        $properties->setManager($metaContent);
+
+                        break;
+                    case 'modified':
+                        $properties->setModified($metaContent);
+
+                        break;
+                    case 'subject':
+                        $properties->setSubject($metaContent);
+
+                        break;
+                    case 'title':
+                        $properties->setTitle($metaContent);
+
+                        break;
+                    case 'viewport':
+                        $properties->setViewport($metaContent);
+
+                        break;
+                    default:
+                        if (preg_match('/^custom[.](bool|date|float|int|string)[.](.+)$/', $metaName, $matches) === 1) {
+                            match ($matches[1]) {
+                                'bool' => $properties->setCustomProperty($matches[2], (bool) $metaContent, Properties::PROPERTY_TYPE_BOOLEAN),
+                                'float' => $properties->setCustomProperty($matches[2], (float) $metaContent, Properties::PROPERTY_TYPE_FLOAT),
+                                'int' => $properties->setCustomProperty($matches[2], (int) $metaContent, Properties::PROPERTY_TYPE_INTEGER),
+                                'date' => $properties->setCustomProperty($matches[2], $metaContent, Properties::PROPERTY_TYPE_DATE),
+                                // string
+                                default => $properties->setCustomProperty($matches[2], $metaContent, Properties::PROPERTY_TYPE_STRING),
+                            };
+                        }
+                }
+            }
+        }
+        if (!empty($dom->baseURI)) {
+            $properties->setHyperlinkBase($dom->baseURI);
+        }
+    }
+
     private static function replaceNonAscii(array $matches): string
     {
         return '&#' . mb_ord($matches[0], 'UTF-8') . ';';
     }
 
-    /**
-     * Spreadsheet from content.
-     *
-     * @param string $content
-     */
-    public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
+    private static function replaceNonAsciiIfNeeded(string $convert): ?string
     {
-        //    Create a new DOM object
-        $dom = new DOMDocument();
-        //    Reload the HTML file into the DOM object
-        try {
-            $convert = $this->securityScanner->scan($content);
+        if (preg_match(self::STARTS_WITH_BOM, $convert) !== 1 && preg_match(self::DECLARES_CHARSET, $convert) !== 1) {
             $lowend = "\u{80}";
             $highend = "\u{10ffff}";
             $regexp = "/[$lowend-$highend]/u";
-            /** @var callable */
+            /** @var callable $callback */
             $callback = [self::class, 'replaceNonAscii'];
             $convert = preg_replace_callback($regexp, $callback, $convert);
+        }
+
+        return $convert;
+    }
+
+    /**
+     * Spreadsheet from content.
+     */
+    public function loadFromString(string $content, ?Spreadsheet $spreadsheet = null): Spreadsheet
+    {
+        //    Create a new DOM object
+        $dom = new DOMDocument();
+
+        //    Reload the HTML file into the DOM object
+        try {
+            $convert = $this->getSecurityScannerOrThrow()->scan($content);
+            $convert = self::replaceNonAsciiIfNeeded($convert);
             $loaded = ($convert === null) ? false : $dom->loadHTML($convert);
         } catch (Throwable $e) {
             $loaded = false;
@@ -698,8 +785,10 @@ class Html extends BaseReader
         if ($loaded === false) {
             throw new Exception('Failed to load content as a DOM Document', 0, $e ?? null);
         }
+        $spreadsheet = $spreadsheet ?? new Spreadsheet();
+        self::loadProperties($dom, $spreadsheet);
 
-        return $this->loadDocument($dom, $spreadsheet ?? new Spreadsheet());
+        return $this->loadDocument($dom, $spreadsheet);
     }
 
     /**
@@ -727,10 +816,8 @@ class Html extends BaseReader
 
     /**
      * Get sheet index.
-     *
-     * @return int
      */
-    public function getSheetIndex()
+    public function getSheetIndex(): int
     {
         return $this->sheetIndex;
     }
@@ -742,7 +829,7 @@ class Html extends BaseReader
      *
      * @return $this
      */
-    public function setSheetIndex($sheetIndex)
+    public function setSheetIndex(int $sheetIndex): static
     {
         $this->sheetIndex = $sheetIndex;
 
@@ -758,18 +845,16 @@ class Html extends BaseReader
      *
      * TODO :
      * - Implement to other propertie, such as border
-     *
-     * @param int $row
-     * @param string $column
-     * @param array $attributeArray
      */
-    private function applyInlineStyle(Worksheet &$sheet, $row, $column, $attributeArray): void
+    private function applyInlineStyle(Worksheet &$sheet, int $row, string $column, array $attributeArray): void
     {
         if (!isset($attributeArray['style'])) {
             return;
         }
 
-        if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
+        if ($row <= 0 || $column === '') {
+            $cellStyle = new Style();
+        } elseif (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
             $columnTo = $column;
             for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
                 ++$columnTo;
@@ -901,16 +986,20 @@ class Html extends BaseReader
                     break;
 
                 case 'width':
-                    $sheet->getColumnDimension($column)->setWidth(
-                        (new CssDimension($styleValue ?? ''))->width()
-                    );
+                    if ($column !== '') {
+                        $sheet->getColumnDimension($column)->setWidth(
+                            (new CssDimension($styleValue ?? ''))->width()
+                        );
+                    }
 
                     break;
 
                 case 'height':
-                    $sheet->getRowDimension($row)->setRowHeight(
-                        (new CssDimension($styleValue ?? ''))->height()
-                    );
+                    if ($row > 0) {
+                        $sheet->getRowDimension($row)->setRowHeight(
+                            (new CssDimension($styleValue ?? ''))->height()
+                        );
+                    }
 
                     break;
 
@@ -933,26 +1022,18 @@ class Html extends BaseReader
 
     /**
      * Check if has #, so we can get clean hex.
-     *
-     * @param mixed $value
-     *
-     * @return null|string
      */
-    public function getStyleColor($value)
+    public function getStyleColor(?string $value): string
     {
         $value = (string) $value;
-        if (strpos($value ?? '', '#') === 0) {
+        if (str_starts_with($value, '#')) {
             return substr($value, 1);
         }
 
-        return \PhpOffice\PhpSpreadsheet\Helper\Html::colourNameLookup($value);
+        return HelperHtml::colourNameLookup($value);
     }
 
-    /**
-     * @param string    $column
-     * @param int       $row
-     */
-    private function insertImage(Worksheet $sheet, $column, $row, array $attributes): void
+    private function insertImage(Worksheet $sheet, string $column, int $row, array $attributes): void
     {
         if (!isset($attributes['src'])) {
             return;
@@ -1016,21 +1097,13 @@ class Html extends BaseReader
 
     /**
      * Map html border style to PhpSpreadsheet border style.
-     *
-     * @param  string $style
-     *
-     * @return null|string
      */
-    public function getBorderStyle($style)
+    public function getBorderStyle(string $style): ?string
     {
         return self::BORDER_MAPPINGS[$style] ?? null;
     }
 
-    /**
-     * @param string $styleValue
-     * @param string $type
-     */
-    private function setBorderStyle(Style $cellStyle, $styleValue, $type): void
+    private function setBorderStyle(Style $cellStyle, string $styleValue, string $type): void
     {
         if (trim($styleValue) === Border::BORDER_NONE) {
             $borderStyle = Border::BORDER_NONE;
@@ -1056,4 +1129,25 @@ class Html extends BaseReader
             ],
         ]);
     }
+
+    /**
+     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
+     */
+    public function listWorksheetInfo(string $filename): array
+    {
+        $info = [];
+        $spreadsheet = new Spreadsheet();
+        $this->loadIntoExisting($filename, $spreadsheet);
+        foreach ($spreadsheet->getAllSheets() as $sheet) {
+            $newEntry = ['worksheetName' => $sheet->getTitle()];
+            $newEntry['lastColumnLetter'] = $sheet->getHighestDataColumn();
+            $newEntry['lastColumnIndex'] = Coordinate::columnIndexFromString($sheet->getHighestDataColumn()) - 1;
+            $newEntry['totalRows'] = $sheet->getHighestDataRow();
+            $newEntry['totalColumns'] = $newEntry['lastColumnIndex'] + 1;
+            $info[] = $newEntry;
+        }
+        $spreadsheet->disconnectWorksheets();
+
+        return $info;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
index 9f68a7f..1fd12e5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
@@ -10,8 +10,6 @@ interface IReadFilter
      * @param string $columnAddress Column address (as a string value like "A", or "IV")
      * @param int $row Row number
      * @param string $worksheetName Optional worksheet name
-     *
-     * @return bool
      */
-    public function readCell($columnAddress, $row, $worksheetName = '');
+    public function readCell(string $columnAddress, int $row, string $worksheetName = ''): bool;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
index d4a997b..62c2103 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet\Reader;
 
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
 interface IReader
 {
     public const LOAD_WITH_CHARTS = 1;
@@ -9,10 +11,10 @@ interface IReader
     public const READ_DATA_ONLY = 2;
 
     public const SKIP_EMPTY_CELLS = 4;
+    public const IGNORE_EMPTY_CELLS = 4;
+
+    public const IGNORE_ROWS_WITH_NO_CELLS = 8;
 
-    /**
-     * IReader constructor.
-     */
     public function __construct();
 
     /**
@@ -22,107 +24,93 @@ interface IReader
 
     /**
      * Read data only?
-     *        If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
+     *        If this is true, then the Reader will only read data values for cells, it will not read any formatting
+     *           or structural information (like merges).
      *        If false (the default) it will read data and formatting.
-     *
-     * @return bool
      */
-    public function getReadDataOnly();
+    public function getReadDataOnly(): bool;
 
     /**
      * Set read data only
-     *        Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
+     *        Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting
+     *            or structural information (like merges).
      *        Set to false (the default) to advise the Reader to read both data and formatting for cells.
      *
-     * @param bool $readDataOnly
-     *
-     * @return IReader
+     * @return $this
      */
-    public function setReadDataOnly($readDataOnly);
+    public function setReadDataOnly(bool $readDataOnly): self;
 
     /**
      * Read empty cells?
      *        If this is true (the default), then the Reader will read data values for all cells, irrespective of value.
      *        If false it will not read data for cells containing a null value or an empty string.
-     *
-     * @return bool
      */
-    public function getReadEmptyCells();
+    public function getReadEmptyCells(): bool;
 
     /**
      * Set read empty cells
      *        Set to true (the default) to advise the Reader read data values for all cells, irrespective of value.
      *        Set to false to advise the Reader to ignore cells containing a null value or an empty string.
      *
-     * @param bool $readEmptyCells
-     *
-     * @return IReader
+     * @return $this
      */
-    public function setReadEmptyCells($readEmptyCells);
+    public function setReadEmptyCells(bool $readEmptyCells): self;
 
     /**
      * Read charts in workbook?
-     *        If this is true, then the Reader will include any charts that exist in the workbook.
-     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
-     *        If false (the default) it will ignore any charts defined in the workbook file.
-     *
-     * @return bool
+     *      If this is true, then the Reader will include any charts that exist in the workbook.
+     *         Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
+     *      If false (the default) it will ignore any charts defined in the workbook file.
      */
-    public function getIncludeCharts();
+    public function getIncludeCharts(): bool;
 
     /**
      * Set read charts in workbook
-     *        Set to true, to advise the Reader to include any charts that exist in the workbook.
-     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
-     *        Set to false (the default) to discard charts.
+     *     Set to true, to advise the Reader to include any charts that exist in the workbook.
+     *         Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
+     *     Set to false (the default) to discard charts.
      *
-     * @param bool $includeCharts
-     *
-     * @return IReader
+     * @return $this
      */
-    public function setIncludeCharts($includeCharts);
+    public function setIncludeCharts(bool $includeCharts): self;
 
     /**
      * Get which sheets to load
      * Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
      *        indicating that all worksheets in the workbook should be loaded.
-     *
-     * @return mixed
      */
-    public function getLoadSheetsOnly();
+    public function getLoadSheetsOnly(): ?array;
 
     /**
      * Set which sheets to load.
      *
-     * @param mixed $value
-     *        This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
-     *        If NULL, then it tells the Reader to read all worksheets in the workbook
+     * @param null|array|string $value This should be either an array of worksheet names to be loaded,
+     *          or a string containing a single worksheet name. If NULL, then it tells the Reader to
+     *          read all worksheets in the workbook
      *
-     * @return IReader
+     * @return $this
      */
-    public function setLoadSheetsOnly($value);
+    public function setLoadSheetsOnly(string|array|null $value): self;
 
     /**
      * Set all sheets to load
      *        Tells the Reader to load all worksheets from the workbook.
      *
-     * @return IReader
+     * @return $this
      */
-    public function setLoadAllSheets();
+    public function setLoadAllSheets(): self;
 
     /**
      * Read filter.
-     *
-     * @return IReadFilter
      */
-    public function getReadFilter();
+    public function getReadFilter(): IReadFilter;
 
     /**
      * Set read filter.
      *
-     * @return IReader
+     * @return $this
      */
-    public function setReadFilter(IReadFilter $readFilter);
+    public function setReadFilter(IReadFilter $readFilter): self;
 
     /**
      * Loads PhpSpreadsheet from file.
@@ -133,8 +121,6 @@ interface IReader
      *            self::READ_DATA_ONLY      Read only data, not style or structure information, from the file
      *            self::SKIP_EMPTY_CELLS    Don't read empty cells (cells that contain a null value,
      *                                      empty string, or a string containing only whitespace characters)
-     *
-     * @return \PhpOffice\PhpSpreadsheet\Spreadsheet
      */
-    public function load(string $filename, int $flags = 0);
+    public function load(string $filename, int $flags = 0): Spreadsheet;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
index 3e0d803..6f13027 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
@@ -6,8 +6,10 @@ use DOMAttr;
 use DOMDocument;
 use DOMElement;
 use DOMNode;
+use DOMText;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Helper\Dimension as HelperDimension;
 use PhpOffice\PhpSpreadsheet\Reader\Ods\AutoFilter;
 use PhpOffice\PhpSpreadsheet\Reader\Ods\DefinedNames;
 use PhpOffice\PhpSpreadsheet\Reader\Ods\FormulaTranslator;
@@ -56,20 +58,21 @@ class Ods extends BaseReader
                     $mimeType = $zip->getFromName($stat['name']);
                 } elseif ($zip->statName('META-INF/manifest.xml')) {
                     $xml = simplexml_load_string(
-                        $this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
+                        $this->getSecurityScannerOrThrow()->scan($zip->getFromName('META-INF/manifest.xml')),
                         'SimpleXMLElement',
                         Settings::getLibXmlLoaderOptions()
                     );
-                    $namespacesContent = $xml->getNamespaces(true);
-                    if (isset($namespacesContent['manifest'])) {
-                        $manifest = $xml->children($namespacesContent['manifest']);
-                        foreach ($manifest as $manifestDataSet) {
-                            /** @scrutinizer ignore-call */
-                            $manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
-                            if ($manifestAttributes && $manifestAttributes->{'full-path'} == '/') {
-                                $mimeType = (string) $manifestAttributes->{'media-type'};
+                    if ($xml !== false) {
+                        $namespacesContent = $xml->getNamespaces(true);
+                        if (isset($namespacesContent['manifest'])) {
+                            $manifest = $xml->children($namespacesContent['manifest']);
+                            foreach ($manifest as $manifestDataSet) {
+                                $manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
+                                if ($manifestAttributes && $manifestAttributes->{'full-path'} == '/') {
+                                    $mimeType = (string) $manifestAttributes->{'media-type'};
 
-                                break;
+                                    break;
+                                }
                             }
                         }
                     }
@@ -85,11 +88,9 @@ class Ods extends BaseReader
     /**
      * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
      *
-     * @param string $filename
-     *
      * @return string[]
      */
-    public function listWorksheetNames($filename)
+    public function listWorksheetNames(string $filename): array
     {
         File::assertFile($filename, self::INITIAL_FILE);
 
@@ -97,7 +98,7 @@ class Ods extends BaseReader
 
         $xml = new XMLReader();
         $xml->xml(
-            $this->securityScanner->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
+            $this->getSecurityScannerOrThrow()->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
             null,
             Settings::getLibXmlLoaderOptions()
         );
@@ -120,7 +121,10 @@ class Ods extends BaseReader
                 if ($xmlName == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
                     // Loop through each table:table node reading the table:name attribute for each worksheet name
                     do {
-                        $worksheetNames[] = $xml->getAttribute('table:name');
+                        $worksheetName = $xml->getAttribute('table:name');
+                        if (!empty($worksheetName)) {
+                            $worksheetNames[] = $worksheetName;
+                        }
                         $xml->next();
                     } while (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
                 }
@@ -132,12 +136,8 @@ class Ods extends BaseReader
 
     /**
      * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetInfo($filename)
+    public function listWorksheetInfo(string $filename): array
     {
         File::assertFile($filename, self::INITIAL_FILE);
 
@@ -145,7 +145,7 @@ class Ods extends BaseReader
 
         $xml = new XMLReader();
         $xml->xml(
-            $this->securityScanner->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
+            $this->getSecurityScannerOrThrow()->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE),
             null,
             Settings::getLibXmlLoaderOptions()
         );
@@ -234,6 +234,7 @@ class Ods extends BaseReader
     {
         // Create new Spreadsheet
         $spreadsheet = new Spreadsheet();
+        $spreadsheet->removeSheetByIndex(0);
 
         // Load into this instance
         return $this->loadIntoExisting($filename, $spreadsheet);
@@ -241,12 +242,8 @@ class Ods extends BaseReader
 
     /**
      * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
-     *
-     * @param string $filename
-     *
-     * @return Spreadsheet
      */
-    public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
+    public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
     {
         File::assertFile($filename, self::INITIAL_FILE);
 
@@ -256,7 +253,7 @@ class Ods extends BaseReader
         // Meta
 
         $xml = @simplexml_load_string(
-            $this->securityScanner->scan($zip->getFromName('meta.xml')),
+            $this->getSecurityScannerOrThrow()->scan($zip->getFromName('meta.xml')),
             'SimpleXMLElement',
             Settings::getLibXmlLoaderOptions()
         );
@@ -272,7 +269,7 @@ class Ods extends BaseReader
 
         $dom = new DOMDocument('1.01', 'UTF-8');
         $dom->loadXML(
-            $this->securityScanner->scan($zip->getFromName('styles.xml')),
+            $this->getSecurityScannerOrThrow()->scan($zip->getFromName('styles.xml')),
             Settings::getLibXmlLoaderOptions()
         );
 
@@ -282,24 +279,41 @@ class Ods extends BaseReader
 
         $dom = new DOMDocument('1.01', 'UTF-8');
         $dom->loadXML(
-            $this->securityScanner->scan($zip->getFromName(self::INITIAL_FILE)),
+            $this->getSecurityScannerOrThrow()->scan($zip->getFromName(self::INITIAL_FILE)),
             Settings::getLibXmlLoaderOptions()
         );
 
-        $officeNs = $dom->lookupNamespaceUri('office');
-        $tableNs = $dom->lookupNamespaceUri('table');
-        $textNs = $dom->lookupNamespaceUri('text');
-        $xlinkNs = $dom->lookupNamespaceUri('xlink');
+        $officeNs = (string) $dom->lookupNamespaceUri('office');
+        $tableNs = (string) $dom->lookupNamespaceUri('table');
+        $textNs = (string) $dom->lookupNamespaceUri('text');
+        $xlinkNs = (string) $dom->lookupNamespaceUri('xlink');
+        $styleNs = (string) $dom->lookupNamespaceUri('style');
 
         $pageSettings->readStyleCrossReferences($dom);
 
         $autoFilterReader = new AutoFilter($spreadsheet, $tableNs);
         $definedNameReader = new DefinedNames($spreadsheet, $tableNs);
+        $columnWidths = [];
+        $automaticStyle0 = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0);
+        $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style');
+        foreach ($automaticStyles as $automaticStyle) {
+            $styleName = $automaticStyle->getAttributeNS($styleNs, 'name');
+            $styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family');
+            if ($styleFamily === 'table-column') {
+                $tcprops = $automaticStyle->getElementsByTagNameNS($styleNs, 'table-column-properties');
+                if ($tcprops !== null) {
+                    $tcprop = $tcprops->item(0);
+                    if ($tcprop !== null) {
+                        $columnWidth = $tcprop->getAttributeNs($styleNs, 'column-width');
+                        $columnWidths[$styleName] = $columnWidth;
+                    }
+                }
+            }
+        }
 
         // Content
-        $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body')
-            ->item(0)
-            ->getElementsByTagNameNS($officeNs, 'spreadsheet');
+        $item0 = $dom->getElementsByTagNameNS($officeNs, 'body')->item(0);
+        $spreadsheets = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($officeNs, 'spreadsheet');
 
         foreach ($spreadsheets as $workbookData) {
             /** @var DOMElement $workbookData */
@@ -322,9 +336,7 @@ class Ods extends BaseReader
                 $worksheetStyleName = $worksheetDataSet->getAttributeNS($tableNs, 'style-name');
 
                 // Create sheet
-                if ($worksheetID > 0) {
-                    $spreadsheet->createSheet(); // First sheet is added by default
-                }
+                $spreadsheet->createSheet();
                 $spreadsheet->setActiveSheetIndex($worksheetID);
 
                 if ($worksheetName || is_numeric($worksheetName)) {
@@ -336,6 +348,7 @@ class Ods extends BaseReader
 
                 // Go through every child of table element
                 $rowID = 1;
+                $tableColumnIndex = 1;
                 foreach ($worksheetDataSet->childNodes as $childNode) {
                     /** @var DOMElement $childNode */
 
@@ -347,7 +360,7 @@ class Ods extends BaseReader
                     $key = $childNode->nodeName;
 
                     // Remove ns from node name
-                    if (strpos($key, ':') !== false) {
+                    if (str_contains($key, ':')) {
                         $keyChunks = explode(':', $key);
                         $key = array_pop($keyChunks);
                     }
@@ -362,6 +375,26 @@ class Ods extends BaseReader
 //                                $rowData = $cellData;
 //                                break;
 //                            }
+                            break;
+                        case 'table-column':
+                            if ($childNode->hasAttributeNS($tableNs, 'number-columns-repeated')) {
+                                $rowRepeats = (int) $childNode->getAttributeNS($tableNs, 'number-columns-repeated');
+                            } else {
+                                $rowRepeats = 1;
+                            }
+                            $tableStyleName = $childNode->getAttributeNS($tableNs, 'style-name');
+                            if (isset($columnWidths[$tableStyleName])) {
+                                $columnWidth = new HelperDimension($columnWidths[$tableStyleName]);
+                                $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex);
+                                for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0; --$rowRepeats2) {
+                                    $spreadsheet->getActiveSheet()
+                                        ->getColumnDimension($tableColumnString)
+                                        ->setWidth($columnWidth->toUnit('cm'), 'cm');
+                                    ++$tableColumnString;
+                                }
+                            }
+                            $tableColumnIndex += $rowRepeats;
+
                             break;
                         case 'table-row':
                             if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) {
@@ -371,8 +404,11 @@ class Ods extends BaseReader
                             }
 
                             $columnID = 'A';
-                            /** @var DOMElement $cellData */
+                            /** @var DOMElement|DOMText $cellData */
                             foreach ($childNode->childNodes as $cellData) {
+                                if ($cellData instanceof DOMText) {
+                                    continue; // should just be whitespace
+                                }
                                 if ($this->getReadFilter() !== null) {
                                     if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
                                         if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
@@ -402,16 +438,27 @@ class Ods extends BaseReader
                                 // Annotations
                                 $annotation = $cellData->getElementsByTagNameNS($officeNs, 'annotation');
 
-                                if ($annotation->length > 0) {
+                                if ($annotation->length > 0 && $annotation->item(0) !== null) {
                                     $textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p');
+                                    $textNodeLength = $textNode->length;
+                                    $newLineOwed = false;
+                                    for ($textNodeIndex = 0; $textNodeIndex < $textNodeLength; ++$textNodeIndex) {
+                                        $textNodeItem = $textNode->item($textNodeIndex);
+                                        if ($textNodeItem !== null) {
+                                            $text = $this->scanElementForText($textNodeItem);
+                                            if ($newLineOwed) {
+                                                $spreadsheet->getActiveSheet()
+                                                    ->getComment($columnID . $rowID)
+                                                    ->getText()
+                                                    ->createText("\n");
+                                            }
+                                            $newLineOwed = true;
 
-                                    if ($textNode->length > 0) {
-                                        $text = $this->scanElementForText($textNode->item(0));
-
-                                        $spreadsheet->getActiveSheet()
-                                            ->getComment($columnID . $rowID)
-                                            ->setText($this->parseRichText($text));
-//                                                                    ->setAuthor( $author )
+                                            $spreadsheet->getActiveSheet()
+                                                ->getComment($columnID . $rowID)
+                                                ->getText()
+                                                ->createText($this->parseRichText($text));
+                                        }
                                     }
                                 }
 
@@ -452,7 +499,7 @@ class Ods extends BaseReader
 
                                             foreach ($paragraphs as $paragraph) {
                                                 $link = $paragraph->getElementsByTagNameNS($textNs, 'a');
-                                                if ($link->length > 0) {
+                                                if ($link->length > 0 && $link->item(0) !== null) {
                                                     $hyperlink = $link->item(0)->getAttributeNS($xlinkNs, 'href');
                                                 }
                                             }
@@ -460,7 +507,7 @@ class Ods extends BaseReader
                                             break;
                                         case 'boolean':
                                             $type = DataType::TYPE_BOOL;
-                                            $dataValue = ($allCellDataText == 'TRUE') ? true : false;
+                                            $dataValue = ($cellData->getAttributeNS($officeNs, 'boolean-value') === 'true') ? true : false;
 
                                             break;
                                         case 'percentage':
@@ -481,7 +528,7 @@ class Ods extends BaseReader
                                             if (floor($dataValue) == $dataValue) {
                                                 $dataValue = (int) $dataValue;
                                             }
-                                            $formatting = NumberFormat::FORMAT_CURRENCY_USD_SIMPLE;
+                                            $formatting = NumberFormat::FORMAT_CURRENCY_USD_INTEGER;
 
                                             break;
                                         case 'float':
@@ -516,7 +563,7 @@ class Ods extends BaseReader
 
                                             $dataValue = Date::PHPToExcel(
                                                 strtotime(
-                                                    '01-01-1970 ' . implode(':', /** @scrutinizer ignore-type */ sscanf($timeValue, 'PT%dH%dM%dS') ?? [])
+                                                    '01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS') ?? [])
                                                 )
                                             );
                                             $formatting = NumberFormat::FORMAT_DATE_TIME4;
@@ -563,7 +610,7 @@ class Ods extends BaseReader
                                                 }
 
                                                 if ($hasCalculatedValue) {
-                                                    $cell->setCalculatedValue($dataValue);
+                                                    $cell->setCalculatedValue($dataValue, $type === DataType::TYPE_NUMERIC);
                                                 }
 
                                                 // Set other properties
@@ -580,6 +627,9 @@ class Ods extends BaseReader
                                                 }
 
                                                 if ($hyperlink !== null) {
+                                                    if ($hyperlink[0] === '#') {
+                                                        $hyperlink = 'sheet://' . substr($hyperlink, 1);
+                                                    }
                                                     $cell->getHyperlink()
                                                         ->setUrl($hyperlink);
                                                 }
@@ -620,17 +670,19 @@ class Ods extends BaseReader
     {
         $dom = new DOMDocument('1.01', 'UTF-8');
         $dom->loadXML(
-            $this->securityScanner->scan($zip->getFromName('settings.xml')),
+            $this->getSecurityScannerOrThrow()->scan($zip->getFromName('settings.xml')),
             Settings::getLibXmlLoaderOptions()
         );
         //$xlinkNs = $dom->lookupNamespaceUri('xlink');
-        $configNs = $dom->lookupNamespaceUri('config');
+        $configNs = (string) $dom->lookupNamespaceUri('config');
         //$oooNs = $dom->lookupNamespaceUri('ooo');
-        $officeNs = $dom->lookupNamespaceUri('office');
+        $officeNs = (string) $dom->lookupNamespaceUri('office');
         $settings = $dom->getElementsByTagNameNS($officeNs, 'settings')
             ->item(0);
-        $this->lookForActiveSheet($settings, $spreadsheet, $configNs);
-        $this->lookForSelectedCells($settings, $spreadsheet, $configNs);
+        if ($settings !== null) {
+            $this->lookForActiveSheet($settings, $spreadsheet, $configNs);
+            $this->lookForSelectedCells($settings, $spreadsheet, $configNs);
+        }
     }
 
     private function lookForActiveSheet(DOMElement $settings, Spreadsheet $spreadsheet, string $configNs): void
@@ -640,7 +692,7 @@ class Ods extends BaseReader
             if ($t->getAttributeNs($configNs, 'name') === 'ActiveTable') {
                 try {
                     $spreadsheet->setActiveSheetIndexByName($t->nodeValue ?? '');
-                } catch (Throwable $e) {
+                } catch (Throwable) {
                     // do nothing
                 }
 
@@ -686,23 +738,23 @@ class Ods extends BaseReader
 
     /**
      * Recursively scan element.
-     *
-     * @return string
      */
-    protected function scanElementForText(DOMNode $element)
+    protected function scanElementForText(DOMNode $element): string
     {
         $str = '';
         foreach ($element->childNodes as $child) {
             /** @var DOMNode $child */
             if ($child->nodeType == XML_TEXT_NODE) {
                 $str .= $child->nodeValue;
+            } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:line-break') {
+                $str .= "\n";
             } elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') {
                 // It's a space
 
                 // Multiple spaces?
-                /** @var DOMAttr $cAttr */
-                /** @scrutinizer ignore-call */
-                $cAttr = $child->attributes->getNamedItem('c');
+                $attributes = $child->attributes;
+                /** @var ?DOMAttr $cAttr */
+                $cAttr = ($attributes === null) ? null : $attributes->getNamedItem('c');
                 $multiplier = self::getMultiplier($cAttr);
                 $str .= str_repeat(' ', $multiplier);
             }
@@ -726,12 +778,7 @@ class Ods extends BaseReader
         return $multiplier;
     }
 
-    /**
-     * @param string $is
-     *
-     * @return RichText
-     */
-    private function parseRichText($is)
+    private function parseRichText(string $is): RichText
     {
         $value = new RichText();
         $value->createText($is);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php
index b06691f..c280c4e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php
@@ -7,15 +7,9 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
 
 abstract class BaseLoader
 {
-    /**
-     * @var Spreadsheet
-     */
-    protected $spreadsheet;
+    protected Spreadsheet $spreadsheet;
 
-    /**
-     * @var string
-     */
-    protected $tableNs;
+    protected string $tableNs;
 
     public function __construct(Spreadsheet $spreadsheet, string $tableNs)
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php
index e0ab890..a99e3ea 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php
@@ -25,6 +25,7 @@ class DefinedNames extends BaseLoader
             $baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
             $range = $definedNameElement->getAttributeNS($this->tableNs, 'cell-range-address');
 
+            /** @var non-empty-string $baseAddress */
             $baseAddress = FormulaTranslator::convertToExcelAddressValue($baseAddress);
             $range = FormulaTranslator::convertToExcelAddressValue($range);
 
@@ -43,6 +44,7 @@ class DefinedNames extends BaseLoader
             $baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
             $expression = $definedNameElement->getAttributeNS($this->tableNs, 'expression');
 
+            /** @var non-empty-string $baseAddress */
             $baseAddress = FormulaTranslator::convertToExcelAddressValue($baseAddress);
             $expression = substr($expression, strpos($expression, ':=') + 1);
             $expression = FormulaTranslator::convertToExcelFormulaValue($expression);
@@ -53,6 +55,8 @@ class DefinedNames extends BaseLoader
 
     /**
      * Assess scope and store the Defined Name.
+     *
+     * @param non-empty-string $baseAddress
      */
     private function addDefinedName(string $baseAddress, string $definedName, string $value): void
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php
index 4d2fd99..fd35de5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/PageSettings.php
@@ -8,42 +8,30 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class PageSettings
 {
-    /**
-     * @var string
-     */
-    private $officeNs;
+    private string $officeNs = '';
 
-    /**
-     * @var string
-     */
-    private $stylesNs;
+    private string $stylesNs = '';
 
-    /**
-     * @var string
-     */
-    private $stylesFo;
+    private string $stylesFo = '';
 
-    /**
-     * @var string
-     */
-    private $tableNs;
+    private string $tableNs = '';
 
     /**
      * @var string[]
      */
-    private $tableStylesCrossReference = [];
+    private array $tableStylesCrossReference = [];
 
-    private $pageLayoutStyles = [];
+    private array $pageLayoutStyles = [];
 
     /**
      * @var string[]
      */
-    private $masterStylesCrossReference = [];
+    private array $masterStylesCrossReference = [];
 
     /**
      * @var string[]
      */
-    private $masterPrintStylesCrossReference = [];
+    private array $masterPrintStylesCrossReference = [];
 
     public function __construct(DOMDocument $styleDom)
     {
@@ -54,36 +42,35 @@ class PageSettings
 
     private function setDomNameSpaces(DOMDocument $styleDom): void
     {
-        $this->officeNs = $styleDom->lookupNamespaceUri('office');
-        $this->stylesNs = $styleDom->lookupNamespaceUri('style');
-        $this->stylesFo = $styleDom->lookupNamespaceUri('fo');
-        $this->tableNs = $styleDom->lookupNamespaceUri('table');
+        $this->officeNs = (string) $styleDom->lookupNamespaceUri('office');
+        $this->stylesNs = (string) $styleDom->lookupNamespaceUri('style');
+        $this->stylesFo = (string) $styleDom->lookupNamespaceUri('fo');
+        $this->tableNs = (string) $styleDom->lookupNamespaceUri('table');
     }
 
     private function readPageSettingStyles(DOMDocument $styleDom): void
     {
-        $styles = $styleDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')
-            ->item(0)
-            ->getElementsByTagNameNS($this->stylesNs, 'page-layout');
+        $item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
+        $styles = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'page-layout');
 
         foreach ($styles as $styleSet) {
             $styleName = $styleSet->getAttributeNS($this->stylesNs, 'name');
-            $pageLayoutProperties = $styleSet->getElementsByTagNameNS($this->stylesNs, 'page-layout-properties')[0];
-            $styleOrientation = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-orientation');
-            $styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to');
-            $stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order');
-            $centered = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'table-centering');
+            $pageLayoutProperties = $styleSet->getElementsByTagNameNS($this->stylesNs, 'page-layout-properties')->item(0);
+            $styleOrientation = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'print-orientation');
+            $styleScale = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'scale-to');
+            $stylePrintOrder = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'print-page-order');
+            $centered = $pageLayoutProperties?->getAttributeNS($this->stylesNs, 'table-centering');
 
-            $marginLeft = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-left');
-            $marginRight = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-right');
-            $marginTop = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-top');
-            $marginBottom = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-bottom');
-            $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')[0];
-            $headerProperties = $header->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0];
-            $marginHeader = isset($headerProperties) ? $headerProperties->getAttributeNS($this->stylesFo, 'min-height') : null;
-            $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')[0];
-            $footerProperties = $footer->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0];
-            $marginFooter = isset($footerProperties) ? $footerProperties->getAttributeNS($this->stylesFo, 'min-height') : null;
+            $marginLeft = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-left');
+            $marginRight = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-right');
+            $marginTop = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-top');
+            $marginBottom = $pageLayoutProperties?->getAttributeNS($this->stylesFo, 'margin-bottom');
+            $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')->item(0);
+            $headerProperties = $header?->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')?->item(0);
+            $marginHeader = $headerProperties?->getAttributeNS($this->stylesFo, 'min-height');
+            $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')->item(0);
+            $footerProperties = $footer?->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')?->item(0);
+            $marginFooter = $footerProperties?->getAttributeNS($this->stylesFo, 'min-height');
 
             $this->pageLayoutStyles[$styleName] = (object) [
                 'orientation' => $styleOrientation ?: PageSetup::ORIENTATION_DEFAULT,
@@ -92,21 +79,20 @@ class PageSettings
                 'horizontalCentered' => $centered === 'horizontal' || $centered === 'both',
                 'verticalCentered' => $centered === 'vertical' || $centered === 'both',
                 // margin size is already stored in inches, so no UOM conversion is required
-                'marginLeft' => (float) $marginLeft ?? 0.7,
-                'marginRight' => (float) $marginRight ?? 0.7,
-                'marginTop' => (float) $marginTop ?? 0.3,
-                'marginBottom' => (float) $marginBottom ?? 0.3,
-                'marginHeader' => (float) $marginHeader ?? 0.45,
-                'marginFooter' => (float) $marginFooter ?? 0.45,
+                'marginLeft' => (float) ($marginLeft ?? 0.7),
+                'marginRight' => (float) ($marginRight ?? 0.7),
+                'marginTop' => (float) ($marginTop ?? 0.3),
+                'marginBottom' => (float) ($marginBottom ?? 0.3),
+                'marginHeader' => (float) ($marginHeader ?? 0.45),
+                'marginFooter' => (float) ($marginFooter ?? 0.45),
             ];
         }
     }
 
     private function readStyleMasterLookup(DOMDocument $styleDom): void
     {
-        $styleMasterLookup = $styleDom->getElementsByTagNameNS($this->officeNs, 'master-styles')
-            ->item(0)
-            ->getElementsByTagNameNS($this->stylesNs, 'master-page');
+        $item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'master-styles')->item(0);
+        $styleMasterLookup = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'master-page');
 
         foreach ($styleMasterLookup as $styleMasterSet) {
             $styleMasterName = $styleMasterSet->getAttributeNS($this->stylesNs, 'name');
@@ -117,9 +103,8 @@ class PageSettings
 
     public function readStyleCrossReferences(DOMDocument $contentDom): void
     {
-        $styleXReferences = $contentDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')
-            ->item(0)
-            ->getElementsByTagNameNS($this->stylesNs, 'style');
+        $item0 = $contentDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
+        $styleXReferences = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'style');
 
         foreach ($styleXReferences as $styleXreferenceSet) {
             $styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name');
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
index 1547a6b..a5f0c79 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
@@ -8,27 +8,25 @@ use SimpleXMLElement;
 
 class Properties
 {
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     public function __construct(Spreadsheet $spreadsheet)
     {
         $this->spreadsheet = $spreadsheet;
     }
 
-    public function load(SimpleXMLElement $xml, $namespacesMeta): void
+    public function load(SimpleXMLElement $xml, array $namespacesMeta): void
     {
         $docProps = $this->spreadsheet->getProperties();
         $officeProperty = $xml->children($namespacesMeta['office']);
         foreach ($officeProperty as $officePropertyData) {
             if (isset($namespacesMeta['dc'])) {
-                /** @scrutinizer ignore-call */
                 $officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
                 $this->setCoreProperties($docProps, $officePropertiesDC);
             }
 
             $officePropertyMeta = null;
             if (isset($namespacesMeta['dc'])) {
-                /** @scrutinizer ignore-call */
                 $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
             }
             $officePropertyMeta = $officePropertyMeta ?? [];
@@ -69,9 +67,9 @@ class Properties
     }
 
     private function setMetaProperties(
-        $namespacesMeta,
+        array $namespacesMeta,
         SimpleXMLElement $propertyValue,
-        $propertyName,
+        string $propertyName,
         DocumentProperties $docProps
     ): void {
         $propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
@@ -90,13 +88,20 @@ class Properties
 
                 break;
             case 'user-defined':
-                $this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
+                $name2 = (string) ($propertyValueAttributes['name'] ?? '');
+                if ($name2 === 'Company') {
+                    $docProps->setCompany($propertyValue);
+                } elseif ($name2 === 'category') {
+                    $docProps->setCategory($propertyValue);
+                } else {
+                    $this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
+                }
 
                 break;
         }
     }
 
-    private function setUserDefinedProperty($propertyValueAttributes, $propertyValue, DocumentProperties $docProps): void
+    private function setUserDefinedProperty(iterable $propertyValueAttributes, string $propertyValue, DocumentProperties $docProps): void
     {
         $propertyValueName = '';
         $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
index f4cf2db..3946ed0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
@@ -6,90 +6,21 @@ use PhpOffice\PhpSpreadsheet\Reader;
 
 class XmlScanner
 {
-    /**
-     * String used to identify risky xml elements.
-     *
-     * @var string
-     */
-    private $pattern;
+    private string $pattern;
 
+    /** @var ?callable */
     private $callback;
 
-    private static $libxmlDisableEntityLoaderValue;
-
-    /**
-     * @var bool
-     */
-    private static $shutdownRegistered = false;
-
-    public function __construct($pattern = '<!DOCTYPE')
+    public function __construct(string $pattern = '<!DOCTYPE')
     {
         $this->pattern = $pattern;
-
-        $this->disableEntityLoaderCheck();
-
-        // A fatal error will bypass the destructor, so we register a shutdown here
-        if (!self::$shutdownRegistered) {
-            self::$shutdownRegistered = true;
-            register_shutdown_function([__CLASS__, 'shutdown']);
-        }
     }
 
-    public static function getInstance(Reader\IReader $reader)
+    public static function getInstance(Reader\IReader $reader): self
     {
-        switch (true) {
-            case $reader instanceof Reader\Html:
-                return new self('<!ENTITY');
-            case $reader instanceof Reader\Xlsx:
-            case $reader instanceof Reader\Xml:
-            case $reader instanceof Reader\Ods:
-            case $reader instanceof Reader\Gnumeric:
-                return new self('<!DOCTYPE');
-            default:
-                return new self('<!DOCTYPE');
-        }
-    }
+        $pattern = ($reader instanceof Reader\Html) ? '<!ENTITY' : '<!DOCTYPE';
 
-    public static function threadSafeLibxmlDisableEntityLoaderAvailability()
-    {
-        if (PHP_MAJOR_VERSION === 7) {
-            switch (PHP_MINOR_VERSION) {
-                case 2:
-                    return PHP_RELEASE_VERSION >= 1;
-                case 1:
-                    return PHP_RELEASE_VERSION >= 13;
-                case 0:
-                    return PHP_RELEASE_VERSION >= 27;
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    private function disableEntityLoaderCheck(): void
-    {
-        if (\PHP_VERSION_ID < 80000) {
-            $libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
-
-            if (self::$libxmlDisableEntityLoaderValue === null) {
-                self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
-            }
-        }
-    }
-
-    public static function shutdown(): void
-    {
-        if (self::$libxmlDisableEntityLoaderValue !== null && \PHP_VERSION_ID < 80000) {
-            libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
-            self::$libxmlDisableEntityLoaderValue = null;
-        }
-    }
-
-    public function __destruct()
-    {
-        self::shutdown();
+        return new self($pattern);
     }
 
     public function setAdditionalCallback(callable $callback): void
@@ -97,28 +28,18 @@ class XmlScanner
         $this->callback = $callback;
     }
 
-    /** @param mixed $arg */
-    private static function forceString($arg): string
+    private static function forceString(mixed $arg): string
     {
         return is_string($arg) ? $arg : '';
     }
 
-    /**
-     * @param string $xml
-     *
-     * @return string
-     */
-    private function toUtf8($xml)
+    private function toUtf8(string $xml): string
     {
-        $pattern = '/encoding="(.*?)"/';
-        $result = preg_match($pattern, $xml, $matches);
-        $charset = strtoupper($result ? $matches[1] : 'UTF-8');
-
+        $charset = $this->findCharSet($xml);
         if ($charset !== 'UTF-8') {
             $xml = self::forceString(mb_convert_encoding($xml, 'UTF-8', $charset));
 
-            $result = preg_match($pattern, $xml, $matches);
-            $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+            $charset = $this->findCharSet($xml);
             if ($charset !== 'UTF-8') {
                 throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
             }
@@ -127,30 +48,41 @@ class XmlScanner
         return $xml;
     }
 
+    private function findCharSet(string $xml): string
+    {
+        $patterns = [
+            '/encoding="([^"]*]?)"/',
+            "/encoding='([^']*?)'/",
+        ];
+
+        foreach ($patterns as $pattern) {
+            if (preg_match($pattern, $xml, $matches)) {
+                return strtoupper($matches[1]);
+            }
+        }
+
+        return 'UTF-8';
+    }
+
     /**
      * Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks.
      *
      * @param false|string $xml
-     *
-     * @return string
      */
-    public function scan($xml)
+    public function scan($xml): string
     {
-        if (!is_string($xml)) {
-            $xml = '';
-        }
-        $this->disableEntityLoaderCheck();
+        $xml = "$xml";
 
         $xml = $this->toUtf8($xml);
 
         // Don't rely purely on libxml_disable_entity_loader()
-        $pattern = '/\\0?' . implode('\\0?', /** @scrutinizer ignore-type */ str_split($this->pattern)) . '\\0?/';
+        $pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
 
         if (preg_match($pattern, $xml)) {
             throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
         }
 
-        if ($this->callback !== null && is_callable($this->callback)) {
+        if ($this->callback !== null) {
             $xml = call_user_func($this->callback, $xml);
         }
 
@@ -159,12 +91,8 @@ class XmlScanner
 
     /**
      * Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks.
-     *
-     * @param string $filestream
-     *
-     * @return string
      */
-    public function scanFile($filestream)
+    public function scanFile(string $filestream): string
     {
         return $this->scan(file_get_contents($filestream));
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
index 38eddbc..2a0b2fc 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
@@ -5,54 +5,39 @@ namespace PhpOffice\PhpSpreadsheet\Reader;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
+use PhpOffice\PhpSpreadsheet\ReferenceHelper;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class Slk extends BaseReader
 {
-    /**
-     * Input encoding.
-     *
-     * @var string
-     */
-    private $inputEncoding = 'ANSI';
-
     /**
      * Sheet index to read.
-     *
-     * @var int
      */
-    private $sheetIndex = 0;
+    private int $sheetIndex = 0;
 
     /**
      * Formats.
-     *
-     * @var array
      */
-    private $formats = [];
+    private array $formats = [];
 
     /**
      * Format Count.
-     *
-     * @var int
      */
-    private $format = 0;
+    private int $format = 0;
 
     /**
      * Fonts.
-     *
-     * @var array
      */
-    private $fonts = [];
+    private array $fonts = [];
 
     /**
      * Font Count.
-     *
-     * @var int
      */
-    private $fontcount = 0;
+    private int $fontcount = 0;
 
     /**
      * Create a new SYLK Reader instance.
@@ -69,7 +54,7 @@ class Slk extends BaseReader
     {
         try {
             $this->openFile($filename);
-        } catch (ReaderException $e) {
+        } catch (ReaderException) {
             return false;
         }
 
@@ -82,7 +67,7 @@ class Slk extends BaseReader
 
         // Analyze first line looking for ID; signature
         $lines = explode("\n", $data);
-        $hasId = substr($lines[0], 0, 4) === 'ID;P';
+        $hasId = str_starts_with($lines[0], 'ID;P');
 
         fclose($this->fileHandle);
 
@@ -97,46 +82,10 @@ class Slk extends BaseReader
         $this->openFile($filename);
     }
 
-    /**
-     * Set input encoding.
-     *
-     * @deprecated no use is made of this property
-     *
-     * @param string $inputEncoding Input encoding, eg: 'ANSI'
-     *
-     * @return $this
-     *
-     * @codeCoverageIgnore
-     */
-    public function setInputEncoding($inputEncoding)
-    {
-        $this->inputEncoding = $inputEncoding;
-
-        return $this;
-    }
-
-    /**
-     * Get input encoding.
-     *
-     * @deprecated no use is made of this property
-     *
-     * @return string
-     *
-     * @codeCoverageIgnore
-     */
-    public function getInputEncoding()
-    {
-        return $this->inputEncoding;
-    }
-
     /**
      * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetInfo($filename)
+    public function listWorksheetInfo(string $filename): array
     {
         // Open file
         $this->canReadOrBust($filename);
@@ -168,7 +117,7 @@ class Slk extends BaseReader
 
                             break;
                         case 'Y':
-                            $rowIndex = substr($rowDatum, 1);
+                            $rowIndex = (int) substr($rowDatum, 1);
 
                             break;
                     }
@@ -270,21 +219,23 @@ class Slk extends BaseReader
     {
         //    Read cell value data
         $hasCalculatedValue = false;
+        $tryNumeric = false;
         $cellDataFormula = $cellData = '';
+        $sharedColumn = $sharedRow = -1;
+        $sharedFormula = false;
         foreach ($rowData as $rowDatum) {
             switch ($rowDatum[0]) {
-                case 'C':
                 case 'X':
                     $column = substr($rowDatum, 1);
 
                     break;
-                case 'R':
                 case 'Y':
                     $row = substr($rowDatum, 1);
 
                     break;
                 case 'K':
                     $cellData = substr($rowDatum, 1);
+                    $tryNumeric = is_numeric($cellData);
 
                     break;
                 case 'E':
@@ -299,23 +250,52 @@ class Slk extends BaseReader
                         ->getText()
                         ->createText($comment);
 
+                    break;
+                case 'C':
+                    $sharedColumn = (int) substr($rowDatum, 1);
+
+                    break;
+                case 'R':
+                    $sharedRow = (int) substr($rowDatum, 1);
+
+                    break;
+                case 'S':
+                    $sharedFormula = true;
+
                     break;
             }
         }
+        if ($sharedFormula === true && $sharedRow >= 0 && $sharedColumn >= 0) {
+            $thisCoordinate = Coordinate::stringFromColumnIndex((int) $column) . $row;
+            $sharedCoordinate = Coordinate::stringFromColumnIndex($sharedColumn) . $sharedRow;
+            /** @var string */
+            $formula = $spreadsheet->getActiveSheet()->getCell($sharedCoordinate)->getValue();
+            $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setValue($formula);
+            $referenceHelper = ReferenceHelper::getInstance();
+            $newFormula = $referenceHelper->updateFormulaReferences($formula, 'A1', (int) $column - $sharedColumn, (int) $row - $sharedRow, '', true, false);
+            $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setValue($newFormula);
+            //$calc = $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->getCalculatedValue();
+            //$spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setCalculatedValue($calc);
+            $cellData = Calculation::unwrapResult($cellData);
+            $spreadsheet->getActiveSheet()->getCell($thisCoordinate)->setCalculatedValue($cellData, $tryNumeric);
+
+            return;
+        }
         $columnLetter = Coordinate::stringFromColumnIndex((int) $column);
+        /** @var string */
         $cellData = Calculation::unwrapResult($cellData);
 
         // Set cell value
-        $this->processCFinal($spreadsheet, $hasCalculatedValue, $cellDataFormula, $cellData, "$columnLetter$row");
+        $this->processCFinal($spreadsheet, $hasCalculatedValue, $cellDataFormula, $cellData, "$columnLetter$row", $tryNumeric);
     }
 
-    private function processCFinal(Spreadsheet &$spreadsheet, bool $hasCalculatedValue, string $cellDataFormula, string $cellData, string $coordinate): void
+    private function processCFinal(Spreadsheet &$spreadsheet, bool $hasCalculatedValue, string $cellDataFormula, string $cellData, string $coordinate, bool $tryNumeric): void
     {
         // Set cell value
         $spreadsheet->getActiveSheet()->getCell($coordinate)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
         if ($hasCalculatedValue) {
             $cellData = Calculation::unwrapResult($cellData);
-            $spreadsheet->getActiveSheet()->getCell($coordinate)->setCalculatedValue($cellData);
+            $spreadsheet->getActiveSheet()->getCell($coordinate)->setCalculatedValue($cellData, $tryNumeric);
         }
     }
 
@@ -378,7 +358,7 @@ class Slk extends BaseReader
             } elseif (array_key_exists($char, self::STYLE_SETTINGS_BORDER)) {
                 $styleData['borders'][self::STYLE_SETTINGS_BORDER[$char]]['borderStyle'] = Border::BORDER_THIN;
             } elseif ($char == 'S') {
-                $styleData['fill']['fillType'] = \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_PATTERN_GRAY125;
+                $styleData['fill']['fillType'] = Fill::FILL_PATTERN_GRAY125;
             } elseif ($char == 'M') {
                 if (preg_match('/M([1-9]\\d*)/', $styleSettings, $matches)) {
                     $fontStyle = $matches[1];
@@ -426,7 +406,7 @@ class Slk extends BaseReader
                 $endCol = Coordinate::stringFromColumnIndex((int) $endCol);
                 $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth);
                 do {
-                    $spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth((float) $columnWidth);
+                    $spreadsheet->getActiveSheet()->getColumnDimension((string) ++$startCol)->setWidth((float) $columnWidth);
                 } while ($startCol !== $endCol);
             }
         }
@@ -469,7 +449,7 @@ class Slk extends BaseReader
     private function processPColors(string $rowDatum, array &$formatArray): void
     {
         if (preg_match('/L([1-9]\\d*)/', $rowDatum, $matches)) {
-            $fontColor = $matches[1] % 8;
+            $fontColor = ((int) $matches[1]) % 8;
             $formatArray['font']['color']['argb'] = self::COLOR_ARRAY[$fontColor];
         }
     }
@@ -501,12 +481,8 @@ class Slk extends BaseReader
 
     /**
      * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
-     *
-     * @param string $filename
-     *
-     * @return Spreadsheet
      */
-    public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
+    public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
     {
         // Open file
         $this->canReadOrBust($filename);
@@ -568,10 +544,8 @@ class Slk extends BaseReader
 
     /**
      * Get sheet index.
-     *
-     * @return int
      */
-    public function getSheetIndex()
+    public function getSheetIndex(): int
     {
         return $this->sheetIndex;
     }
@@ -583,7 +557,7 @@ class Slk extends BaseReader
      *
      * @return $this
      */
-    public function setSheetIndex($sheetIndex)
+    public function setSheetIndex(int $sheetIndex): static
     {
         $this->sheetIndex = $sheetIndex;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
index 5367eff..a6b4fb1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
@@ -14,6 +14,7 @@ use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Shared\CodePage;
 use PhpOffice\PhpSpreadsheet\Shared\Date;
 use PhpOffice\PhpSpreadsheet\Shared\Escher;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
 use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
 use PhpOffice\PhpSpreadsheet\Shared\File;
 use PhpOffice\PhpSpreadsheet\Shared\OLE;
@@ -22,6 +23,7 @@ use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Shared\Xls as SharedXls;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
 use PhpOffice\PhpSpreadsheet\Style\Borders;
 use PhpOffice\PhpSpreadsheet\Style\Conditional;
 use PhpOffice\PhpSpreadsheet\Style\Fill;
@@ -66,6 +68,10 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 //         external sheet reference structure
 class Xls extends BaseReader
 {
+    private const HIGH_ORDER_BIT = 0x80 << 24;
+    private const FC000000 = 0xFC << 24;
+    private const FE000000 = 0xFE << 24;
+
     // ParseXL definitions
     const XLS_BIFF8 = 0x0600;
     const XLS_BIFF7 = 0x0500;
@@ -74,57 +80,57 @@ class Xls extends BaseReader
 
     // record identifiers
     const XLS_TYPE_FORMULA = 0x0006;
-    const XLS_TYPE_EOF = 0x000a;
+    const XLS_TYPE_EOF = 0x000A;
     const XLS_TYPE_PROTECT = 0x0012;
     const XLS_TYPE_OBJECTPROTECT = 0x0063;
-    const XLS_TYPE_SCENPROTECT = 0x00dd;
+    const XLS_TYPE_SCENPROTECT = 0x00DD;
     const XLS_TYPE_PASSWORD = 0x0013;
     const XLS_TYPE_HEADER = 0x0014;
     const XLS_TYPE_FOOTER = 0x0015;
     const XLS_TYPE_EXTERNSHEET = 0x0017;
     const XLS_TYPE_DEFINEDNAME = 0x0018;
-    const XLS_TYPE_VERTICALPAGEBREAKS = 0x001a;
-    const XLS_TYPE_HORIZONTALPAGEBREAKS = 0x001b;
-    const XLS_TYPE_NOTE = 0x001c;
-    const XLS_TYPE_SELECTION = 0x001d;
+    const XLS_TYPE_VERTICALPAGEBREAKS = 0x001A;
+    const XLS_TYPE_HORIZONTALPAGEBREAKS = 0x001B;
+    const XLS_TYPE_NOTE = 0x001C;
+    const XLS_TYPE_SELECTION = 0x001D;
     const XLS_TYPE_DATEMODE = 0x0022;
     const XLS_TYPE_EXTERNNAME = 0x0023;
     const XLS_TYPE_LEFTMARGIN = 0x0026;
     const XLS_TYPE_RIGHTMARGIN = 0x0027;
     const XLS_TYPE_TOPMARGIN = 0x0028;
     const XLS_TYPE_BOTTOMMARGIN = 0x0029;
-    const XLS_TYPE_PRINTGRIDLINES = 0x002b;
-    const XLS_TYPE_FILEPASS = 0x002f;
+    const XLS_TYPE_PRINTGRIDLINES = 0x002B;
+    const XLS_TYPE_FILEPASS = 0x002F;
     const XLS_TYPE_FONT = 0x0031;
-    const XLS_TYPE_CONTINUE = 0x003c;
+    const XLS_TYPE_CONTINUE = 0x003C;
     const XLS_TYPE_PANE = 0x0041;
     const XLS_TYPE_CODEPAGE = 0x0042;
     const XLS_TYPE_DEFCOLWIDTH = 0x0055;
-    const XLS_TYPE_OBJ = 0x005d;
-    const XLS_TYPE_COLINFO = 0x007d;
-    const XLS_TYPE_IMDATA = 0x007f;
+    const XLS_TYPE_OBJ = 0x005D;
+    const XLS_TYPE_COLINFO = 0x007D;
+    const XLS_TYPE_IMDATA = 0x007F;
     const XLS_TYPE_SHEETPR = 0x0081;
     const XLS_TYPE_HCENTER = 0x0083;
     const XLS_TYPE_VCENTER = 0x0084;
     const XLS_TYPE_SHEET = 0x0085;
     const XLS_TYPE_PALETTE = 0x0092;
-    const XLS_TYPE_SCL = 0x00a0;
-    const XLS_TYPE_PAGESETUP = 0x00a1;
-    const XLS_TYPE_MULRK = 0x00bd;
-    const XLS_TYPE_MULBLANK = 0x00be;
-    const XLS_TYPE_DBCELL = 0x00d7;
-    const XLS_TYPE_XF = 0x00e0;
-    const XLS_TYPE_MERGEDCELLS = 0x00e5;
-    const XLS_TYPE_MSODRAWINGGROUP = 0x00eb;
-    const XLS_TYPE_MSODRAWING = 0x00ec;
-    const XLS_TYPE_SST = 0x00fc;
-    const XLS_TYPE_LABELSST = 0x00fd;
-    const XLS_TYPE_EXTSST = 0x00ff;
-    const XLS_TYPE_EXTERNALBOOK = 0x01ae;
-    const XLS_TYPE_DATAVALIDATIONS = 0x01b2;
-    const XLS_TYPE_TXO = 0x01b6;
-    const XLS_TYPE_HYPERLINK = 0x01b8;
-    const XLS_TYPE_DATAVALIDATION = 0x01be;
+    const XLS_TYPE_SCL = 0x00A0;
+    const XLS_TYPE_PAGESETUP = 0x00A1;
+    const XLS_TYPE_MULRK = 0x00BD;
+    const XLS_TYPE_MULBLANK = 0x00BE;
+    const XLS_TYPE_DBCELL = 0x00D7;
+    const XLS_TYPE_XF = 0x00E0;
+    const XLS_TYPE_MERGEDCELLS = 0x00E5;
+    const XLS_TYPE_MSODRAWINGGROUP = 0x00EB;
+    const XLS_TYPE_MSODRAWING = 0x00EC;
+    const XLS_TYPE_SST = 0x00FC;
+    const XLS_TYPE_LABELSST = 0x00FD;
+    const XLS_TYPE_EXTSST = 0x00FF;
+    const XLS_TYPE_EXTERNALBOOK = 0x01AE;
+    const XLS_TYPE_DATAVALIDATIONS = 0x01B2;
+    const XLS_TYPE_TXO = 0x01B6;
+    const XLS_TYPE_HYPERLINK = 0x01B8;
+    const XLS_TYPE_DATAVALIDATION = 0x01BE;
     const XLS_TYPE_DIMENSION = 0x0200;
     const XLS_TYPE_BLANK = 0x0201;
     const XLS_TYPE_NUMBER = 0x0203;
@@ -132,23 +138,23 @@ class Xls extends BaseReader
     const XLS_TYPE_BOOLERR = 0x0205;
     const XLS_TYPE_STRING = 0x0207;
     const XLS_TYPE_ROW = 0x0208;
-    const XLS_TYPE_INDEX = 0x020b;
+    const XLS_TYPE_INDEX = 0x020B;
     const XLS_TYPE_ARRAY = 0x0221;
     const XLS_TYPE_DEFAULTROWHEIGHT = 0x0225;
-    const XLS_TYPE_WINDOW2 = 0x023e;
-    const XLS_TYPE_RK = 0x027e;
+    const XLS_TYPE_WINDOW2 = 0x023E;
+    const XLS_TYPE_RK = 0x027E;
     const XLS_TYPE_STYLE = 0x0293;
-    const XLS_TYPE_FORMAT = 0x041e;
-    const XLS_TYPE_SHAREDFMLA = 0x04bc;
+    const XLS_TYPE_FORMAT = 0x041E;
+    const XLS_TYPE_SHAREDFMLA = 0x04BC;
     const XLS_TYPE_BOF = 0x0809;
     const XLS_TYPE_SHEETPROTECTION = 0x0867;
     const XLS_TYPE_RANGEPROTECTION = 0x0868;
     const XLS_TYPE_SHEETLAYOUT = 0x0862;
-    const XLS_TYPE_XFEXT = 0x087d;
-    const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
-    const XLS_TYPE_CFHEADER = 0x01b0;
-    const XLS_TYPE_CFRULE = 0x01b1;
-    const XLS_TYPE_UNKNOWN = 0xffff;
+    const XLS_TYPE_XFEXT = 0x087D;
+    const XLS_TYPE_PAGELAYOUTVIEW = 0x088B;
+    const XLS_TYPE_CFHEADER = 0x01B0;
+    const XLS_TYPE_CFRULE = 0x01B1;
+    const XLS_TYPE_UNKNOWN = 0xFFFF;
 
     // Encryption type
     const MS_BIFF_CRYPTO_NONE = 0;
@@ -158,262 +164,211 @@ class Xls extends BaseReader
     // Size of stream blocks when using RC4 encryption
     const REKEY_BLOCK = 0x400;
 
+    // should be consistent with Writer\Xls\Style\CellBorder
+    const BORDER_STYLE_MAP = [
+        Border::BORDER_NONE, // => 0x00,
+        Border::BORDER_THIN,  // => 0x01,
+        Border::BORDER_MEDIUM, // => 0x02,
+        Border::BORDER_DASHED, // => 0x03,
+        Border::BORDER_DOTTED,  // => 0x04,
+        Border::BORDER_THICK, // => 0x05,
+        Border::BORDER_DOUBLE, // => 0x06,
+        Border::BORDER_HAIR, // => 0x07,
+        Border::BORDER_MEDIUMDASHED, // => 0x08,
+        Border::BORDER_DASHDOT, // => 0x09,
+        Border::BORDER_MEDIUMDASHDOT, // => 0x0A,
+        Border::BORDER_DASHDOTDOT, // => 0x0B,
+        Border::BORDER_MEDIUMDASHDOTDOT, // => 0x0C,
+        Border::BORDER_SLANTDASHDOT, // => 0x0D,
+        Border::BORDER_OMIT, // => 0x0E,
+        Border::BORDER_OMIT, // => 0x0F,
+    ];
+
     /**
      * Summary Information stream data.
-     *
-     * @var string
      */
-    private $summaryInformation;
+    private ?string $summaryInformation = null;
 
     /**
      * Extended Summary Information stream data.
-     *
-     * @var string
      */
-    private $documentSummaryInformation;
+    private ?string $documentSummaryInformation = null;
 
     /**
      * Workbook stream data. (Includes workbook globals substream as well as sheet substreams).
-     *
-     * @var string
      */
-    private $data;
+    private string $data;
 
     /**
      * Size in bytes of $this->data.
-     *
-     * @var int
      */
-    private $dataSize;
+    private int $dataSize;
 
     /**
      * Current position in stream.
-     *
-     * @var int
      */
-    private $pos;
+    private int $pos;
 
     /**
      * Workbook to be returned by the reader.
-     *
-     * @var Spreadsheet
      */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     /**
      * Worksheet that is currently being built by the reader.
-     *
-     * @var Worksheet
      */
-    private $phpSheet;
+    private Worksheet $phpSheet;
 
     /**
      * BIFF version.
-     *
-     * @var int
      */
-    private $version;
+    private int $version = 0;
 
     /**
      * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
      * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'.
-     *
-     * @var string
      */
-    private $codepage;
+    private string $codepage = '';
 
     /**
      * Shared formats.
-     *
-     * @var array
      */
-    private $formats;
+    private array $formats;
 
     /**
      * Shared fonts.
      *
      * @var Font[]
      */
-    private $objFonts;
+    private array $objFonts;
 
     /**
      * Color palette.
-     *
-     * @var array
      */
-    private $palette;
+    private array $palette;
 
     /**
      * Worksheets.
-     *
-     * @var array
      */
-    private $sheets;
+    private array $sheets;
 
     /**
      * External books.
-     *
-     * @var array
      */
-    private $externalBooks;
+    private array $externalBooks;
 
     /**
      * REF structures. Only applies to BIFF8.
-     *
-     * @var array
      */
-    private $ref;
+    private array $ref;
 
     /**
      * External names.
-     *
-     * @var array
      */
-    private $externalNames;
+    private array $externalNames;
 
     /**
      * Defined names.
-     *
-     * @var array
      */
-    private $definedname;
+    private array $definedname;
 
     /**
      * Shared strings. Only applies to BIFF8.
-     *
-     * @var array
      */
-    private $sst;
+    private array $sst;
 
     /**
      * Panes are frozen? (in sheet currently being read). See WINDOW2 record.
-     *
-     * @var bool
      */
-    private $frozen;
+    private bool $frozen;
 
     /**
      * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
-     *
-     * @var bool
      */
-    private $isFitToPages;
+    private bool $isFitToPages;
 
     /**
      * Objects. One OBJ record contributes with one entry.
-     *
-     * @var array
      */
-    private $objs;
+    private array $objs;
 
     /**
      * Text Objects. One TXO record corresponds with one entry.
-     *
-     * @var array
      */
-    private $textObjects;
+    private array $textObjects;
 
     /**
      * Cell Annotations (BIFF8).
-     *
-     * @var array
      */
-    private $cellNotes;
+    private array $cellNotes;
 
     /**
      * The combined MSODRAWINGGROUP data.
-     *
-     * @var string
      */
-    private $drawingGroupData;
+    private string $drawingGroupData;
 
     /**
      * The combined MSODRAWING data (per sheet).
-     *
-     * @var string
      */
-    private $drawingData;
+    private string $drawingData;
 
     /**
      * Keep track of XF index.
-     *
-     * @var int
      */
-    private $xfIndex;
+    private int $xfIndex;
 
     /**
      * Mapping of XF index (that is a cell XF) to final index in cellXf collection.
-     *
-     * @var array
      */
-    private $mapCellXfIndex;
+    private array $mapCellXfIndex;
 
     /**
      * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection.
-     *
-     * @var array
      */
-    private $mapCellStyleXfIndex;
+    private array $mapCellStyleXfIndex;
 
     /**
      * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
-     *
-     * @var array
      */
-    private $sharedFormulas;
+    private array $sharedFormulas;
 
     /**
      * The shared formula parts in a sheet. One FORMULA record contributes with one value if it
      * refers to a shared formula.
-     *
-     * @var array
      */
-    private $sharedFormulaParts;
+    private array $sharedFormulaParts;
 
     /**
      * The type of encryption in use.
-     *
-     * @var int
      */
-    private $encryption = 0;
+    private int $encryption = 0;
 
     /**
      * The position in the stream after which contents are encrypted.
-     *
-     * @var int
      */
-    private $encryptionStartPos = 0;
+    private int $encryptionStartPos = 0;
 
     /**
      * The current RC4 decryption object.
-     *
-     * @var Xls\RC4
      */
-    private $rc4Key;
+    private ?Xls\RC4 $rc4Key = null;
 
     /**
      * The position in the stream that the RC4 decryption object was left at.
-     *
-     * @var int
      */
-    private $rc4Pos = 0;
+    private int $rc4Pos = 0;
 
     /**
      * The current MD5 context state.
-     *
-     * @var string
+     * It is never set in the program, so code which uses it is suspect.
      */
-    private $md5Ctxt;
+    private string $md5Ctxt; // @phpstan-ignore-line
 
-    /**
-     * @var int
-     */
-    private $textObjRef;
+    private int $textObjRef;
 
-    /**
-     * @var string
-     */
-    private $baseCell;
+    private string $baseCell;
+
+    private bool $activeSheetSet = false;
 
     /**
      * Create a new Xls Reader instance.
@@ -428,7 +383,7 @@ class Xls extends BaseReader
      */
     public function canRead(string $filename): bool
     {
-        if (!File::testFileNoThrow($filename)) {
+        if (File::testFileNoThrow($filename) === false) {
             return false;
         }
 
@@ -438,30 +393,34 @@ class Xls extends BaseReader
 
             // get excel data
             $ole->read($filename);
+            if ($ole->wrkbook === null) {
+                throw new Exception('The filename ' . $filename . ' is not recognised as a Spreadsheet file');
+            }
 
             return true;
-        } catch (PhpSpreadsheetException $e) {
+        } catch (PhpSpreadsheetException) {
             return false;
         }
     }
 
     public function setCodepage(string $codepage): void
     {
-        if (!CodePage::validate($codepage)) {
+        if (CodePage::validate($codepage) === false) {
             throw new PhpSpreadsheetException('Unknown codepage: ' . $codepage);
         }
 
         $this->codepage = $codepage;
     }
 
+    public function getCodepage(): string
+    {
+        return $this->codepage;
+    }
+
     /**
      * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetNames($filename)
+    public function listWorksheetNames(string $filename): array
     {
         File::assertFile($filename);
 
@@ -480,23 +439,16 @@ class Xls extends BaseReader
         while ($this->pos < $this->dataSize) {
             $code = self::getUInt2d($this->data, $this->pos);
 
-            switch ($code) {
-                case self::XLS_TYPE_BOF:
-                    $this->readBof();
+            match ($code) {
+                self::XLS_TYPE_BOF => $this->readBof(),
+                self::XLS_TYPE_SHEET => $this->readSheet(),
+                self::XLS_TYPE_EOF => $this->readDefault(),
+                self::XLS_TYPE_CODEPAGE => $this->readCodepage(),
+                default => $this->readDefault(),
+            };
 
-                    break;
-                case self::XLS_TYPE_SHEET:
-                    $this->readSheet();
-
-                    break;
-                case self::XLS_TYPE_EOF:
-                    $this->readDefault();
-
-                    break 2;
-                default:
-                    $this->readDefault();
-
-                    break;
+            if ($code === self::XLS_TYPE_EOF) {
+                break;
             }
         }
 
@@ -514,12 +466,8 @@ class Xls extends BaseReader
 
     /**
      * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetInfo($filename)
+    public function listWorksheetInfo(string $filename): array
     {
         File::assertFile($filename);
 
@@ -539,23 +487,16 @@ class Xls extends BaseReader
         while ($this->pos < $this->dataSize) {
             $code = self::getUInt2d($this->data, $this->pos);
 
-            switch ($code) {
-                case self::XLS_TYPE_BOF:
-                    $this->readBof();
+            match ($code) {
+                self::XLS_TYPE_BOF => $this->readBof(),
+                self::XLS_TYPE_SHEET => $this->readSheet(),
+                self::XLS_TYPE_EOF => $this->readDefault(),
+                self::XLS_TYPE_CODEPAGE => $this->readCodepage(),
+                default => $this->readDefault(),
+            };
 
-                    break;
-                case self::XLS_TYPE_SHEET:
-                    $this->readSheet();
-
-                    break;
-                case self::XLS_TYPE_EOF:
-                    $this->readDefault();
-
-                    break 2;
-                default:
-                    $this->readDefault();
-
-                    break;
+            if ($code === self::XLS_TYPE_EOF) {
+                break;
             }
         }
 
@@ -669,83 +610,30 @@ class Xls extends BaseReader
         while ($this->pos < $this->dataSize) {
             $code = self::getUInt2d($this->data, $this->pos);
 
-            switch ($code) {
-                case self::XLS_TYPE_BOF:
-                    $this->readBof();
+            match ($code) {
+                self::XLS_TYPE_BOF => $this->readBof(),
+                self::XLS_TYPE_FILEPASS => $this->readFilepass(),
+                self::XLS_TYPE_CODEPAGE => $this->readCodepage(),
+                self::XLS_TYPE_DATEMODE => $this->readDateMode(),
+                self::XLS_TYPE_FONT => $this->readFont(),
+                self::XLS_TYPE_FORMAT => $this->readFormat(),
+                self::XLS_TYPE_XF => $this->readXf(),
+                self::XLS_TYPE_XFEXT => $this->readXfExt(),
+                self::XLS_TYPE_STYLE => $this->readStyle(),
+                self::XLS_TYPE_PALETTE => $this->readPalette(),
+                self::XLS_TYPE_SHEET => $this->readSheet(),
+                self::XLS_TYPE_EXTERNALBOOK => $this->readExternalBook(),
+                self::XLS_TYPE_EXTERNNAME => $this->readExternName(),
+                self::XLS_TYPE_EXTERNSHEET => $this->readExternSheet(),
+                self::XLS_TYPE_DEFINEDNAME => $this->readDefinedName(),
+                self::XLS_TYPE_MSODRAWINGGROUP => $this->readMsoDrawingGroup(),
+                self::XLS_TYPE_SST => $this->readSst(),
+                self::XLS_TYPE_EOF => $this->readDefault(),
+                default => $this->readDefault(),
+            };
 
-                    break;
-                case self::XLS_TYPE_FILEPASS:
-                    $this->readFilepass();
-
-                    break;
-                case self::XLS_TYPE_CODEPAGE:
-                    $this->readCodepage();
-
-                    break;
-                case self::XLS_TYPE_DATEMODE:
-                    $this->readDateMode();
-
-                    break;
-                case self::XLS_TYPE_FONT:
-                    $this->readFont();
-
-                    break;
-                case self::XLS_TYPE_FORMAT:
-                    $this->readFormat();
-
-                    break;
-                case self::XLS_TYPE_XF:
-                    $this->readXf();
-
-                    break;
-                case self::XLS_TYPE_XFEXT:
-                    $this->readXfExt();
-
-                    break;
-                case self::XLS_TYPE_STYLE:
-                    $this->readStyle();
-
-                    break;
-                case self::XLS_TYPE_PALETTE:
-                    $this->readPalette();
-
-                    break;
-                case self::XLS_TYPE_SHEET:
-                    $this->readSheet();
-
-                    break;
-                case self::XLS_TYPE_EXTERNALBOOK:
-                    $this->readExternalBook();
-
-                    break;
-                case self::XLS_TYPE_EXTERNNAME:
-                    $this->readExternName();
-
-                    break;
-                case self::XLS_TYPE_EXTERNSHEET:
-                    $this->readExternSheet();
-
-                    break;
-                case self::XLS_TYPE_DEFINEDNAME:
-                    $this->readDefinedName();
-
-                    break;
-                case self::XLS_TYPE_MSODRAWINGGROUP:
-                    $this->readMsoDrawingGroup();
-
-                    break;
-                case self::XLS_TYPE_SST:
-                    $this->readSst();
-
-                    break;
-                case self::XLS_TYPE_EOF:
-                    $this->readDefault();
-
-                    break 2;
-                default:
-                    $this->readDefault();
-
-                    break;
+            if ($code === self::XLS_TYPE_EOF) {
+                break;
             }
         }
 
@@ -811,7 +699,9 @@ class Xls extends BaseReader
         }
 
         // Parse the individual sheets
+        $this->activeSheetSet = false;
         foreach ($this->sheets as $sheet) {
+            $selectedCells = '';
             if ($sheet['sheetType'] != 0x00) {
                 // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
                 continue;
@@ -1019,7 +909,7 @@ class Xls extends BaseReader
 
                         break;
                     case self::XLS_TYPE_SELECTION:
-                        $this->readSelection();
+                        $selectedCells = $this->readSelection();
 
                         break;
                     case self::XLS_TYPE_MERGEDCELLS:
@@ -1088,13 +978,14 @@ class Xls extends BaseReader
                 $escherWorksheet = $reader->load($this->drawingData);
 
                 // get all spContainers in one long array, so they can be mapped to OBJ records
-                $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
+                /** @var SpContainer[] $allSpContainers */
+                $allSpContainers = method_exists($escherWorksheet, 'getDgContainer') ? $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers() : [];
             }
 
             // treat OBJ records
             foreach ($this->objs as $n => $obj) {
                 // the first shape container never has a corresponding OBJ record, hence $n + 1
-                if (isset($allSpContainers[$n + 1]) && is_object($allSpContainers[$n + 1])) {
+                if (isset($allSpContainers[$n + 1])) {
                     $spContainer = $allSpContainers[$n + 1];
 
                     // we skip all spContainers that are a part of a group shape since we cannot yet handle those
@@ -1103,7 +994,9 @@ class Xls extends BaseReader
                     }
 
                     // calculate the width and height of the shape
+                    /** @var int $startRow */
                     [$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
+                    /** @var int $endRow */
                     [$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
 
                     $startOffsetX = $spContainer->getStartOffsetX();
@@ -1122,7 +1015,7 @@ class Xls extends BaseReader
                         case 0x19:
                             // Note
                             if (isset($this->cellNotes[$obj['idObjID']])) {
-                                $cellNote = $this->cellNotes[$obj['idObjID']];
+                                //$cellNote = $this->cellNotes[$obj['idObjID']];
 
                                 if (isset($this->textObjects[$obj['idObjID']])) {
                                     $textObject = $this->textObjects[$obj['idObjID']];
@@ -1145,7 +1038,7 @@ class Xls extends BaseReader
                             }
 
                             if ($escherWorkbook) {
-                                $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
+                                $BSECollection = method_exists($escherWorkbook, 'getDggContainer') ? $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection() : [];
                                 $BSE = $BSECollection[$BSEindex - 1];
                                 $blipType = $BSE->getBlipType();
 
@@ -1195,6 +1088,7 @@ class Xls extends BaseReader
             // treat SHAREDFMLA records
             if ($this->version == self::XLS_BIFF8) {
                 foreach ($this->sharedFormulaParts as $cell => $baseCell) {
+                    /** @var int $row */
                     [$column, $row] = Coordinate::coordinateFromString($cell);
                     if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
                         $formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
@@ -1217,6 +1111,12 @@ class Xls extends BaseReader
                     $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
                 }
             }
+            if ($selectedCells !== '') {
+                $this->phpSheet->setSelectedCells($selectedCells);
+            }
+        }
+        if ($this->activeSheetSet === false) {
+            $this->spreadsheet->setActiveSheetIndex(0);
         }
 
         // add the named ranges (defined names)
@@ -1229,18 +1129,18 @@ class Xls extends BaseReader
                         $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
 
                         $extractedRanges = [];
+                        $sheetName = '';
+                        /** @var non-empty-string $range */
                         foreach ($ranges as $range) {
                             // $range should look like one of these
                             //        Foo!$C$7:$J$66
                             //        Bar!$A$1:$IV$2
                             $explodes = Worksheet::extractSheetTitle($range, true);
                             $sheetName = trim($explodes[0], "'");
-                            if (count($explodes) == 2) {
-                                if (strpos($explodes[1], ':') === false) {
-                                    $explodes[1] = $explodes[1] . ':' . $explodes[1];
-                                }
-                                $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
+                            if (!str_contains($explodes[1], ':')) {
+                                $explodes[1] = $explodes[1] . ':' . $explodes[1];
                             }
+                            $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
                         }
                         if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
                             $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
@@ -1263,7 +1163,7 @@ class Xls extends BaseReader
                             // $range should look like this one of these
                             //        Sheet!$A$1:$B$65536
                             //        Sheet!$A$1:$IV$2
-                            if (strpos($range, '!') !== false) {
+                            if (str_contains($range, '!')) {
                                 $explodes = Worksheet::extractSheetTitle($range, true);
                                 if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
                                     $extractedRange = $explodes[1];
@@ -1290,11 +1190,13 @@ class Xls extends BaseReader
                 }
             } else {
                 // Extract range
-                if (strpos($definedName['formula'], '!') !== false) {
-                    $explodes = Worksheet::extractSheetTitle($definedName['formula'], true);
+                /** @var non-empty-string $formula */
+                $formula = $definedName['formula'];
+                if (str_contains($formula, '!')) {
+                    $explodes = Worksheet::extractSheetTitle($formula, true);
                     if (
-                        ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
-                        ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))
+                        ($docSheet = $this->spreadsheet->getSheetByName($explodes[0]))
+                        || ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))
                     ) {
                         $extractedRange = $explodes[1];
 
@@ -1306,7 +1208,7 @@ class Xls extends BaseReader
                     }
                 }
                 //    Named Value
-                    //    TODO Provide support for named values
+                //    TODO Provide support for named values
             }
         }
         $this->data = '';
@@ -1323,7 +1225,7 @@ class Xls extends BaseReader
      *
      * @return string Record data
      */
-    private function readRecordData($data, $pos, $len)
+    private function readRecordData(string $data, int $pos, int $len): string
     {
         $data = substr($data, $pos, $len);
 
@@ -1335,8 +1237,8 @@ class Xls extends BaseReader
         $recordData = '';
         if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
             $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
-            $block = floor($pos / self::REKEY_BLOCK);
-            $endBlock = floor(($pos + $len) / self::REKEY_BLOCK);
+            $block = (int) floor($pos / self::REKEY_BLOCK);
+            $endBlock = (int) floor(($pos + $len) / self::REKEY_BLOCK);
 
             // Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
             // at a point earlier in the current block, re-use it as we can save some time.
@@ -1372,17 +1274,15 @@ class Xls extends BaseReader
 
     /**
      * Use OLE reader to extract the relevant data streams from the OLE file.
-     *
-     * @param string $filename
      */
-    private function loadOLE($filename): void
+    private function loadOLE(string $filename): void
     {
         // OLE reader
         $ole = new OLERead();
         // get excel data,
         $ole->read($filename);
         // Get workbook data: workbook stream + sheet streams
-        $this->data = $ole->getStream($ole->wrkbook);
+        $this->data = $ole->getStream($ole->wrkbook); // @phpstan-ignore-line
         // Get summary information data
         $this->summaryInformation = $ole->getStream($ole->summaryInformation);
         // Get additional document summary information data
@@ -1404,7 +1304,7 @@ class Xls extends BaseReader
         // offset: 6; size: 2; OS indicator
         // offset: 8; size: 16
         // offset: 24; size: 4; section count
-        $secCount = self::getInt4d($this->summaryInformation, 24);
+        //$secCount = self::getInt4d($this->summaryInformation, 24);
 
         // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
         // offset: 44; size: 4
@@ -1412,7 +1312,7 @@ class Xls extends BaseReader
 
         // section header
         // offset: $secOffset; size: 4; section length
-        $secLength = self::getInt4d($this->summaryInformation, $secOffset);
+        //$secLength = self::getInt4d($this->summaryInformation, $secOffset);
 
         // offset: $secOffset+4; size: 4; property count
         $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
@@ -1551,7 +1451,7 @@ class Xls extends BaseReader
         //    offset: 6;    size: 2;    OS indicator
         //    offset: 8;    size: 16
         //    offset: 24;    size: 4;    section count
-        $secCount = self::getInt4d($this->documentSummaryInformation, 24);
+        //$secCount = self::getInt4d($this->documentSummaryInformation, 24);
 
         // offset: 28;    size: 16;    first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
         // offset: 44;    size: 4;    first section offset
@@ -1559,7 +1459,7 @@ class Xls extends BaseReader
 
         //    section header
         //    offset: $secOffset;    size: 4;    section length
-        $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
+        //$secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
 
         //    offset: $secOffset+4;    size: 4;    property count
         $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
@@ -1717,14 +1617,14 @@ class Xls extends BaseReader
                 //    If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
                 //        note from the previous cell annotation. We're not yet handling this, so annotations longer than the
                 //        max 2048 bytes will probably throw a wobbly.
-                $row = self::getUInt2d($recordData, 0);
+                //$row = self::getUInt2d($recordData, 0);
                 $extension = true;
                 $arrayKeys = array_keys($this->phpSheet->getComments());
                 $cellAddress = array_pop($arrayKeys);
             }
 
-            $cellAddress = str_replace('$', '', $cellAddress);
-            $noteLength = self::getUInt2d($recordData, 4);
+            $cellAddress = str_replace('$', '', (string) $cellAddress);
+            //$noteLength = self::getUInt2d($recordData, 4);
             $noteText = trim(substr($recordData, 6));
 
             if ($extension) {
@@ -1763,7 +1663,7 @@ class Xls extends BaseReader
         // followed by the continuation records containing the actual text and formatting
         $grbitOpts = self::getUInt2d($recordData, 0);
         $rot = self::getUInt2d($recordData, 2);
-        $cchText = self::getUInt2d($recordData, 10);
+        //$cchText = self::getUInt2d($recordData, 10);
         $cbRuns = self::getUInt2d($recordData, 12);
         $text = $this->getSplicedRecordData();
 
@@ -1869,10 +1769,8 @@ class Xls extends BaseReader
      *
      * @param int $block Block for which to create decrypto
      * @param string $valContext MD5 context state
-     *
-     * @return Xls\RC4
      */
-    private function makeKey($block, $valContext)
+    private function makeKey(int $block, string $valContext): Xls\RC4
     {
         $pwarray = str_repeat("\0", 64);
 
@@ -1880,10 +1778,10 @@ class Xls extends BaseReader
             $pwarray[$i] = $valContext[$i];
         }
 
-        $pwarray[5] = chr($block & 0xff);
-        $pwarray[6] = chr(($block >> 8) & 0xff);
-        $pwarray[7] = chr(($block >> 16) & 0xff);
-        $pwarray[8] = chr(($block >> 24) & 0xff);
+        $pwarray[5] = chr($block & 0xFF);
+        $pwarray[6] = chr(($block >> 8) & 0xFF);
+        $pwarray[7] = chr(($block >> 16) & 0xFF);
+        $pwarray[8] = chr(($block >> 24) & 0xFF);
 
         $pwarray[9] = "\x80";
         $pwarray[56] = "\x48";
@@ -1907,18 +1805,18 @@ class Xls extends BaseReader
      *
      * @return bool Success
      */
-    private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
+    private function verifyPassword(string $password, string $docid, string $salt_data, string $hashedsalt_data, string &$valContext): bool
     {
         $pwarray = str_repeat("\0", 64);
 
         $iMax = strlen($password);
         for ($i = 0; $i < $iMax; ++$i) {
             $o = ord(substr($password, $i, 1));
-            $pwarray[2 * $i] = chr($o & 0xff);
-            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
+            $pwarray[2 * $i] = chr($o & 0xFF);
+            $pwarray[2 * $i + 1] = chr(($o >> 8) & 0xFF);
         }
         $pwarray[2 * $i] = chr(0x80);
-        $pwarray[56] = chr(($i << 4) & 0xff);
+        $pwarray[56] = chr(($i << 4) & 0xFF);
 
         $md5 = new Xls\MD5();
         $md5->add($pwarray);
@@ -2027,8 +1925,10 @@ class Xls extends BaseReader
 
         // offset: 0; size: 2; 0 = base 1900, 1 = base 1904
         Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
+        $this->spreadsheet->setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
         if (ord($recordData[0]) == 1) {
             Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
+            $this->spreadsheet->setExcelCalendar(Date::CALENDAR_MAC_1904);
         }
     }
 
@@ -2070,12 +1970,9 @@ class Xls extends BaseReader
             $objFont->colorIndex = $colorIndex;
 
             // offset: 6; size: 2; font weight
-            $weight = self::getUInt2d($recordData, 6);
-            switch ($weight) {
-                case 0x02BC:
-                    $objFont->setBold(true);
-
-                    break;
+            $weight = self::getUInt2d($recordData, 6); // regular=400 bold=700
+            if ($weight >= 550) {
+                $objFont->setBold(true);
             }
 
             // offset: 8; size: 2; escapement type
@@ -2175,7 +2072,9 @@ class Xls extends BaseReader
                 // check the OpenOffice documentation of the FONT record
                 $fontIndex = self::getUInt2d($recordData, 0) - 1;
             }
-            $objStyle->setFont($this->objFonts[$fontIndex]);
+            if (isset($this->objFonts[$fontIndex])) {
+                $objStyle->setFont($this->objFonts[$fontIndex]);
+            }
 
             // offset:  2; size: 2; Index to FORMAT record
             $numberFormatIndex = self::getUInt2d($recordData, 2);
@@ -2277,16 +2176,16 @@ class Xls extends BaseReader
                 // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
                 $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
 
-                // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
-                $diagonalUp = ((int) 0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
+                // bit: 31; mask: 0x800000; 1 = diagonal line from bottom left to top right
+                $diagonalUp = (self::HIGH_ORDER_BIT & self::getInt4d($recordData, 10)) >> 31 ? true : false;
 
                 if ($diagonalUp === false) {
-                    if ($diagonalDown == false) {
+                    if ($diagonalDown === false) {
                         $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
                     } else {
                         $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
                     }
-                } elseif ($diagonalDown == false) {
+                } elseif ($diagonalDown === false) {
                     $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
                 } else {
                     $objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
@@ -2308,7 +2207,7 @@ class Xls extends BaseReader
                 }
 
                 // bit: 31-26; mask: 0xFC000000 fill pattern
-                if ($fillType = Xls\Style\FillPattern::lookup(((int) 0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
+                if ($fillType = FillPattern::lookup((self::FC000000 & self::getInt4d($recordData, 14)) >> 26)) {
                     $objStyle->getFill()->setFillType($fillType);
                 }
                 // offset: 18; size: 2; pattern and background colour
@@ -2354,13 +2253,13 @@ class Xls extends BaseReader
                 $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
 
                 // bit: 21-16; mask: 0x003F0000; fill pattern
-                $objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
+                $objStyle->getFill()->setFillType(FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
 
                 // bit: 24-22; mask: 0x01C00000; bottom line style
                 $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
 
                 // bit: 31-25; mask: 0xFE000000; bottom line color
-                $objStyle->getBorders()->getBottom()->colorIndex = ((int) 0xFE000000 & $borderAndBackground) >> 25;
+                $objStyle->getBorders()->getBottom()->colorIndex = (self::FE000000 & $borderAndBackground) >> 25;
 
                 // offset: 12; size: 4; cell border lines
                 $borderLines = self::getInt4d($recordData, 12);
@@ -2425,7 +2324,7 @@ class Xls extends BaseReader
             // offset: 16; size: 2; not used
 
             // offset: 18; size: 2; number of extension properties that follow
-            $cexts = self::getUInt2d($recordData, 18);
+            //$cexts = self::getUInt2d($recordData, 18);
 
             // start reading the actual extension data
             $offset = 20;
@@ -2591,7 +2490,7 @@ class Xls extends BaseReader
             $ixfe = self::getUInt2d($recordData, 0);
 
             // bit: 11-0; mask 0x0FFF; index to XF record
-            $xfIndex = (0x0FFF & $ixfe) >> 0;
+            //$xfIndex = (0x0FFF & $ixfe) >> 0;
 
             // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
             $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
@@ -2660,24 +2559,12 @@ class Xls extends BaseReader
         $this->pos += 4 + $length;
 
         // offset: 4; size: 1; sheet state
-        switch (ord($recordData[4])) {
-            case 0x00:
-                $sheetState = Worksheet::SHEETSTATE_VISIBLE;
-
-                break;
-            case 0x01:
-                $sheetState = Worksheet::SHEETSTATE_HIDDEN;
-
-                break;
-            case 0x02:
-                $sheetState = Worksheet::SHEETSTATE_VERYHIDDEN;
-
-                break;
-            default:
-                $sheetState = Worksheet::SHEETSTATE_VISIBLE;
-
-                break;
-        }
+        $sheetState = match (ord($recordData[4])) {
+            0x00 => Worksheet::SHEETSTATE_VISIBLE,
+            0x01 => Worksheet::SHEETSTATE_HIDDEN,
+            0x02 => Worksheet::SHEETSTATE_VERYHIDDEN,
+            default => Worksheet::SHEETSTATE_VISIBLE,
+        };
 
         // offset: 5; size: 1; sheet type
         $sheetType = ord($recordData[5]);
@@ -2776,7 +2663,7 @@ class Xls extends BaseReader
         // external sheet references provided for named cells
         if ($this->version == self::XLS_BIFF8) {
             // offset: 0; size: 2; options
-            $options = self::getUInt2d($recordData, 0);
+            //$options = self::getUInt2d($recordData, 0);
 
             // offset: 2; size: 2;
 
@@ -2873,8 +2760,9 @@ class Xls extends BaseReader
 
             try {
                 $formula = $this->getFormulaFromStructure($formulaStructure);
-            } catch (PhpSpreadsheetException $e) {
+            } catch (PhpSpreadsheetException) {
                 $formula = '';
+                $isBuiltInName = 0;
             }
 
             $this->definedname[] = [
@@ -2891,7 +2779,7 @@ class Xls extends BaseReader
      */
     private function readMsoDrawingGroup(): void
     {
-        $length = self::getUInt2d($this->data, $this->pos + 2);
+        //$length = self::getUInt2d($this->data, $this->pos + 2);
 
         // get spliced record data
         $splicedRecordData = $this->getSplicedRecordData();
@@ -3043,7 +2931,7 @@ class Xls extends BaseReader
                         $len = min($charsLeft, $limitpos - $pos);
                         for ($j = 0; $j < $len; ++$j) {
                             $retstr .= $recordData[$pos + $j]
-                            . chr(0);
+                                . chr(0);
                         }
                         $charsLeft -= $len;
                         $isCompressed = false;
@@ -3154,11 +3042,11 @@ class Xls extends BaseReader
 
         // bit: 6; mask: 0x0040; 0 = outline buttons above outline group
         $isSummaryBelow = (0x0040 & self::getUInt2d($recordData, 0)) >> 6;
-        $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
+        $this->phpSheet->setShowSummaryBelow((bool) $isSummaryBelow);
 
         // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
         $isSummaryRight = (0x0080 & self::getUInt2d($recordData, 0)) >> 7;
-        $this->phpSheet->setShowSummaryRight($isSummaryRight);
+        $this->phpSheet->setShowSummaryRight((bool) $isSummaryRight);
 
         // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
         // this corresponds to radio button setting in page setup dialog in Excel
@@ -3184,7 +3072,7 @@ class Xls extends BaseReader
             for ($i = 0; $i < $nm; ++$i) {
                 $r = self::getUInt2d($recordData, 2 + 6 * $i);
                 $cf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
-                $cl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
+                //$cl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
 
                 // not sure why two column indexes are necessary?
                 $this->phpSheet->setBreak([$cf + 1, $r], Worksheet::BREAK_ROW);
@@ -3211,10 +3099,10 @@ class Xls extends BaseReader
             for ($i = 0; $i < $nm; ++$i) {
                 $c = self::getUInt2d($recordData, 2 + 6 * $i);
                 $rf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
-                $rl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
+                //$rl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
 
                 // not sure why two row indexes are necessary?
-                $this->phpSheet->setBreak([$c + 1, $rf], Worksheet::BREAK_COLUMN);
+                $this->phpSheet->setBreak([$c + 1, ($rf > 0) ? $rf : 1], Worksheet::BREAK_COLUMN);
             }
         }
     }
@@ -3731,7 +3619,7 @@ class Xls extends BaseReader
         $column = self::getUInt2d($recordData, 2);
         $columnString = Coordinate::stringFromColumnIndex($column + 1);
 
-        $emptyCell = true;
+        $cell = null;
         // Read cell?
         if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
             // offset: 4; size: 2; index to XF record
@@ -3778,17 +3666,15 @@ class Xls extends BaseReader
                 if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
                     $cell = $this->phpSheet->getCell($columnString . ($row + 1));
                     $cell->setValueExplicit($richText, DataType::TYPE_STRING);
-                    $emptyCell = false;
                 }
             } else {
                 if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
                     $cell = $this->phpSheet->getCell($columnString . ($row + 1));
                     $cell->setValueExplicit($this->sst[$index]['value'], DataType::TYPE_STRING);
-                    $emptyCell = false;
                 }
             }
 
-            if (!$this->readDataOnly && !$emptyCell && isset($this->mapCellXfIndex[$xfIndex])) {
+            if (!$this->readDataOnly && $cell !== null && isset($this->mapCellXfIndex[$xfIndex])) {
                 // add style information
                 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
             }
@@ -4008,7 +3894,7 @@ class Xls extends BaseReader
                     }
                     $formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
                     $cell->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
-                } catch (PhpSpreadsheetException $e) {
+                } catch (PhpSpreadsheetException) {
                     $cell->setValueExplicit($value, $dataType);
                 }
             } else {
@@ -4020,7 +3906,7 @@ class Xls extends BaseReader
             }
 
             // store the cached calculated value
-            $cell->setCalculatedValue($value);
+            $cell->setCalculatedValue($value, $dataType === DataType::TYPE_NUMERIC);
         }
     }
 
@@ -4038,13 +3924,13 @@ class Xls extends BaseReader
         $this->pos += 4 + $length;
 
         // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
-        $cellRange = substr($recordData, 0, 6);
-        $cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
+        //$cellRange = substr($recordData, 0, 6);
+        //$cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
 
         // offset: 6, size: 1; not used
 
         // offset: 7, size: 1; number of existing FORMULA records for this shared formula
-        $no = ord($recordData[7]);
+        //$no = ord($recordData[7]);
 
         // offset: 8, size: var; Binary token array of the shared formula
         $formula = substr($recordData, 8);
@@ -4060,7 +3946,7 @@ class Xls extends BaseReader
      *
      * @return string The string contents as UTF-8
      */
-    private function readString()
+    private function readString(): string
     {
         $length = self::getUInt2d($this->data, $this->pos + 2);
         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
@@ -4265,7 +4151,7 @@ class Xls extends BaseReader
      */
     private function readMsoDrawing(): void
     {
-        $length = self::getUInt2d($this->data, $this->pos + 2);
+        //$length = self::getUInt2d($this->data, $this->pos + 2);
 
         // get spliced record data
         $splicedRecordData = $this->getSplicedRecordData();
@@ -4329,10 +4215,10 @@ class Xls extends BaseReader
         $options = self::getUInt2d($recordData, 0);
 
         // offset: 2; size: 2; index to first visible row
-        $firstVisibleRow = self::getUInt2d($recordData, 2);
+        //$firstVisibleRow = self::getUInt2d($recordData, 2);
 
         // offset: 4; size: 2; index to first visible colum
-        $firstVisibleColumn = self::getUInt2d($recordData, 4);
+        //$firstVisibleColumn = self::getUInt2d($recordData, 4);
         $zoomscaleInPageBreakPreview = 0;
         $zoomscaleInNormalView = 0;
         if ($this->version === self::XLS_BIFF8) {
@@ -4379,6 +4265,7 @@ class Xls extends BaseReader
         $isActive = (bool) ((0x0400 & $options) >> 10);
         if ($isActive) {
             $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
+            $this->activeSheetSet = true;
         }
 
         // bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
@@ -4411,10 +4298,10 @@ class Xls extends BaseReader
 
         // offset: 0; size: 2; rt
         //->ignore
-        $rt = self::getUInt2d($recordData, 0);
+        //$rt = self::getUInt2d($recordData, 0);
         // offset: 2; size: 2; grbitfr
         //->ignore
-        $grbitFrt = self::getUInt2d($recordData, 2);
+        //$grbitFrt = self::getUInt2d($recordData, 2);
         // offset: 4; size: 8; reserved
         //->ignore
 
@@ -4425,8 +4312,8 @@ class Xls extends BaseReader
 
         // decomprise grbit
         $fPageLayoutView = $grbit & 0x01;
-        $fRulerVisible = ($grbit >> 1) & 0x01; //no support
-        $fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
+        //$fRulerVisible = ($grbit >> 1) & 0x01; //no support
+        //$fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
 
         if ($fPageLayoutView === 1) {
             $this->phpSheet->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);
@@ -4493,27 +4380,28 @@ class Xls extends BaseReader
     /**
      * Read SELECTION record. There is one such record for each pane in the sheet.
      */
-    private function readSelection(): void
+    private function readSelection(): string
     {
         $length = self::getUInt2d($this->data, $this->pos + 2);
         $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+        $selectedCells = '';
 
         // move stream pointer to next record
         $this->pos += 4 + $length;
 
         if (!$this->readDataOnly) {
             // offset: 0; size: 1; pane identifier
-            $paneId = ord($recordData[0]);
+            //$paneId = ord($recordData[0]);
 
             // offset: 1; size: 2; index to row of the active cell
-            $r = self::getUInt2d($recordData, 1);
+            //$r = self::getUInt2d($recordData, 1);
 
             // offset: 3; size: 2; index to column of the active cell
-            $c = self::getUInt2d($recordData, 3);
+            //$c = self::getUInt2d($recordData, 3);
 
             // offset: 5; size: 2; index into the following cell range list to the
             //  entry that contains the active cell
-            $index = self::getUInt2d($recordData, 5);
+            //$index = self::getUInt2d($recordData, 5);
 
             // offset: 7; size: var; cell range address list containing all selected cell ranges
             $data = substr($recordData, 7);
@@ -4538,9 +4426,11 @@ class Xls extends BaseReader
 
             $this->phpSheet->setSelectedCells($selectedCells);
         }
+
+        return $selectedCells;
     }
 
-    private function includeCellRangeFiltered($cellRangeAddress)
+    private function includeCellRangeFiltered(string $cellRangeAddress): bool
     {
         $includeCellRange = true;
         if ($this->getReadFilter() !== null) {
@@ -4582,8 +4472,8 @@ class Xls extends BaseReader
             $cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
             foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
                 if (
-                    (strpos($cellRangeAddress, ':') !== false) &&
-                    ($this->includeCellRangeFiltered($cellRangeAddress))
+                    (str_contains($cellRangeAddress, ':'))
+                    && ($this->includeCellRangeFiltered($cellRangeAddress))
                 ) {
                     $this->phpSheet->mergeCells($cellRangeAddress, Worksheet::MERGE_CELL_CONTENT_HIDE);
                 }
@@ -4606,7 +4496,7 @@ class Xls extends BaseReader
             // offset: 0; size: 8; cell range address of all cells containing this hyperlink
             try {
                 $cellRange = $this->readBIFF8CellRangeAddressFixed($recordData);
-            } catch (PhpSpreadsheetException $e) {
+            } catch (PhpSpreadsheetException) {
                 return;
             }
 
@@ -4619,7 +4509,7 @@ class Xls extends BaseReader
             $isFileLinkOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 0;
 
             // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
-            $isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1;
+            //$isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1;
 
             // bit: 2 (and 4); mask: 0x00000014; 0 = no description
             $hasDesc = (0x00000014 & self::getUInt2d($recordData, 28)) >> 2;
@@ -4640,7 +4530,7 @@ class Xls extends BaseReader
                 // offset: 32; size: var; character count of description text
                 $dl = self::getInt4d($recordData, 32);
                 // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
-                $desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
+                //$desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
                 $offset += 4 + 2 * $dl;
             }
             if ($hasFrame) {
@@ -4713,6 +4603,7 @@ class Xls extends BaseReader
                     $sz = self::getInt4d($recordData, $offset);
                     $offset += 4;
 
+                    $extendedFilePath = '';
                     // only present if $sz > 0
                     if ($sz > 0) {
                         // offset: var; size: 4; size of the character array of the extended file path and name
@@ -4770,7 +4661,7 @@ class Xls extends BaseReader
     private function readDataValidations(): void
     {
         $length = self::getUInt2d($this->data, $this->pos + 2);
-        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
+        //$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
 
         // move stream pointer forward to next record
         $this->pos += 4 + $length;
@@ -4804,7 +4695,7 @@ class Xls extends BaseReader
 
         // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
         // I have only seen cases where this is 1
-        $explicitFormula = (0x00000080 & $options) >> 7;
+        //$explicitFormula = (0x00000080 & $options) >> 7;
 
         // bit: 8; mask: 0x00000100; 1= empty cells allowed
         $allowBlank = (0x00000100 & $options) >> 8;
@@ -4883,7 +4774,7 @@ class Xls extends BaseReader
 
         try {
             $formula2 = $this->getFormulaFromStructure($formula2);
-        } catch (PhpSpreadsheetException $e) {
+        } catch (PhpSpreadsheetException) {
             return;
         }
         $offset += $sz2;
@@ -4924,9 +4815,6 @@ class Xls extends BaseReader
         // move stream pointer to next record
         $this->pos += 4 + $length;
 
-        // local pointer in record data
-        $offset = 0;
-
         if (!$this->readDataOnly) {
             // offset: 0; size: 2; repeated record identifier 0x0862
 
@@ -5093,7 +4981,7 @@ class Xls extends BaseReader
             for ($i = 0; $i < $cref; ++$i) {
                 try {
                     $cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
-                } catch (PhpSpreadsheetException $e) {
+                } catch (PhpSpreadsheetException) {
                     return;
                 }
                 $cellRanges[] = $cellRange;
@@ -5101,7 +4989,7 @@ class Xls extends BaseReader
             }
 
             // offset: var; size: var; variable length of feature specific data
-            $rgbFeat = substr($recordData, $offset);
+            //$rgbFeat = substr($recordData, $offset);
             $offset += 4;
 
             // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
@@ -5110,7 +4998,7 @@ class Xls extends BaseReader
 
             // Apply range protection to sheet
             if ($cellRanges) {
-                $this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
+                $this->phpSheet->protectCells(implode(' ', $cellRanges), ($wPassword === 0) ? '' : strtoupper(dechex($wPassword)), true);
             }
         }
     }
@@ -5168,10 +5056,8 @@ class Xls extends BaseReader
      * records are found. Splices the record data pieces and returns the combined string as if record data
      * is in one piece.
      * Moves to next current position in data stream to start of next record different from a CONtINUE record.
-     *
-     * @return array
      */
-    private function getSplicedRecordData()
+    private function getSplicedRecordData(): array
     {
         $data = '';
         $spliceOffsets = [];
@@ -5183,7 +5069,7 @@ class Xls extends BaseReader
             ++$i;
 
             // offset: 0; size: 2; identifier
-            $identifier = self::getUInt2d($this->data, $this->pos);
+            //$identifier = self::getUInt2d($this->data, $this->pos);
             // offset: 2; size: 2; length
             $length = self::getUInt2d($this->data, $this->pos + 2);
             $data .= $this->readRecordData($this->data, $this->pos + 4, $length);
@@ -5208,7 +5094,7 @@ class Xls extends BaseReader
      *
      * @return string Human readable formula
      */
-    private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
+    private function getFormulaFromStructure(string $formulaStructure, string $baseCell = 'A1'): string
     {
         // offset: 0; size: 2; size of the following formula data
         $sz = self::getUInt2d($formulaStructure, 0);
@@ -5235,12 +5121,12 @@ class Xls extends BaseReader
      *
      * @return string Human readable formula
      */
-    private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
+    private function getFormulaFromData(string $formulaData, string $additionalData = '', string $baseCell = 'A1'): string
     {
         // start parsing the formula data
         $tokens = [];
 
-        while (strlen($formulaData) > 0 && $token = $this->getNextToken($formulaData, $baseCell)) {
+        while ($formulaData !== '' && $token = $this->getNextToken($formulaData, $baseCell)) {
             $tokens[] = $token;
             $formulaData = substr($formulaData, $token['size']);
         }
@@ -5253,12 +5139,11 @@ class Xls extends BaseReader
     /**
      * Take array of tokens together with additional data for formula and return human readable formula.
      *
-     * @param array $tokens
      * @param string $additionalData Additional binary data going with the formula
      *
      * @return string Human readable formula
      */
-    private function createFormulaFromTokens($tokens, $additionalData)
+    private function createFormulaFromTokens(array $tokens, string $additionalData): string
     {
         // empty formula?
         if (empty($tokens)) {
@@ -5430,10 +5315,8 @@ class Xls extends BaseReader
      *
      * @param string $formulaData Formula data
      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
-     *
-     * @return array
      */
-    private function getNextToken($formulaData, $baseCell = 'A1')
+    private function getNextToken(string $formulaData, string $baseCell = 'A1'): array
     {
         // offset: 0; size: 1; token id
         $id = ord($formulaData[0]); // token id
@@ -5610,36 +5493,15 @@ class Xls extends BaseReader
                         $name = 'tAttrSpace';
                         $size = 4;
                         // offset: 2; size: 2; space type and position
-                        switch (ord($formulaData[2])) {
-                            case 0x00:
-                                $spacetype = 'type0';
-
-                                break;
-                            case 0x01:
-                                $spacetype = 'type1';
-
-                                break;
-                            case 0x02:
-                                $spacetype = 'type2';
-
-                                break;
-                            case 0x03:
-                                $spacetype = 'type3';
-
-                                break;
-                            case 0x04:
-                                $spacetype = 'type4';
-
-                                break;
-                            case 0x05:
-                                $spacetype = 'type5';
-
-                                break;
-                            default:
-                                throw new Exception('Unrecognized space type in tAttrSpace token');
-
-                                break;
-                        }
+                        $spacetype = match (ord($formulaData[2])) {
+                            0x00 => 'type0',
+                            0x01 => 'type1',
+                            0x02 => 'type2',
+                            0x03 => 'type3',
+                            0x04 => 'type4',
+                            0x05 => 'type5',
+                            default => throw new Exception('Unrecognized space type in tAttrSpace token'),
+                        };
                         // offset: 3; size: 1; number of inserted spaces/carriage returns
                         $spacecount = ord($formulaData[3]);
 
@@ -5648,8 +5510,6 @@ class Xls extends BaseReader
                         break;
                     default:
                         throw new Exception('Unrecognized attribute flag in tAttr token');
-
-                        break;
                 }
 
                 break;
@@ -6500,8 +6360,6 @@ class Xls extends BaseReader
                         break;
                     default:
                         throw new Exception('Unrecognized function in formula');
-
-                        break;
                 }
                 $data = ['function' => $function, 'args' => $args];
 
@@ -6515,364 +6373,97 @@ class Xls extends BaseReader
                 $args = ord($formulaData[1]);
                 // offset: 2: size: 2; index to built-in sheet function
                 $index = self::getUInt2d($formulaData, 2);
-                switch ($index) {
-                    case 0:
-                        $function = 'COUNT';
-
-                        break;
-                    case 1:
-                        $function = 'IF';
-
-                        break;
-                    case 4:
-                        $function = 'SUM';
-
-                        break;
-                    case 5:
-                        $function = 'AVERAGE';
-
-                        break;
-                    case 6:
-                        $function = 'MIN';
-
-                        break;
-                    case 7:
-                        $function = 'MAX';
-
-                        break;
-                    case 8:
-                        $function = 'ROW';
-
-                        break;
-                    case 9:
-                        $function = 'COLUMN';
-
-                        break;
-                    case 11:
-                        $function = 'NPV';
-
-                        break;
-                    case 12:
-                        $function = 'STDEV';
-
-                        break;
-                    case 13:
-                        $function = 'DOLLAR';
-
-                        break;
-                    case 14:
-                        $function = 'FIXED';
-
-                        break;
-                    case 28:
-                        $function = 'LOOKUP';
-
-                        break;
-                    case 29:
-                        $function = 'INDEX';
-
-                        break;
-                    case 36:
-                        $function = 'AND';
-
-                        break;
-                    case 37:
-                        $function = 'OR';
-
-                        break;
-                    case 46:
-                        $function = 'VAR';
-
-                        break;
-                    case 49:
-                        $function = 'LINEST';
-
-                        break;
-                    case 50:
-                        $function = 'TREND';
-
-                        break;
-                    case 51:
-                        $function = 'LOGEST';
-
-                        break;
-                    case 52:
-                        $function = 'GROWTH';
-
-                        break;
-                    case 56:
-                        $function = 'PV';
-
-                        break;
-                    case 57:
-                        $function = 'FV';
-
-                        break;
-                    case 58:
-                        $function = 'NPER';
-
-                        break;
-                    case 59:
-                        $function = 'PMT';
-
-                        break;
-                    case 60:
-                        $function = 'RATE';
-
-                        break;
-                    case 62:
-                        $function = 'IRR';
-
-                        break;
-                    case 64:
-                        $function = 'MATCH';
-
-                        break;
-                    case 70:
-                        $function = 'WEEKDAY';
-
-                        break;
-                    case 78:
-                        $function = 'OFFSET';
-
-                        break;
-                    case 82:
-                        $function = 'SEARCH';
-
-                        break;
-                    case 100:
-                        $function = 'CHOOSE';
-
-                        break;
-                    case 101:
-                        $function = 'HLOOKUP';
-
-                        break;
-                    case 102:
-                        $function = 'VLOOKUP';
-
-                        break;
-                    case 109:
-                        $function = 'LOG';
-
-                        break;
-                    case 115:
-                        $function = 'LEFT';
-
-                        break;
-                    case 116:
-                        $function = 'RIGHT';
-
-                        break;
-                    case 120:
-                        $function = 'SUBSTITUTE';
-
-                        break;
-                    case 124:
-                        $function = 'FIND';
-
-                        break;
-                    case 125:
-                        $function = 'CELL';
-
-                        break;
-                    case 144:
-                        $function = 'DDB';
-
-                        break;
-                    case 148:
-                        $function = 'INDIRECT';
-
-                        break;
-                    case 167:
-                        $function = 'IPMT';
-
-                        break;
-                    case 168:
-                        $function = 'PPMT';
-
-                        break;
-                    case 169:
-                        $function = 'COUNTA';
-
-                        break;
-                    case 183:
-                        $function = 'PRODUCT';
-
-                        break;
-                    case 193:
-                        $function = 'STDEVP';
-
-                        break;
-                    case 194:
-                        $function = 'VARP';
-
-                        break;
-                    case 197:
-                        $function = 'TRUNC';
-
-                        break;
-                    case 204:
-                        $function = 'USDOLLAR';
-
-                        break;
-                    case 205:
-                        $function = 'FINDB';
-
-                        break;
-                    case 206:
-                        $function = 'SEARCHB';
-
-                        break;
-                    case 208:
-                        $function = 'LEFTB';
-
-                        break;
-                    case 209:
-                        $function = 'RIGHTB';
-
-                        break;
-                    case 216:
-                        $function = 'RANK';
-
-                        break;
-                    case 219:
-                        $function = 'ADDRESS';
-
-                        break;
-                    case 220:
-                        $function = 'DAYS360';
-
-                        break;
-                    case 222:
-                        $function = 'VDB';
-
-                        break;
-                    case 227:
-                        $function = 'MEDIAN';
-
-                        break;
-                    case 228:
-                        $function = 'SUMPRODUCT';
-
-                        break;
-                    case 247:
-                        $function = 'DB';
-
-                        break;
-                    case 255:
-                        $function = '';
-
-                        break;
-                    case 269:
-                        $function = 'AVEDEV';
-
-                        break;
-                    case 270:
-                        $function = 'BETADIST';
-
-                        break;
-                    case 272:
-                        $function = 'BETAINV';
-
-                        break;
-                    case 317:
-                        $function = 'PROB';
-
-                        break;
-                    case 318:
-                        $function = 'DEVSQ';
-
-                        break;
-                    case 319:
-                        $function = 'GEOMEAN';
-
-                        break;
-                    case 320:
-                        $function = 'HARMEAN';
-
-                        break;
-                    case 321:
-                        $function = 'SUMSQ';
-
-                        break;
-                    case 322:
-                        $function = 'KURT';
-
-                        break;
-                    case 323:
-                        $function = 'SKEW';
-
-                        break;
-                    case 324:
-                        $function = 'ZTEST';
-
-                        break;
-                    case 329:
-                        $function = 'PERCENTRANK';
-
-                        break;
-                    case 330:
-                        $function = 'MODE';
-
-                        break;
-                    case 336:
-                        $function = 'CONCATENATE';
-
-                        break;
-                    case 344:
-                        $function = 'SUBTOTAL';
-
-                        break;
-                    case 345:
-                        $function = 'SUMIF';
-
-                        break;
-                    case 354:
-                        $function = 'ROMAN';
-
-                        break;
-                    case 358:
-                        $function = 'GETPIVOTDATA';
-
-                        break;
-                    case 359:
-                        $function = 'HYPERLINK';
-
-                        break;
-                    case 361:
-                        $function = 'AVERAGEA';
-
-                        break;
-                    case 362:
-                        $function = 'MAXA';
-
-                        break;
-                    case 363:
-                        $function = 'MINA';
-
-                        break;
-                    case 364:
-                        $function = 'STDEVPA';
-
-                        break;
-                    case 365:
-                        $function = 'VARPA';
-
-                        break;
-                    case 366:
-                        $function = 'STDEVA';
-
-                        break;
-                    case 367:
-                        $function = 'VARA';
-
-                        break;
-                    default:
-                        throw new Exception('Unrecognized function in formula');
-
-                        break;
-                }
+                $function = match ($index) {
+                    0 => 'COUNT',
+                    1 => 'IF',
+                    4 => 'SUM',
+                    5 => 'AVERAGE',
+                    6 => 'MIN',
+                    7 => 'MAX',
+                    8 => 'ROW',
+                    9 => 'COLUMN',
+                    11 => 'NPV',
+                    12 => 'STDEV',
+                    13 => 'DOLLAR',
+                    14 => 'FIXED',
+                    28 => 'LOOKUP',
+                    29 => 'INDEX',
+                    36 => 'AND',
+                    37 => 'OR',
+                    46 => 'VAR',
+                    49 => 'LINEST',
+                    50 => 'TREND',
+                    51 => 'LOGEST',
+                    52 => 'GROWTH',
+                    56 => 'PV',
+                    57 => 'FV',
+                    58 => 'NPER',
+                    59 => 'PMT',
+                    60 => 'RATE',
+                    62 => 'IRR',
+                    64 => 'MATCH',
+                    70 => 'WEEKDAY',
+                    78 => 'OFFSET',
+                    82 => 'SEARCH',
+                    100 => 'CHOOSE',
+                    101 => 'HLOOKUP',
+                    102 => 'VLOOKUP',
+                    109 => 'LOG',
+                    115 => 'LEFT',
+                    116 => 'RIGHT',
+                    120 => 'SUBSTITUTE',
+                    124 => 'FIND',
+                    125 => 'CELL',
+                    144 => 'DDB',
+                    148 => 'INDIRECT',
+                    167 => 'IPMT',
+                    168 => 'PPMT',
+                    169 => 'COUNTA',
+                    183 => 'PRODUCT',
+                    193 => 'STDEVP',
+                    194 => 'VARP',
+                    197 => 'TRUNC',
+                    204 => 'USDOLLAR',
+                    205 => 'FINDB',
+                    206 => 'SEARCHB',
+                    208 => 'LEFTB',
+                    209 => 'RIGHTB',
+                    216 => 'RANK',
+                    219 => 'ADDRESS',
+                    220 => 'DAYS360',
+                    222 => 'VDB',
+                    227 => 'MEDIAN',
+                    228 => 'SUMPRODUCT',
+                    247 => 'DB',
+                    255 => '',
+                    269 => 'AVEDEV',
+                    270 => 'BETADIST',
+                    272 => 'BETAINV',
+                    317 => 'PROB',
+                    318 => 'DEVSQ',
+                    319 => 'GEOMEAN',
+                    320 => 'HARMEAN',
+                    321 => 'SUMSQ',
+                    322 => 'KURT',
+                    323 => 'SKEW',
+                    324 => 'ZTEST',
+                    329 => 'PERCENTRANK',
+                    330 => 'MODE',
+                    336 => 'CONCATENATE',
+                    344 => 'SUBTOTAL',
+                    345 => 'SUMIF',
+                    354 => 'ROMAN',
+                    358 => 'GETPIVOTDATA',
+                    359 => 'HYPERLINK',
+                    361 => 'AVERAGEA',
+                    362 => 'MAXA',
+                    363 => 'MINA',
+                    364 => 'STDEVPA',
+                    365 => 'VARPA',
+                    366 => 'STDEVA',
+                    367 => 'VARA',
+                    default => throw new Exception('Unrecognized function in formula'),
+                };
                 $data = ['function' => $function, 'args' => $args];
 
                 break;
@@ -6961,6 +6552,7 @@ class Xls extends BaseReader
                 $index = self::getUInt2d($formulaData, 3);
                 // assume index is to EXTERNNAME record
                 $data = $this->externalNames[$index - 1]['name'] ?? '';
+
                 // offset: 5; size: 2; not used
                 break;
             case 0x3A:    //    3d reference to cell
@@ -6976,7 +6568,7 @@ class Xls extends BaseReader
                     $cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
 
                     $data = "$sheetRange!$cellAddress";
-                } catch (PhpSpreadsheetException $e) {
+                } catch (PhpSpreadsheetException) {
                     // deleted sheet reference
                     $data = '#REF!';
                 }
@@ -6995,7 +6587,7 @@ class Xls extends BaseReader
                     $cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
 
                     $data = "$sheetRange!$cellRangeAddress";
-                } catch (PhpSpreadsheetException $e) {
+                } catch (PhpSpreadsheetException) {
                     // deleted sheet reference
                     $data = '#REF!';
                 }
@@ -7004,8 +6596,6 @@ class Xls extends BaseReader
                 // Unknown cases    // don't know how to deal with
             default:
                 throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
-
-                break;
         }
 
         return [
@@ -7019,12 +6609,8 @@ class Xls extends BaseReader
     /**
      * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
      * section 3.3.4.
-     *
-     * @param string $cellAddressStructure
-     *
-     * @return string
      */
-    private function readBIFF8CellAddress($cellAddressStructure)
+    private function readBIFF8CellAddress(string $cellAddressStructure): string
     {
         // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
         $row = self::getUInt2d($cellAddressStructure, 0) + 1;
@@ -7050,12 +6636,9 @@ class Xls extends BaseReader
      * to indicate offsets from a base cell
      * section 3.3.4.
      *
-     * @param string $cellAddressStructure
      * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
-     *
-     * @return string
      */
-    private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
+    private function readBIFF8CellAddressB(string $cellAddressStructure, string $baseCell = 'A1'): string
     {
         [$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
         $baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
@@ -7098,12 +6681,8 @@ class Xls extends BaseReader
      * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
      * always fixed range
      * section 2.5.14.
-     *
-     * @param string $subData
-     *
-     * @return string
      */
-    private function readBIFF5CellRangeAddressFixed($subData)
+    private function readBIFF5CellRangeAddressFixed(string $subData): string
     {
         // offset: 0; size: 2; index to first row
         $fr = self::getUInt2d($subData, 0) + 1;
@@ -7137,12 +6716,8 @@ class Xls extends BaseReader
      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
      * always fixed range
      * section 2.5.14.
-     *
-     * @param string $subData
-     *
-     * @return string
      */
-    private function readBIFF8CellRangeAddressFixed($subData)
+    private function readBIFF8CellRangeAddressFixed(string $subData): string
     {
         // offset: 0; size: 2; index to first row
         $fr = self::getUInt2d($subData, 0) + 1;
@@ -7176,12 +6751,8 @@ class Xls extends BaseReader
      * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
      * there are flags indicating whether column/row index is relative
      * section 3.3.4.
-     *
-     * @param string $subData
-     *
-     * @return string
      */
-    private function readBIFF8CellRangeAddress($subData)
+    private function readBIFF8CellRangeAddress(string $subData): string
     {
         // todo: if cell range is just a single cell, should this funciton
         // not just return e.g. 'A1' and not 'A1:A1' ?
@@ -7230,12 +6801,11 @@ class Xls extends BaseReader
      * to indicate offsets from a base cell
      * section 3.3.4.
      *
-     * @param string $subData
      * @param string $baseCell Base cell
      *
      * @return string Cell range address
      */
-    private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
+    private function readBIFF8CellRangeAddressB(string $subData, string $baseCell = 'A1'): string
     {
         [$baseCol, $baseRow] = Coordinate::indexesFromString($baseCell);
         $baseCol = $baseCol - 1;
@@ -7315,12 +6885,8 @@ class Xls extends BaseReader
     /**
      * Read BIFF8 cell range address list
      * section 2.5.15.
-     *
-     * @param string $subData
-     *
-     * @return array
      */
-    private function readBIFF8CellRangeAddressList($subData)
+    private function readBIFF8CellRangeAddressList(string $subData): array
     {
         $cellRangeAddresses = [];
 
@@ -7343,12 +6909,8 @@ class Xls extends BaseReader
     /**
      * Read BIFF5 cell range address list
      * section 2.5.15.
-     *
-     * @param string $subData
-     *
-     * @return array
      */
-    private function readBIFF5CellRangeAddressList($subData)
+    private function readBIFF5CellRangeAddressList(string $subData): array
     {
         $cellRangeAddresses = [];
 
@@ -7373,12 +6935,8 @@ class Xls extends BaseReader
      * Note: If there is only one sheet in the range, one gets e.g Sheet1
      * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
      * in which case an Exception is thrown.
-     *
-     * @param int $index
-     *
-     * @return false|string
      */
-    private function readSheetRangeByRefIndex($index)
+    private function readSheetRangeByRefIndex(int $index): string|false
     {
         if (isset($this->ref[$index])) {
             $type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
@@ -7413,13 +6971,9 @@ class Xls extends BaseReader
                     }
 
                     return $sheetRange;
-
-                    break;
                 default:
                     // TODO: external sheet support
                     throw new Exception('Xls reader only supports internal sheets in formulas');
-
-                    break;
             }
         }
 
@@ -7430,12 +6984,8 @@ class Xls extends BaseReader
      * read BIFF8 constant value array from array data
      * returns e.g. ['value' => '{1,2;3,4}', 'size' => 40]
      * section 2.5.8.
-     *
-     * @param string $arrayData
-     *
-     * @return array
      */
-    private static function readBIFF8ConstantArray($arrayData)
+    private static function readBIFF8ConstantArray(string $arrayData): array
     {
         // offset: 0; size: 1; number of columns decreased by 1
         $nc = ord($arrayData[0]);
@@ -7469,12 +7019,8 @@ class Xls extends BaseReader
      * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
      * section 2.5.7
      * returns e.g. ['value' => '5', 'size' => 9].
-     *
-     * @param string $valueData
-     *
-     * @return array
      */
-    private static function readBIFF8Constant($valueData)
+    private static function readBIFF8Constant(string $valueData): array
     {
         // offset: 0; size: 1; identifier for type of constant
         $identifier = ord($valueData[0]);
@@ -7529,10 +7075,8 @@ class Xls extends BaseReader
      * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
      *
      * @param string $rgb Encoded RGB value (4 bytes)
-     *
-     * @return array
      */
-    private static function readRGB($rgb)
+    private static function readRGB(string $rgb): array
     {
         // offset: 0; size 1; Red component
         $r = ord($rgb[0]);
@@ -7552,12 +7096,8 @@ class Xls extends BaseReader
     /**
      * Read byte string (8-bit string length)
      * OpenOffice documentation: 2.5.2.
-     *
-     * @param string $subData
-     *
-     * @return array
      */
-    private function readByteStringShort($subData)
+    private function readByteStringShort(string $subData): array
     {
         // offset: 0; size: 1; length of the string (character count)
         $ln = ord($subData[0]);
@@ -7574,12 +7114,8 @@ class Xls extends BaseReader
     /**
      * Read byte string (16-bit string length)
      * OpenOffice documentation: 2.5.2.
-     *
-     * @param string $subData
-     *
-     * @return array
      */
-    private function readByteStringLong($subData)
+    private function readByteStringLong(string $subData): array
     {
         // offset: 0; size: 2; length of the string (character count)
         $ln = self::getUInt2d($subData, 0);
@@ -7598,15 +7134,9 @@ class Xls extends BaseReader
      * Extracts an Excel Unicode short string (8-bit string length)
      * OpenOffice documentation: 2.5.3
      * function will automatically find out where the Unicode string ends.
-     *
-     * @param string $subData
-     *
-     * @return array
      */
-    private static function readUnicodeStringShort($subData)
+    private static function readUnicodeStringShort(string $subData): array
     {
-        $value = '';
-
         // offset: 0: size: 1; length of the string (character count)
         $characterCount = ord($subData[0]);
 
@@ -7622,15 +7152,9 @@ class Xls extends BaseReader
      * Extracts an Excel Unicode long string (16-bit string length)
      * OpenOffice documentation: 2.5.3
      * this function is under construction, needs to support rich text, and Asian phonetic settings.
-     *
-     * @param string $subData
-     *
-     * @return array
      */
-    private static function readUnicodeStringLong($subData)
+    private static function readUnicodeStringLong(string $subData): array
     {
-        $value = '';
-
         // offset: 0: size: 2; length of the string (character count)
         $characterCount = self::getUInt2d($subData, 0);
 
@@ -7646,25 +7170,18 @@ class Xls extends BaseReader
      * Read Unicode string with no string length field, but with known character count
      * this function is under construction, needs to support rich text, and Asian phonetic settings
      * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
-     *
-     * @param string $subData
-     * @param int $characterCount
-     *
-     * @return array
      */
-    private static function readUnicodeString($subData, $characterCount)
+    private static function readUnicodeString(string $subData, int $characterCount): array
     {
-        $value = '';
-
         // offset: 0: size: 1; option flags
         // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
         $isCompressed = !((0x01 & ord($subData[0])) >> 0);
 
         // bit: 2; mask: 0x04; Asian phonetic settings
-        $hasAsian = (0x04) & ord($subData[0]) >> 2;
+        //$hasAsian = (0x04) & ord($subData[0]) >> 2;
 
         // bit: 3; mask: 0x08; Rich-Text settings
-        $hasRichText = (0x08) & ord($subData[0]) >> 3;
+        //$hasRichText = (0x08) & ord($subData[0]) >> 3;
 
         // offset: 1: size: var; character array
         // this offset assumes richtext and Asian phonetic settings are off which is generally wrong
@@ -7682,10 +7199,8 @@ class Xls extends BaseReader
      * Example:  hello"world  -->  "hello""world".
      *
      * @param string $value UTF-8 encoded string
-     *
-     * @return string
      */
-    private static function UTF8toExcelDoubleQuoted($value)
+    private static function UTF8toExcelDoubleQuoted(string $value): string
     {
         return '"' . str_replace('"', '""', $value) . '"';
     }
@@ -7694,25 +7209,25 @@ class Xls extends BaseReader
      * Reads first 8 bytes of a string and return IEEE 754 float.
      *
      * @param string $data Binary string that is at least 8 bytes long
-     *
-     * @return float
      */
-    private static function extractNumber($data)
+    private static function extractNumber(string $data): int|float
     {
         $rknumhigh = self::getInt4d($data, 4);
         $rknumlow = self::getInt4d($data, 0);
-        $sign = ($rknumhigh & (int) 0x80000000) >> 31;
-        $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
-        $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
-        $mantissalow1 = ($rknumlow & (int) 0x80000000) >> 31;
-        $mantissalow2 = ($rknumlow & 0x7fffffff);
+        $sign = ($rknumhigh & self::HIGH_ORDER_BIT) >> 31;
+        $exp = (($rknumhigh & 0x7FF00000) >> 20) - 1023;
+        $mantissa = (0x100000 | ($rknumhigh & 0x000FFFFF));
+        $mantissalow1 = ($rknumlow & self::HIGH_ORDER_BIT) >> 31;
+        $mantissalow2 = ($rknumlow & 0x7FFFFFFF);
         $value = $mantissa / 2 ** (20 - $exp);
 
         if ($mantissalow1 != 0) {
             $value += 1 / 2 ** (21 - $exp);
         }
 
-        $value += $mantissalow2 / 2 ** (52 - $exp);
+        if ($mantissalow2 != 0) {
+            $value += $mantissalow2 / 2 ** (52 - $exp);
+        }
         if ($sign) {
             $value *= -1;
         }
@@ -7720,12 +7235,7 @@ class Xls extends BaseReader
         return $value;
     }
 
-    /**
-     * @param int $rknum
-     *
-     * @return float
-     */
-    private static function getIEEE754($rknum)
+    private static function getIEEE754(int $rknum): float|int
     {
         if (($rknum & 0x02) != 0) {
             $value = $rknum >> 2;
@@ -7735,9 +7245,9 @@ class Xls extends BaseReader
             // The RK format calls for using only the most significant 30 bits
             // of the 64 bit floating point value. The other 34 bits are assumed
             // to be 0 so we use the upper 30 bits of $rknum as follows...
-            $sign = ($rknum & (int) 0x80000000) >> 31;
-            $exp = ($rknum & 0x7ff00000) >> 20;
-            $mantissa = (0x100000 | ($rknum & 0x000ffffc));
+            $sign = ($rknum & self::HIGH_ORDER_BIT) >> 31;
+            $exp = ($rknum & 0x7FF00000) >> 20;
+            $mantissa = (0x100000 | ($rknum & 0x000FFFFC));
             $value = $mantissa / 2 ** (20 - ($exp - 1023));
             if ($sign) {
                 $value = -1 * $value;
@@ -7753,13 +7263,8 @@ class Xls extends BaseReader
 
     /**
      * Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
-     *
-     * @param string $string
-     * @param bool $compressed
-     *
-     * @return string
      */
-    private static function encodeUTF16($string, $compressed = false)
+    private static function encodeUTF16(string $string, bool $compressed = false): string
     {
         if ($compressed) {
             $string = self::uncompressByteString($string);
@@ -7770,12 +7275,8 @@ class Xls extends BaseReader
 
     /**
      * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
-     *
-     * @param string $string
-     *
-     * @return string
      */
-    private static function uncompressByteString($string)
+    private static function uncompressByteString(string $string): string
     {
         $uncompressedString = '';
         $strLen = strlen($string);
@@ -7788,51 +7289,32 @@ class Xls extends BaseReader
 
     /**
      * Convert string to UTF-8. Only used for BIFF5.
-     *
-     * @param string $string
-     *
-     * @return string
      */
-    private function decodeCodepage($string)
+    private function decodeCodepage(string $string): string
     {
         return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
     }
 
     /**
      * Read 16-bit unsigned integer.
-     *
-     * @param string $data
-     * @param int $pos
-     *
-     * @return int
      */
-    public static function getUInt2d($data, $pos)
+    public static function getUInt2d(string $data, int $pos): int
     {
         return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
     }
 
     /**
      * Read 16-bit signed integer.
-     *
-     * @param string $data
-     * @param int $pos
-     *
-     * @return int
      */
-    public static function getInt2d($data, $pos)
+    public static function getInt2d(string $data, int $pos): int
     {
-        return unpack('s', $data[$pos] . $data[$pos + 1])[1];
+        return unpack('s', $data[$pos] . $data[$pos + 1])[1]; // @phpstan-ignore-line
     }
 
     /**
      * Read 32-bit signed integer.
-     *
-     * @param string $data
-     * @param int $pos
-     *
-     * @return int
      */
-    public static function getInt4d($data, $pos)
+    public static function getInt4d(string $data, int $pos): int
     {
         // FIX: represent numbers correctly on 64-bit system
         // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
@@ -7848,7 +7330,7 @@ class Xls extends BaseReader
         return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
     }
 
-    private function parseRichText($is)
+    private function parseRichText(string $is): RichText
     {
         $value = new RichText();
         $value->createText($is);
@@ -7869,6 +7351,11 @@ class Xls extends BaseReader
         return $this->mapCellStyleXfIndex;
     }
 
+    /**
+     * Parse conditional formatting blocks.
+     *
+     * @see https://www.openoffice.org/sc/excelfileformat.pdf Search for CFHEADER followed by CFRULE
+     */
     private function readCFHeader(): array
     {
         $length = self::getUInt2d($this->data, $this->pos + 2);
@@ -7929,14 +7416,20 @@ class Xls extends BaseReader
         // offset: 6; size: 4; Options
         $options = self::getInt4d($recordData, 6);
 
-        $style = new Style();
-        $this->getCFStyleOptions($options, $style);
+        $style = new Style(false, true); // non-supervisor, conditional
+        $noFormatSet = true;
+        //$this->getCFStyleOptions($options, $style);
 
         $hasFontRecord = (bool) ((0x04000000 & $options) >> 26);
         $hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27);
         $hasBorderRecord = (bool) ((0x10000000 & $options) >> 28);
         $hasFillRecord = (bool) ((0x20000000 & $options) >> 29);
         $hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30);
+        // note unexpected values for following 4
+        $hasBorderLeft = !(bool) (0x00000400 & $options);
+        $hasBorderRight = !(bool) (0x00000800 & $options);
+        $hasBorderTop = !(bool) (0x00001000 & $options);
+        $hasBorderBottom = !(bool) (0x00002000 & $options);
 
         $offset = 12;
 
@@ -7944,29 +7437,32 @@ class Xls extends BaseReader
             $fontStyle = substr($recordData, $offset, 118);
             $this->getCFFontStyle($fontStyle, $style);
             $offset += 118;
+            $noFormatSet = false;
         }
 
         if ($hasAlignmentRecord === true) {
-            $alignmentStyle = substr($recordData, $offset, 8);
-            $this->getCFAlignmentStyle($alignmentStyle, $style);
+            //$alignmentStyle = substr($recordData, $offset, 8);
+            //$this->getCFAlignmentStyle($alignmentStyle, $style);
             $offset += 8;
         }
 
         if ($hasBorderRecord === true) {
             $borderStyle = substr($recordData, $offset, 8);
-            $this->getCFBorderStyle($borderStyle, $style);
+            $this->getCFBorderStyle($borderStyle, $style, $hasBorderLeft, $hasBorderRight, $hasBorderTop, $hasBorderBottom);
             $offset += 8;
+            $noFormatSet = false;
         }
 
         if ($hasFillRecord === true) {
             $fillStyle = substr($recordData, $offset, 4);
             $this->getCFFillStyle($fillStyle, $style);
             $offset += 4;
+            $noFormatSet = false;
         }
 
         if ($hasProtectionRecord === true) {
-            $protectionStyle = substr($recordData, $offset, 4);
-            $this->getCFProtectionStyle($protectionStyle, $style);
+            //$protectionStyle = substr($recordData, $offset, 4);
+            //$this->getCFProtectionStyle($protectionStyle, $style);
             $offset += 2;
         }
 
@@ -7989,12 +7485,12 @@ class Xls extends BaseReader
             $offset += $size2;
         }
 
-        $this->setCFRules($cellRangeAddresses, $type, $operator, $formula1, $formula2, $style);
+        $this->setCFRules($cellRangeAddresses, $type, $operator, $formula1, $formula2, $style, $noFormatSet);
     }
 
-    private function getCFStyleOptions(int $options, Style $style): void
+    /*private function getCFStyleOptions(int $options, Style $style): void
     {
-    }
+    }*/
 
     private function getCFFontStyle(string $options, Style $style): void
     {
@@ -8002,9 +7498,23 @@ class Xls extends BaseReader
         if ($fontSize !== -1) {
             $style->getFont()->setSize($fontSize / 20); // Convert twips to points
         }
+        $options68 = self::getInt4d($options, 68);
+        $options88 = self::getInt4d($options, 88);
 
-        $bold = self::getUInt2d($options, 72) === 700; // 400 = normal, 700 = bold
-        $style->getFont()->setBold($bold);
+        if (($options88 & 2) === 0) {
+            $bold = self::getUInt2d($options, 72); // 400 = normal, 700 = bold
+            if ($bold !== 0) {
+                $style->getFont()->setBold($bold >= 550);
+            }
+            if (($options68 & 2) !== 0) {
+                $style->getFont()->setItalic(true);
+            }
+        }
+        if (($options88 & 0x80) === 0) {
+            if (($options68 & 0x80) !== 0) {
+                $style->getFont()->setStrikethrough(true);
+            }
+        }
 
         $color = self::getInt4d($options, 80);
 
@@ -8013,12 +7523,48 @@ class Xls extends BaseReader
         }
     }
 
-    private function getCFAlignmentStyle(string $options, Style $style): void
+    /*private function getCFAlignmentStyle(string $options, Style $style): void
     {
-    }
+    }*/
 
-    private function getCFBorderStyle(string $options, Style $style): void
+    private function getCFBorderStyle(string $options, Style $style, bool $hasBorderLeft, bool $hasBorderRight, bool $hasBorderTop, bool $hasBorderBottom): void
     {
+        $valueArray = unpack('V', $options);
+        $value = is_array($valueArray) ? $valueArray[1] : 0;
+        $left = $value & 15;
+        $right = ($value >> 4) & 15;
+        $top = ($value >> 8) & 15;
+        $bottom = ($value >> 12) & 15;
+        $leftc = ($value >> 16) & 0x7F;
+        $rightc = ($value >> 23) & 0x7F;
+        $valueArray = unpack('V', substr($options, 4));
+        $value = is_array($valueArray) ? $valueArray[1] : 0;
+        $topc = $value & 0x7F;
+        $bottomc = ($value & 0x3F80) >> 7;
+        if ($hasBorderLeft) {
+            $style->getBorders()->getLeft()
+                ->setBorderStyle(self::BORDER_STYLE_MAP[$left]);
+            $style->getBorders()->getLeft()->getColor()
+                ->setRGB(Xls\Color::map($leftc, $this->palette, $this->version)['rgb']);
+        }
+        if ($hasBorderRight) {
+            $style->getBorders()->getRight()
+                ->setBorderStyle(self::BORDER_STYLE_MAP[$right]);
+            $style->getBorders()->getRight()->getColor()
+                ->setRGB(Xls\Color::map($rightc, $this->palette, $this->version)['rgb']);
+        }
+        if ($hasBorderTop) {
+            $style->getBorders()->getTop()
+                ->setBorderStyle(self::BORDER_STYLE_MAP[$top]);
+            $style->getBorders()->getTop()->getColor()
+                ->setRGB(Xls\Color::map($topc, $this->palette, $this->version)['rgb']);
+        }
+        if ($hasBorderBottom) {
+            $style->getBorders()->getBottom()
+                ->setBorderStyle(self::BORDER_STYLE_MAP[$bottom]);
+            $style->getBorders()->getBottom()->getColor()
+                ->setRGB(Xls\Color::map($bottomc, $this->palette, $this->version)['rgb']);
+        }
     }
 
     private function getCFFillStyle(string $options, Style $style): void
@@ -8036,22 +7582,23 @@ class Xls extends BaseReader
 
             // bit: 0-6; mask: 0x007F; type
             $color1 = (0x007F & $fillColors) >> 0;
-            $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color1, $this->palette, $this->version)['rgb']);
 
             // bit: 7-13; mask: 0x3F80; type
             $color2 = (0x3F80 & $fillColors) >> 7;
-            $style->getFill()->getEndColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']);
+            if ($fillPattern === Fill::FILL_SOLID) {
+                $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']);
+            } else {
+                $style->getFill()->getStartColor()->setRGB(Xls\Color::map($color1, $this->palette, $this->version)['rgb']);
+                $style->getFill()->getEndColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']);
+            }
         }
     }
 
-    private function getCFProtectionStyle(string $options, Style $style): void
+    /*private function getCFProtectionStyle(string $options, Style $style): void
     {
-    }
+    }*/
 
-    /**
-     * @return null|float|int|string
-     */
-    private function readCFFormula(string $recordData, int $offset, int $size)
+    private function readCFFormula(string $recordData, int $offset, int $size): float|int|string|null
     {
         try {
             $formula = substr($recordData, $offset, $size);
@@ -8059,26 +7606,23 @@ class Xls extends BaseReader
 
             $formula = $this->getFormulaFromStructure($formula);
             if (is_numeric($formula)) {
-                return (strpos($formula, '.') !== false) ? (float) $formula : (int) $formula;
+                return (str_contains($formula, '.')) ? (float) $formula : (int) $formula;
             }
 
             return $formula;
-        } catch (PhpSpreadsheetException $e) {
+        } catch (PhpSpreadsheetException) {
+            return null;
         }
-
-        return null;
     }
 
-    /**
-     * @param null|float|int|string $formula1
-     * @param null|float|int|string $formula2
-     */
-    private function setCFRules(array $cellRanges, string $type, string $operator, $formula1, $formula2, Style $style): void
+    private function setCFRules(array $cellRanges, string $type, string $operator, null|float|int|string $formula1, null|float|int|string $formula2, Style $style, bool $noFormatSet): void
     {
         foreach ($cellRanges as $cellRange) {
             $conditional = new Conditional();
+            $conditional->setNoFormatSet($noFormatSet);
             $conditional->setConditionType($type);
             $conditional->setOperatorType($operator);
+            $conditional->setStopIfTrue(true);
             if ($formula1 !== null) {
                 $conditional->addCondition($formula1);
             }
@@ -8093,4 +7637,9 @@ class Xls extends BaseReader
             $this->phpSheet->getStyle($cellRange)->setConditionalStyles($conditionalStyles);
         }
     }
+
+    public function getVersion(): int
+    {
+        return $this->version;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
index 06c2d0b..6fd346b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
@@ -11,11 +11,10 @@ class Color
      *
      * @param int $color Indexed color
      * @param array $palette Color palette
-     * @param int $version
      *
      * @return array RGB color value, example: ['rgb' => 'FF0000']
      */
-    public static function map($color, $palette, $version)
+    public static function map(int $color, array $palette, int $version): array
     {
         if ($color <= 0x07 || $color >= 0x40) {
             // special built-in color
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
index 15d0b73..2c0790c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
@@ -65,12 +65,8 @@ class BIFF5
 
     /**
      * Map color array from BIFF5 built-in color index.
-     *
-     * @param int $color
-     *
-     * @return array
      */
-    public static function lookup($color)
+    public static function lookup(int $color): array
     {
         return ['rgb' => self::BIFF5_COLOR_MAP[$color] ?? '000000'];
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
index 019ec79..914034d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
@@ -65,12 +65,8 @@ class BIFF8
 
     /**
      * Map color array from BIFF8 built-in color index.
-     *
-     * @param int $color
-     *
-     * @return array
      */
-    public static function lookup($color)
+    public static function lookup(int $color): array
     {
         return ['rgb' => self::BIFF8_COLOR_MAP[$color] ?? '000000'];
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
index b6a96af..a715b11 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
@@ -21,10 +21,8 @@ class BuiltIn
      * Map built-in color to RGB value.
      *
      * @param int $color Indexed color
-     *
-     * @return array
      */
-    public static function lookup($color)
+    public static function lookup(int $color): array
     {
         return ['rgb' => self::BUILTIN_COLOR_MAP[$color] ?? '000000'];
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
index 8400efb..fbd31d5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php
@@ -9,7 +9,7 @@ class ConditionalFormatting
     /**
      * @var array<int, string>
      */
-    private static $types = [
+    private static array $types = [
         0x01 => Conditional::CONDITION_CELLIS,
         0x02 => Conditional::CONDITION_EXPRESSION,
     ];
@@ -17,7 +17,7 @@ class ConditionalFormatting
     /**
      * @var array<int, string>
      */
-    private static $operators = [
+    private static array $operators = [
         0x00 => Conditional::OPERATOR_NONE,
         0x01 => Conditional::OPERATOR_BETWEEN,
         0x02 => Conditional::OPERATOR_NOTBETWEEN,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
index 02f844e..874e699 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php
@@ -9,7 +9,7 @@ class DataValidationHelper
     /**
      * @var array<int, string>
      */
-    private static $types = [
+    private static array $types = [
         0x00 => DataValidation::TYPE_NONE,
         0x01 => DataValidation::TYPE_WHOLE,
         0x02 => DataValidation::TYPE_DECIMAL,
@@ -23,7 +23,7 @@ class DataValidationHelper
     /**
      * @var array<int, string>
      */
-    private static $errorStyles = [
+    private static array $errorStyles = [
         0x00 => DataValidation::STYLE_STOP,
         0x01 => DataValidation::STYLE_WARNING,
         0x02 => DataValidation::STYLE_INFORMATION,
@@ -32,7 +32,7 @@ class DataValidationHelper
     /**
      * @var array<int, string>
      */
-    private static $operators = [
+    private static array $operators = [
         0x00 => DataValidation::OPERATOR_BETWEEN,
         0x01 => DataValidation::OPERATOR_NOTBETWEEN,
         0x02 => DataValidation::OPERATOR_EQUAL,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
index 0b79366..fa8f8cd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
@@ -16,12 +16,8 @@ class ErrorCode
 
     /**
      * Map error code, e.g. '#N/A'.
-     *
-     * @param int $code
-     *
-     * @return bool|string
      */
-    public static function lookup($code)
+    public static function lookup(int $code): string|bool
     {
         return self::ERROR_CODE_MAP[$code] ?? false;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
index e9c95c8..094c19e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
@@ -35,38 +35,28 @@ class Escher
 
     /**
      * Escher stream data (binary).
-     *
-     * @var string
      */
-    private $data;
+    private string $data;
 
     /**
      * Size in bytes of the Escher stream data.
-     *
-     * @var int
      */
-    private $dataSize;
+    private int $dataSize;
 
     /**
      * Current position of stream pointer in Escher stream data.
-     *
-     * @var int
      */
-    private $pos;
+    private int $pos;
 
     /**
      * The object to be returned by the reader. Modified during load.
-     *
-     * @var BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
      */
-    private $object;
+    private BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer $object;
 
     /**
      * Create a new Escher instance.
-     *
-     * @param mixed $object
      */
-    public function __construct($object)
+    public function __construct(BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer $object)
     {
         $this->object = $object;
     }
@@ -94,12 +84,8 @@ class Escher
 
     /**
      * Load Escher stream data. May be a partial Escher stream.
-     *
-     * @param string $data
-     *
-     * @return BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
      */
-    public function load($data)
+    public function load(string $data): BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
     {
         $this->data = $data;
 
@@ -553,10 +539,7 @@ class Escher
         $this->applyAttribute('setEndOffsetY', $endOffsetY);
     }
 
-    /**
-     * @param mixed $value
-     */
-    private function applyAttribute(string $name, $value): void
+    private function applyAttribute(string $name, mixed $value): void
     {
         if (method_exists($this->object, $name)) {
             $this->object->$name($value);
@@ -581,7 +564,7 @@ class Escher
      * @param string $data Binary data
      * @param int $n Number of properties
      */
-    private function readOfficeArtRGFOPTE($data, $n): void
+    private function readOfficeArtRGFOPTE(string $data, int $n): void
     {
         $splicedComplexData = substr($data, 6 * $n);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
index d376c45..7da2eee 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
@@ -4,37 +4,22 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
 
 class MD5
 {
-    /**
-     * @var int
-     */
-    private $a;
+    private int $a;
 
-    /**
-     * @var int
-     */
-    private $b;
+    private int $b;
 
-    /**
-     * @var int
-     */
-    private $c;
+    private int $c;
 
-    /**
-     * @var int
-     */
-    private $d;
+    private int $d;
 
-    /**
-     * @var int
-     */
-    private static $allOneBits;
+    private static int $allOneBits;
 
     /**
      * MD5 stream constructor.
      */
     public function __construct()
     {
-        self::$allOneBits = self::signedInt(0xffffffff);
+        self::$allOneBits = self::signedInt(0xFFFFFFFF);
         $this->reset();
     }
 
@@ -51,18 +36,16 @@ class MD5
 
     /**
      * Get MD5 stream context.
-     *
-     * @return string
      */
-    public function getContext()
+    public function getContext(): string
     {
         $s = '';
         foreach (['a', 'b', 'c', 'd'] as $i) {
             $v = $this->{$i};
-            $s .= chr($v & 0xff);
-            $s .= chr(($v >> 8) & 0xff);
-            $s .= chr(($v >> 16) & 0xff);
-            $s .= chr(($v >> 24) & 0xff);
+            $s .= chr($v & 0xFF);
+            $s .= chr(($v >> 8) & 0xFF);
+            $s .= chr(($v >> 16) & 0xFF);
+            $s .= chr(($v >> 24) & 0xFF);
         }
 
         return $s;
@@ -89,76 +72,76 @@ class MD5
         $I = [self::class, 'i'];
 
         // ROUND 1
-        self::step($F, $A, $B, $C, $D, $words[0], 7, 0xd76aa478);
-        self::step($F, $D, $A, $B, $C, $words[1], 12, 0xe8c7b756);
-        self::step($F, $C, $D, $A, $B, $words[2], 17, 0x242070db);
-        self::step($F, $B, $C, $D, $A, $words[3], 22, 0xc1bdceee);
-        self::step($F, $A, $B, $C, $D, $words[4], 7, 0xf57c0faf);
-        self::step($F, $D, $A, $B, $C, $words[5], 12, 0x4787c62a);
-        self::step($F, $C, $D, $A, $B, $words[6], 17, 0xa8304613);
-        self::step($F, $B, $C, $D, $A, $words[7], 22, 0xfd469501);
-        self::step($F, $A, $B, $C, $D, $words[8], 7, 0x698098d8);
-        self::step($F, $D, $A, $B, $C, $words[9], 12, 0x8b44f7af);
-        self::step($F, $C, $D, $A, $B, $words[10], 17, 0xffff5bb1);
-        self::step($F, $B, $C, $D, $A, $words[11], 22, 0x895cd7be);
-        self::step($F, $A, $B, $C, $D, $words[12], 7, 0x6b901122);
-        self::step($F, $D, $A, $B, $C, $words[13], 12, 0xfd987193);
-        self::step($F, $C, $D, $A, $B, $words[14], 17, 0xa679438e);
-        self::step($F, $B, $C, $D, $A, $words[15], 22, 0x49b40821);
+        self::step($F, $A, $B, $C, $D, $words[0], 7, 0xD76AA478);
+        self::step($F, $D, $A, $B, $C, $words[1], 12, 0xE8C7B756);
+        self::step($F, $C, $D, $A, $B, $words[2], 17, 0x242070DB);
+        self::step($F, $B, $C, $D, $A, $words[3], 22, 0xC1BDCEEE);
+        self::step($F, $A, $B, $C, $D, $words[4], 7, 0xF57C0FAF);
+        self::step($F, $D, $A, $B, $C, $words[5], 12, 0x4787C62A);
+        self::step($F, $C, $D, $A, $B, $words[6], 17, 0xA8304613);
+        self::step($F, $B, $C, $D, $A, $words[7], 22, 0xFD469501);
+        self::step($F, $A, $B, $C, $D, $words[8], 7, 0x698098D8);
+        self::step($F, $D, $A, $B, $C, $words[9], 12, 0x8B44F7AF);
+        self::step($F, $C, $D, $A, $B, $words[10], 17, 0xFFFF5BB1);
+        self::step($F, $B, $C, $D, $A, $words[11], 22, 0x895CD7BE);
+        self::step($F, $A, $B, $C, $D, $words[12], 7, 0x6B901122);
+        self::step($F, $D, $A, $B, $C, $words[13], 12, 0xFD987193);
+        self::step($F, $C, $D, $A, $B, $words[14], 17, 0xA679438E);
+        self::step($F, $B, $C, $D, $A, $words[15], 22, 0x49B40821);
 
         // ROUND 2
-        self::step($G, $A, $B, $C, $D, $words[1], 5, 0xf61e2562);
-        self::step($G, $D, $A, $B, $C, $words[6], 9, 0xc040b340);
-        self::step($G, $C, $D, $A, $B, $words[11], 14, 0x265e5a51);
-        self::step($G, $B, $C, $D, $A, $words[0], 20, 0xe9b6c7aa);
-        self::step($G, $A, $B, $C, $D, $words[5], 5, 0xd62f105d);
+        self::step($G, $A, $B, $C, $D, $words[1], 5, 0xF61E2562);
+        self::step($G, $D, $A, $B, $C, $words[6], 9, 0xC040B340);
+        self::step($G, $C, $D, $A, $B, $words[11], 14, 0x265E5A51);
+        self::step($G, $B, $C, $D, $A, $words[0], 20, 0xE9B6C7AA);
+        self::step($G, $A, $B, $C, $D, $words[5], 5, 0xD62F105D);
         self::step($G, $D, $A, $B, $C, $words[10], 9, 0x02441453);
-        self::step($G, $C, $D, $A, $B, $words[15], 14, 0xd8a1e681);
-        self::step($G, $B, $C, $D, $A, $words[4], 20, 0xe7d3fbc8);
-        self::step($G, $A, $B, $C, $D, $words[9], 5, 0x21e1cde6);
-        self::step($G, $D, $A, $B, $C, $words[14], 9, 0xc33707d6);
-        self::step($G, $C, $D, $A, $B, $words[3], 14, 0xf4d50d87);
-        self::step($G, $B, $C, $D, $A, $words[8], 20, 0x455a14ed);
-        self::step($G, $A, $B, $C, $D, $words[13], 5, 0xa9e3e905);
-        self::step($G, $D, $A, $B, $C, $words[2], 9, 0xfcefa3f8);
-        self::step($G, $C, $D, $A, $B, $words[7], 14, 0x676f02d9);
-        self::step($G, $B, $C, $D, $A, $words[12], 20, 0x8d2a4c8a);
+        self::step($G, $C, $D, $A, $B, $words[15], 14, 0xD8A1E681);
+        self::step($G, $B, $C, $D, $A, $words[4], 20, 0xE7D3FBC8);
+        self::step($G, $A, $B, $C, $D, $words[9], 5, 0x21E1CDE6);
+        self::step($G, $D, $A, $B, $C, $words[14], 9, 0xC33707D6);
+        self::step($G, $C, $D, $A, $B, $words[3], 14, 0xF4D50D87);
+        self::step($G, $B, $C, $D, $A, $words[8], 20, 0x455A14ED);
+        self::step($G, $A, $B, $C, $D, $words[13], 5, 0xA9E3E905);
+        self::step($G, $D, $A, $B, $C, $words[2], 9, 0xFCEFA3F8);
+        self::step($G, $C, $D, $A, $B, $words[7], 14, 0x676F02D9);
+        self::step($G, $B, $C, $D, $A, $words[12], 20, 0x8D2A4C8A);
 
         // ROUND 3
-        self::step($H, $A, $B, $C, $D, $words[5], 4, 0xfffa3942);
-        self::step($H, $D, $A, $B, $C, $words[8], 11, 0x8771f681);
-        self::step($H, $C, $D, $A, $B, $words[11], 16, 0x6d9d6122);
-        self::step($H, $B, $C, $D, $A, $words[14], 23, 0xfde5380c);
-        self::step($H, $A, $B, $C, $D, $words[1], 4, 0xa4beea44);
-        self::step($H, $D, $A, $B, $C, $words[4], 11, 0x4bdecfa9);
-        self::step($H, $C, $D, $A, $B, $words[7], 16, 0xf6bb4b60);
-        self::step($H, $B, $C, $D, $A, $words[10], 23, 0xbebfbc70);
-        self::step($H, $A, $B, $C, $D, $words[13], 4, 0x289b7ec6);
-        self::step($H, $D, $A, $B, $C, $words[0], 11, 0xeaa127fa);
-        self::step($H, $C, $D, $A, $B, $words[3], 16, 0xd4ef3085);
-        self::step($H, $B, $C, $D, $A, $words[6], 23, 0x04881d05);
-        self::step($H, $A, $B, $C, $D, $words[9], 4, 0xd9d4d039);
-        self::step($H, $D, $A, $B, $C, $words[12], 11, 0xe6db99e5);
-        self::step($H, $C, $D, $A, $B, $words[15], 16, 0x1fa27cf8);
-        self::step($H, $B, $C, $D, $A, $words[2], 23, 0xc4ac5665);
+        self::step($H, $A, $B, $C, $D, $words[5], 4, 0xFFFA3942);
+        self::step($H, $D, $A, $B, $C, $words[8], 11, 0x8771F681);
+        self::step($H, $C, $D, $A, $B, $words[11], 16, 0x6D9D6122);
+        self::step($H, $B, $C, $D, $A, $words[14], 23, 0xFDE5380C);
+        self::step($H, $A, $B, $C, $D, $words[1], 4, 0xA4BEEA44);
+        self::step($H, $D, $A, $B, $C, $words[4], 11, 0x4BDECFA9);
+        self::step($H, $C, $D, $A, $B, $words[7], 16, 0xF6BB4B60);
+        self::step($H, $B, $C, $D, $A, $words[10], 23, 0xBEBFBC70);
+        self::step($H, $A, $B, $C, $D, $words[13], 4, 0x289B7EC6);
+        self::step($H, $D, $A, $B, $C, $words[0], 11, 0xEAA127FA);
+        self::step($H, $C, $D, $A, $B, $words[3], 16, 0xD4EF3085);
+        self::step($H, $B, $C, $D, $A, $words[6], 23, 0x04881D05);
+        self::step($H, $A, $B, $C, $D, $words[9], 4, 0xD9D4D039);
+        self::step($H, $D, $A, $B, $C, $words[12], 11, 0xE6DB99E5);
+        self::step($H, $C, $D, $A, $B, $words[15], 16, 0x1FA27CF8);
+        self::step($H, $B, $C, $D, $A, $words[2], 23, 0xC4AC5665);
 
         // ROUND 4
-        self::step($I, $A, $B, $C, $D, $words[0], 6, 0xf4292244);
-        self::step($I, $D, $A, $B, $C, $words[7], 10, 0x432aff97);
-        self::step($I, $C, $D, $A, $B, $words[14], 15, 0xab9423a7);
-        self::step($I, $B, $C, $D, $A, $words[5], 21, 0xfc93a039);
-        self::step($I, $A, $B, $C, $D, $words[12], 6, 0x655b59c3);
-        self::step($I, $D, $A, $B, $C, $words[3], 10, 0x8f0ccc92);
-        self::step($I, $C, $D, $A, $B, $words[10], 15, 0xffeff47d);
-        self::step($I, $B, $C, $D, $A, $words[1], 21, 0x85845dd1);
-        self::step($I, $A, $B, $C, $D, $words[8], 6, 0x6fa87e4f);
-        self::step($I, $D, $A, $B, $C, $words[15], 10, 0xfe2ce6e0);
-        self::step($I, $C, $D, $A, $B, $words[6], 15, 0xa3014314);
-        self::step($I, $B, $C, $D, $A, $words[13], 21, 0x4e0811a1);
-        self::step($I, $A, $B, $C, $D, $words[4], 6, 0xf7537e82);
-        self::step($I, $D, $A, $B, $C, $words[11], 10, 0xbd3af235);
-        self::step($I, $C, $D, $A, $B, $words[2], 15, 0x2ad7d2bb);
-        self::step($I, $B, $C, $D, $A, $words[9], 21, 0xeb86d391);
+        self::step($I, $A, $B, $C, $D, $words[0], 6, 0xF4292244);
+        self::step($I, $D, $A, $B, $C, $words[7], 10, 0x432AFF97);
+        self::step($I, $C, $D, $A, $B, $words[14], 15, 0xAB9423A7);
+        self::step($I, $B, $C, $D, $A, $words[5], 21, 0xFC93A039);
+        self::step($I, $A, $B, $C, $D, $words[12], 6, 0x655B59C3);
+        self::step($I, $D, $A, $B, $C, $words[3], 10, 0x8F0CCC92);
+        self::step($I, $C, $D, $A, $B, $words[10], 15, 0xFFEFF47D);
+        self::step($I, $B, $C, $D, $A, $words[1], 21, 0x85845DD1);
+        self::step($I, $A, $B, $C, $D, $words[8], 6, 0x6FA87E4F);
+        self::step($I, $D, $A, $B, $C, $words[15], 10, 0xFE2CE6E0);
+        self::step($I, $C, $D, $A, $B, $words[6], 15, 0xA3014314);
+        self::step($I, $B, $C, $D, $A, $words[13], 21, 0x4E0811A1);
+        self::step($I, $A, $B, $C, $D, $words[4], 6, 0xF7537E82);
+        self::step($I, $D, $A, $B, $C, $words[11], 10, 0xBD3AF235);
+        self::step($I, $C, $D, $A, $B, $words[2], 15, 0x2AD7D2BB);
+        self::step($I, $B, $C, $D, $A, $words[9], 21, 0xEB86D391);
 
         $this->a = ($this->a + $A) & self::$allOneBits;
         $this->b = ($this->b + $B) & self::$allOneBits;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
index b7c7c90..663f367 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
@@ -5,20 +5,18 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
 class RC4
 {
     /** @var int[] */
-    protected $s = []; // Context
+    protected array $s = []; // Context
 
-    /** @var int */
-    protected $i = 0;
+    protected int $i = 0;
 
-    /** @var int */
-    protected $j = 0;
+    protected int $j = 0;
 
     /**
      * RC4 stream decryption/encryption constrcutor.
      *
      * @param string $key Encryption key/passphrase
      */
-    public function __construct($key)
+    public function __construct(string $key)
     {
         $len = strlen($key);
 
@@ -40,10 +38,8 @@ class RC4
      * Symmetric decryption/encryption function.
      *
      * @param string $data Data to encrypt/decrypt
-     *
-     * @return string
      */
-    public function RC4($data)
+    public function RC4(string $data): string
     {
         $len = strlen($data);
         for ($c = 0; $c < $len; ++$c) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
index d832eb0..97cebbd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
@@ -9,7 +9,7 @@ class Border
     /**
      * @var array<int, string>
      */
-    protected static $borderStyleMap = [
+    protected static array $borderStyleMap = [
         0x00 => StyleBorder::BORDER_NONE,
         0x01 => StyleBorder::BORDER_THIN,
         0x02 => StyleBorder::BORDER_MEDIUM,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php
index f03d47f..6b89d27 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php
@@ -9,7 +9,7 @@ class CellAlignment
     /**
      * @var array<int, string>
      */
-    protected static $horizontalAlignmentMap = [
+    protected static array $horizontalAlignmentMap = [
         0 => Alignment::HORIZONTAL_GENERAL,
         1 => Alignment::HORIZONTAL_LEFT,
         2 => Alignment::HORIZONTAL_CENTER,
@@ -22,7 +22,7 @@ class CellAlignment
     /**
      * @var array<int, string>
      */
-    protected static $verticalAlignmentMap = [
+    protected static array $verticalAlignmentMap = [
         0 => Alignment::VERTICAL_TOP,
         1 => Alignment::VERTICAL_CENTER,
         2 => Alignment::VERTICAL_BOTTOM,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php
index e975be4..2c6d2f7 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php
@@ -23,7 +23,7 @@ class CellFont
     /**
      * @var array<int, string>
      */
-    protected static $underlineMap = [
+    protected static array $underlineMap = [
         0x01 => Font::UNDERLINE_SINGLE,
         0x02 => Font::UNDERLINE_DOUBLE,
         0x21 => Font::UNDERLINE_SINGLEACCOUNTING,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
index 32ab5c8..4e37950 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
@@ -9,7 +9,7 @@ class FillPattern
     /**
      * @var array<int, string>
      */
-    protected static $fillPatternMap = [
+    protected static array $fillPatternMap = [
         0x00 => Fill::FILL_NONE,
         0x01 => Fill::FILL_SOLID,
         0x02 => Fill::FILL_PATTERN_MEDIUMGRAY,
@@ -34,12 +34,8 @@ class FillPattern
     /**
      * Get fill pattern from index
      * OpenOffice documentation: 2.5.12.
-     *
-     * @param int $index
-     *
-     * @return string
      */
-    public static function lookup($index)
+    public static function lookup(int $index): string
     {
         if (isset(self::$fillPatternMap[$index])) {
             return self::$fillPatternMap[$index];
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
index a79b25e..882f296 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
@@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
+use PhpOffice\PhpSpreadsheet\Comment;
 use PhpOffice\PhpSpreadsheet\DefinedName;
 use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\AutoFilter;
@@ -16,6 +17,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Hyperlinks;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\PageSetup;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Properties as PropertyReader;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SharedFormula;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles;
@@ -48,18 +50,14 @@ class Xlsx extends BaseReader
 
     /**
      * ReferenceHelper instance.
-     *
-     * @var ReferenceHelper
      */
-    private $referenceHelper;
+    private ReferenceHelper $referenceHelper;
 
-    /**
-     * @var ZipArchive
-     */
-    private $zip;
+    private ZipArchive $zip;
 
-    /** @var Styles */
-    private $styleReader;
+    private Styles $styleReader;
+
+    private array $sharedFormulae = [];
 
     /**
      * Create a new Xlsx Reader instance.
@@ -93,10 +91,7 @@ class Xlsx extends BaseReader
         return $result;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function testSimpleXml($value): SimpleXMLElement
+    public static function testSimpleXml(mixed $value): SimpleXMLElement
     {
         return ($value instanceof SimpleXMLElement) ? $value : new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><root></root>');
     }
@@ -107,26 +102,24 @@ class Xlsx extends BaseReader
     }
 
     // Phpstan thinks, correctly, that xpath can return false.
-    // Scrutinizer thinks it can't.
-    // Sigh.
-    private static function xpathNoFalse(SimpleXmlElement $sxml, string $path): array
+    private static function xpathNoFalse(SimpleXMLElement $sxml, string $path): array
     {
         return self::falseToArray($sxml->xpath($path));
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function falseToArray($value): array
+    public static function falseToArray(mixed $value): array
     {
         return is_array($value) ? $value : [];
     }
 
-    private function loadZip(string $filename, string $ns = ''): SimpleXMLElement
+    private function loadZip(string $filename, string $ns = '', bool $replaceUnclosedBr = false): SimpleXMLElement
     {
         $contents = $this->getFromZipArchive($this->zip, $filename);
-        $rels = simplexml_load_string(
-            $this->securityScanner->scan($contents),
+        if ($replaceUnclosedBr) {
+            $contents = str_replace('<br>', '<br/>', $contents);
+        }
+        $rels = @simplexml_load_string(
+            $this->getSecurityScannerOrThrow()->scan($contents),
             'SimpleXMLElement',
             Settings::getLibXmlLoaderOptions(),
             $ns
@@ -141,7 +134,7 @@ class Xlsx extends BaseReader
     {
         $contents = $this->getFromZipArchive($this->zip, $filename);
         $rels = simplexml_load_string(
-            $this->securityScanner->scan($contents),
+            $this->getSecurityScannerOrThrow()->scan($contents),
             'SimpleXMLElement',
             Settings::getLibXmlLoaderOptions(),
             ($ns === '' ? $ns : '')
@@ -165,12 +158,8 @@ class Xlsx extends BaseReader
 
     /**
      * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetNames($filename)
+    public function listWorksheetNames(string $filename): array
     {
         File::assertFile($filename, self::INITIAL_FILE);
 
@@ -204,12 +193,8 @@ class Xlsx extends BaseReader
 
     /**
      * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetInfo($filename)
+    public function listWorksheetInfo(string $filename): array
     {
         File::assertFile($filename, self::INITIAL_FILE);
 
@@ -227,14 +212,14 @@ class Xlsx extends BaseReader
                 $relTarget = (string) $rel['Target'];
                 $dir = dirname($relTarget);
                 $namespace = dirname($relType);
-                $relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', '');
+                $relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', Namespaces::RELATIONSHIPS);
 
                 $worksheets = [];
                 foreach ($relsWorkbook->Relationship as $elex) {
                     $ele = self::getAttributes($elex);
                     if (
-                        ((string) $ele['Type'] === "$namespace/worksheet") ||
-                        ((string) $ele['Type'] === "$namespace/chartsheet")
+                        ((string) $ele['Type'] === "$namespace/worksheet")
+                        || ((string) $ele['Type'] === "$namespace/chartsheet")
                     ) {
                         $worksheets[(string) $ele['Id']] = $ele['Target'];
                     }
@@ -243,6 +228,7 @@ class Xlsx extends BaseReader
                 $xmlWorkbook = $this->loadZip($relTarget, $mainNS);
                 if ($xmlWorkbook->sheets) {
                     $dir = dirname($relTarget);
+
                     /** @var SimpleXMLElement $eleSheet */
                     foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
                         $tmpInfo = [
@@ -254,12 +240,12 @@ class Xlsx extends BaseReader
                         ];
 
                         $fileWorksheet = (string) $worksheets[(string) self::getArrayItem(self::getAttributes($eleSheet, $namespace), 'id')];
-                        $fileWorksheetPath = strpos($fileWorksheet, '/') === 0 ? substr($fileWorksheet, 1) : "$dir/$fileWorksheet";
+                        $fileWorksheetPath = str_starts_with($fileWorksheet, '/') ? substr($fileWorksheet, 1) : "$dir/$fileWorksheet";
 
                         $xml = new XMLReader();
                         $xml->xml(
-                            $this->securityScanner->scanFile(
-                                'zip://' . File::realpath($filename) . '#' . $fileWorksheetPath
+                            $this->getSecurityScannerOrThrow()->scan(
+                                $this->getFromZipArchive($this->zip, $fileWorksheetPath)
                             ),
                             null,
                             Settings::getLibXmlLoaderOptions()
@@ -317,17 +303,13 @@ class Xlsx extends BaseReader
         return isset($c, $c->v) ? (string) $c->v : null;
     }
 
-    /**
-     * @param mixed $value
-     * @param mixed $calculatedValue
-     */
-    private function castToFormula(?SimpleXMLElement $c, string $r, string &$cellDataType, &$value, &$calculatedValue, array &$sharedFormulas, string $castBaseType): void
+    private function castToFormula(?SimpleXMLElement $c, string $r, string &$cellDataType, mixed &$value, mixed &$calculatedValue, string $castBaseType, bool $updateSharedCells = true): void
     {
         if ($c === null) {
             return;
         }
         $attr = $c->f->attributes();
-        $cellDataType = 'f';
+        $cellDataType = DataType::TYPE_FORMULA;
         $value = "={$c->f}";
         $calculatedValue = self::$castBaseType($c);
 
@@ -335,28 +317,27 @@ class Xlsx extends BaseReader
         if (isset($attr['t']) && strtolower((string) $attr['t']) == 'shared') {
             $instance = (string) $attr['si'];
 
-            if (!isset($sharedFormulas[(string) $attr['si']])) {
-                $sharedFormulas[$instance] = ['master' => $r, 'formula' => $value];
-            } else {
-                $master = Coordinate::indexesFromString($sharedFormulas[$instance]['master']);
+            if (!isset($this->sharedFormulae[(string) $attr['si']])) {
+                $this->sharedFormulae[$instance] = new SharedFormula($r, $value);
+            } elseif ($updateSharedCells === true) {
+                // It's only worth the overhead of adjusting the shared formula for this cell if we're actually loading
+                //     the cell, which may not be the case if we're using a read filter.
+                $master = Coordinate::indexesFromString($this->sharedFormulae[$instance]->master());
                 $current = Coordinate::indexesFromString($r);
 
                 $difference = [0, 0];
                 $difference[0] = $current[0] - $master[0];
                 $difference[1] = $current[1] - $master[1];
 
-                $value = $this->referenceHelper->updateFormulaReferences($sharedFormulas[$instance]['formula'], 'A1', $difference[0], $difference[1]);
+                $value = $this->referenceHelper->updateFormulaReferences($this->sharedFormulae[$instance]->formula(), 'A1', $difference[0], $difference[1]);
             }
         }
     }
 
-    /**
-     * @param string $fileName
-     */
-    private function fileExistsInArchive(ZipArchive $archive, $fileName = ''): bool
+    private function fileExistsInArchive(ZipArchive $archive, string $fileName = ''): bool
     {
         // Root-relative paths
-        if (strpos($fileName, '//') !== false) {
+        if (str_contains($fileName, '//')) {
             $fileName = substr($fileName, strpos($fileName, '//') + 1);
         }
         $fileName = File::realpath($fileName);
@@ -373,15 +354,10 @@ class Xlsx extends BaseReader
         return $contents !== false;
     }
 
-    /**
-     * @param string $fileName
-     *
-     * @return string
-     */
-    private function getFromZipArchive(ZipArchive $archive, $fileName = '')
+    private function getFromZipArchive(ZipArchive $archive, string $fileName = ''): string
     {
         // Root-relative paths
-        if (strpos($fileName, '//') !== false) {
+        if (str_contains($fileName, '//')) {
             $fileName = substr($fileName, strpos($fileName, '//') + 1);
         }
         // Relative paths generated by dirname($filename) when $filename
@@ -392,12 +368,18 @@ class Xlsx extends BaseReader
         // Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
         //    so we need to load case-insensitively from the zip file
 
-        // Apache POI fixes
         $contents = $archive->getFromName($fileName, 0, ZipArchive::FL_NOCASE);
+
+        // Apache POI fixes
         if ($contents === false) {
             $contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
         }
 
+        // Has the file been saved with Windoze directory separators rather than unix?
+        if ($contents === false) {
+            $contents = $archive->getFromName(str_replace('/', '\\', $fileName), 0, ZipArchive::FL_NOCASE);
+        }
+
         return ($contents === false) ? '' : $contents;
     }
 
@@ -429,11 +411,14 @@ class Xlsx extends BaseReader
         foreach ($wbRels->Relationship as $relx) {
             $rel = self::getAttributes($relx);
             $relTarget = (string) $rel['Target'];
-            if (substr($relTarget, 0, 4) === '/xl/') {
+            if (str_starts_with($relTarget, '/xl/')) {
                 $relTarget = substr($relTarget, 4);
             }
             switch ($rel['Type']) {
                 case "$xmlNamespaceBase/theme":
+                    if (!$this->fileExistsInArchive($zip, "xl/{$relTarget}")) {
+                        break; // issue3770
+                    }
                     $themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
                     $themeOrderAdditional = count($themeOrderArray);
 
@@ -444,6 +429,7 @@ class Xlsx extends BaseReader
 
                     $colourScheme = self::getAttributes($xmlTheme->themeElements->clrScheme);
                     $colourSchemeName = (string) $colourScheme['name'];
+                    $excel->getTheme()->setThemeColorName($colourSchemeName);
                     $colourScheme = $xmlTheme->themeElements->clrScheme->children($drawingNS);
 
                     $themeColours = [];
@@ -455,25 +441,61 @@ class Xlsx extends BaseReader
                         if (isset($xmlColour->sysClr)) {
                             $xmlColourData = self::getAttributes($xmlColour->sysClr);
                             $themeColours[$themePos] = (string) $xmlColourData['lastClr'];
+                            $excel->getTheme()->setThemeColor($k, (string) $xmlColourData['lastClr']);
                         } elseif (isset($xmlColour->srgbClr)) {
                             $xmlColourData = self::getAttributes($xmlColour->srgbClr);
                             $themeColours[$themePos] = (string) $xmlColourData['val'];
+                            $excel->getTheme()->setThemeColor($k, (string) $xmlColourData['val']);
                         }
                     }
                     $theme = new Theme($themeName, $colourSchemeName, $themeColours);
                     $this->styleReader->setTheme($theme);
 
+                    $fontScheme = self::getAttributes($xmlTheme->themeElements->fontScheme);
+                    $fontSchemeName = (string) $fontScheme['name'];
+                    $excel->getTheme()->setThemeFontName($fontSchemeName);
+                    $majorFonts = [];
+                    $minorFonts = [];
+                    $fontScheme = $xmlTheme->themeElements->fontScheme->children($drawingNS);
+                    $majorLatin = self::getAttributes($fontScheme->majorFont->latin)['typeface'] ?? '';
+                    $majorEastAsian = self::getAttributes($fontScheme->majorFont->ea)['typeface'] ?? '';
+                    $majorComplexScript = self::getAttributes($fontScheme->majorFont->cs)['typeface'] ?? '';
+                    $minorLatin = self::getAttributes($fontScheme->minorFont->latin)['typeface'] ?? '';
+                    $minorEastAsian = self::getAttributes($fontScheme->minorFont->ea)['typeface'] ?? '';
+                    $minorComplexScript = self::getAttributes($fontScheme->minorFont->cs)['typeface'] ?? '';
+
+                    foreach ($fontScheme->majorFont->font as $xmlFont) {
+                        $fontAttributes = self::getAttributes($xmlFont);
+                        $script = (string) ($fontAttributes['script'] ?? '');
+                        if (!empty($script)) {
+                            $majorFonts[$script] = (string) ($fontAttributes['typeface'] ?? '');
+                        }
+                    }
+                    foreach ($fontScheme->minorFont->font as $xmlFont) {
+                        $fontAttributes = self::getAttributes($xmlFont);
+                        $script = (string) ($fontAttributes['script'] ?? '');
+                        if (!empty($script)) {
+                            $minorFonts[$script] = (string) ($fontAttributes['typeface'] ?? '');
+                        }
+                    }
+                    $excel->getTheme()->setMajorFontValues($majorLatin, $majorEastAsian, $majorComplexScript, $majorFonts);
+                    $excel->getTheme()->setMinorFontValues($minorLatin, $minorEastAsian, $minorComplexScript, $minorFonts);
+
                     break;
             }
         }
 
         $rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
 
-        $propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties());
-        $chartDetails = [];
+        $propertyReader = new PropertyReader($this->getSecurityScannerOrThrow(), $excel->getProperties());
+        $charts = $chartDetails = [];
         foreach ($rels->Relationship as $relx) {
             $rel = self::getAttributes($relx);
             $relTarget = (string) $rel['Target'];
+            // issue 3553
+            if ($relTarget[0] === '/') {
+                $relTarget = substr($relTarget, 1);
+            }
             $relType = (string) $rel['Type'];
             $mainNS = self::REL_TO_MAIN[$relType] ?? Namespaces::MAIN;
             switch ($relType) {
@@ -501,29 +523,9 @@ class Xlsx extends BaseReader
                     $dir = dirname($relTarget);
 
                     // Do not specify namespace in next stmt - do it in Xpath
-                    $relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', '');
+                    $relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', Namespaces::RELATIONSHIPS);
                     $relsWorkbook->registerXPathNamespace('rel', Namespaces::RELATIONSHIPS);
 
-                    $sharedStrings = [];
-                    $relType = "rel:Relationship[@Type='"
-                        //. Namespaces::SHARED_STRINGS
-                        . "$xmlNamespaceBase/sharedStrings"
-                        . "']";
-                    $xpath = self::getArrayItem($relsWorkbook->xpath($relType));
-
-                    if ($xpath) {
-                        $xmlStrings = $this->loadZip("$dir/$xpath[Target]", $mainNS);
-                        if (isset($xmlStrings->si)) {
-                            foreach ($xmlStrings->si as $val) {
-                                if (isset($val->t)) {
-                                    $sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
-                                } elseif (isset($val->r)) {
-                                    $sharedStrings[] = $this->parseRichText($val);
-                                }
-                            }
-                        }
-                    }
-
                     $worksheets = [];
                     $macros = $customUI = null;
                     foreach ($relsWorkbook->Relationship as $elex) {
@@ -569,7 +571,9 @@ class Xlsx extends BaseReader
                     if ($xpath === null) {
                         $xmlStyles = self::testSimpleXml(null);
                     } else {
-                        $xmlStyles = $this->loadZip("$dir/$xpath[Target]", $mainNS);
+                        $stylesTarget = (string) $xpath['Target'];
+                        $stylesTarget = str_starts_with($stylesTarget, '/') ? substr($stylesTarget, 1) : "$dir/$stylesTarget";
+                        $xmlStyles = $this->loadZip($stylesTarget, $mainNS);
                     }
 
                     $palette = self::extractPalette($xmlStyles);
@@ -608,14 +612,14 @@ class Xlsx extends BaseReader
                                 //  But there's a lot of naughty homebrew xlsx writers that do use "reserved" id values that aren't actually used
                                 //  So we make allowance for them rather than lose formatting masks
                                 if (
-                                    $numFmt === null &&
-                                    (int) $xf['numFmtId'] < 164 &&
-                                    NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== ''
+                                    $numFmt === null
+                                    && (int) $xf['numFmtId'] < 164
+                                    && NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== ''
                                 ) {
                                     $numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
                                 }
                             }
-                            $quotePrefix = (bool) ($xf['quotePrefix'] ?? false);
+                            $quotePrefix = (bool) (string) ($xf['quotePrefix'] ?? '');
 
                             $style = (object) [
                                 'numFmt' => $numFmt ?? NumberFormat::FORMAT_GENERAL,
@@ -650,7 +654,7 @@ class Xlsx extends BaseReader
                                 }
                             }
 
-                            $quotePrefix = (bool) ($xf['quotePrefix'] ?? false);
+                            $quotePrefix = (bool) (string) ($xf['quotePrefix'] ?? '');
 
                             $cellStyle = (object) [
                                 'numFmt' => $numFmt,
@@ -679,16 +683,43 @@ class Xlsx extends BaseReader
                     $dxfs = $this->styleReader->dxfs($this->readDataOnly);
                     $styles = $this->styleReader->styles();
 
+                    // Read content after setting the styles
+                    $sharedStrings = [];
+                    $relType = "rel:Relationship[@Type='"
+                        //. Namespaces::SHARED_STRINGS
+                        . "$xmlNamespaceBase/sharedStrings"
+                        . "']";
+                    $xpath = self::getArrayItem($relsWorkbook->xpath($relType));
+
+                    if ($xpath) {
+                        $sharedStringsTarget = (string) $xpath['Target'];
+                        $sharedStringsTarget = str_starts_with($sharedStringsTarget, '/') ? substr($sharedStringsTarget, 1) : "$dir/$sharedStringsTarget";
+                        $xmlStrings = $this->loadZip($sharedStringsTarget, $mainNS);
+                        if (isset($xmlStrings->si)) {
+                            foreach ($xmlStrings->si as $val) {
+                                if (isset($val->t)) {
+                                    $sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
+                                } elseif (isset($val->r)) {
+                                    $sharedStrings[] = $this->parseRichText($val);
+                                } else {
+                                    $sharedStrings[] = '';
+                                }
+                            }
+                        }
+                    }
+
                     $xmlWorkbook = $this->loadZipNoNamespace($relTarget, $mainNS);
                     $xmlWorkbookNS = $this->loadZip($relTarget, $mainNS);
 
                     // Set base date
+                    $excel->setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
                     if ($xmlWorkbookNS->workbookPr) {
                         Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
                         $attrs1904 = self::getAttributes($xmlWorkbookNS->workbookPr);
                         if (isset($attrs1904['date1904'])) {
                             if (self::boolean((string) $attrs1904['date1904'])) {
                                 Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
+                                $excel->setExcelCalendar(Date::CALENDAR_MAC_1904);
                             }
                         }
                     }
@@ -737,10 +768,19 @@ class Xlsx extends BaseReader
                             $docSheet->setTitle((string) $eleSheetAttr['name'], false, false);
 
                             $fileWorksheet = (string) $worksheets[$sheetReferenceId];
+                            // issue 3665 adds test for /.
+                            // This broke XlsxRootZipFilesTest,
+                            //  but Excel reports an error with that file.
+                            //  Testing dir for . avoids this problem.
+                            //  It might be better just to drop the test.
+                            if ($fileWorksheet[0] == '/' && $dir !== '.') {
+                                $fileWorksheet = substr($fileWorksheet, strlen($dir) + 2);
+                            }
                             $xmlSheet = $this->loadZipNoNamespace("$dir/$fileWorksheet", $mainNS);
                             $xmlSheetNS = $this->loadZip("$dir/$fileWorksheet", $mainNS);
 
-                            $sharedFormulas = [];
+                            // Shared Formula table is unique to each Worksheet, so we need to reset it here
+                            $this->sharedFormulae = [];
 
                             if (isset($eleSheetAttr['state']) && (string) $eleSheetAttr['state'] != '') {
                                 $docSheet->setSheetState((string) $eleSheetAttr['state']);
@@ -750,10 +790,10 @@ class Xlsx extends BaseReader
                                 // Setting Conditional Styles adjusts selected cells, so we need to execute this
                                 //    before reading the sheet view data to get the actual selected cells
                                 if (!$this->readDataOnly && ($xmlSheet->conditionalFormatting)) {
-                                    (new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load();
+                                    (new ConditionalStyles($docSheet, $xmlSheet, $dxfs, $this->styleReader))->load();
                                 }
                                 if (!$this->readDataOnly && $xmlSheet->extLst) {
-                                    (new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->loadFromExt($this->styleReader);
+                                    (new ConditionalStyles($docSheet, $xmlSheet, $dxfs, $this->styleReader))->loadFromExt();
                                 }
                                 if (isset($xmlSheetMain->sheetViews, $xmlSheetMain->sheetViews->sheetView)) {
                                     $sheetViews = new SheetViews($xmlSheetMain->sheetViews->sheetView, $docSheet);
@@ -761,12 +801,13 @@ class Xlsx extends BaseReader
                                 }
 
                                 $sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheetNS);
-                                $sheetViewOptions->load($this->getReadDataOnly(), $this->styleReader);
+                                $sheetViewOptions->load($this->readDataOnly, $this->styleReader);
 
                                 (new ColumnAndRowAttributes($docSheet, $xmlSheetNS))
-                                    ->load($this->getReadFilter(), $this->getReadDataOnly());
+                                    ->load($this->getReadFilter(), $this->readDataOnly, $this->ignoreRowsWithNoCells);
                             }
 
+                            $holdSelectedCells = $docSheet->getSelectedCells();
                             if ($xmlSheetNS && $xmlSheetNS->sheetData && $xmlSheetNS->sheetData->row) {
                                 $cIndex = 1; // Cell Start from 1
                                 foreach ($xmlSheetNS->sheetData->row as $row) {
@@ -778,6 +819,7 @@ class Xlsx extends BaseReader
                                             $r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex;
                                         }
                                         $cellDataType = (string) $cAttr['t'];
+                                        $originalCellDataTypeNumeric = $cellDataType === '';
                                         $value = null;
                                         $calculatedValue = null;
 
@@ -786,8 +828,12 @@ class Xlsx extends BaseReader
                                             $coordinates = Coordinate::coordinateFromString($r);
 
                                             if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) {
-                                                if (isset($cAttr->f)) {
-                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
+                                                // Normally, just testing for the f attribute should identify this cell as containing a formula
+                                                // that we need to read, even though it is outside of the filter range, in case it is a shared formula.
+                                                // But in some cases, this attribute isn't set; so we need to delve a level deeper and look at
+                                                // whether or not the cell has a child formula element that is shared.
+                                                if (isset($cAttr->f) || (isset($c->f, $c->f->attributes()['t']) && strtolower((string) $c->f->attributes()['t']) === 'shared')) {
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError', false);
                                                 }
                                                 ++$rowIndex;
 
@@ -815,11 +861,11 @@ class Xlsx extends BaseReader
                                                         $value = self::castToBoolean($c);
                                                     } else {
                                                         $value = null;
-                                                        $cellDataType = DATATYPE::TYPE_NULL;
+                                                        $cellDataType = DataType::TYPE_NULL;
                                                     }
                                                 } else {
                                                     // Formula
-                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToBoolean');
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToBoolean');
                                                     if (isset($c->f['t'])) {
                                                         $att = $c->f;
                                                         $docSheet->getCell($r)->setFormulaAttributes($att);
@@ -829,7 +875,7 @@ class Xlsx extends BaseReader
                                                 break;
                                             case 'inlineStr':
                                                 if (isset($c->f)) {
-                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError');
                                                 } else {
                                                     $value = $this->parseRichText($c->is);
                                                 }
@@ -840,7 +886,7 @@ class Xlsx extends BaseReader
                                                     $value = self::castToError($c);
                                                 } else {
                                                     // Formula
-                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToError');
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError');
                                                 }
 
                                                 break;
@@ -849,7 +895,7 @@ class Xlsx extends BaseReader
                                                     $value = self::castToString($c);
                                                 } else {
                                                     // Formula
-                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, $sharedFormulas, 'castToString');
+                                                    $this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToString');
                                                     if (isset($c->f['t'])) {
                                                         $attributes = $c->f['t'];
                                                         $docSheet->getCell($r)->setFormulaAttributes(['t' => (string) $attributes]);
@@ -880,14 +926,21 @@ class Xlsx extends BaseReader
                                                 $cell->setValue($value);
                                             }
                                             if ($calculatedValue !== null) {
-                                                $cell->setCalculatedValue($calculatedValue);
+                                                $cell->setCalculatedValue($calculatedValue, $originalCellDataTypeNumeric);
                                             }
 
                                             // Style information?
-                                            if ($cAttr['s'] && !$this->readDataOnly) {
+                                            if (!$this->readDataOnly) {
+                                                $holdSelected = $docSheet->getSelectedCells();
+                                                $cAttrS = (int) ($cAttr['s'] ?? 0);
                                                 // no style index means 0, it seems
-                                                $cell->setXfIndex(isset($styles[(int) ($cAttr['s'])]) ?
-                                                    (int) ($cAttr['s']) : 0);
+                                                $cAttrS = isset($styles[$cAttrS]) ? $cAttrS : 0;
+                                                $cell->setXfIndex($cAttrS);
+                                                // issue 3495
+                                                if ($cellDataType === DataType::TYPE_FORMULA && $styles[$cAttrS]->quotePrefix === true) {
+                                                    $cell->getStyle()->setQuotePrefix(false);
+                                                }
+                                                $docSheet->setSelectedCells($holdSelected);
                                             }
                                         }
                                         ++$rowIndex;
@@ -895,6 +948,13 @@ class Xlsx extends BaseReader
                                     ++$cIndex;
                                 }
                             }
+                            $docSheet->setSelectedCells($holdSelectedCells);
+                            if ($xmlSheetNS && $xmlSheetNS->ignoredErrors) {
+                                foreach ($xmlSheetNS->ignoredErrors->ignoredError as $ignoredErrorx) {
+                                    $ignoredError = self::testSimpleXml($ignoredErrorx);
+                                    $this->processIgnoredErrors($ignoredError, $docSheet);
+                                }
+                            }
 
                             if (!$this->readDataOnly && $xmlSheetNS && $xmlSheetNS->sheetProtection) {
                                 $protAttr = $xmlSheetNS->sheetProtection->attributes() ?? [];
@@ -909,16 +969,17 @@ class Xlsx extends BaseReader
                             }
 
                             if ($this->readDataOnly === false) {
-                                $this->readAutoFilter($xmlSheet, $docSheet);
-                                $this->readTables($xmlSheet, $docSheet, $dir, $fileWorksheet, $zip);
+                                $this->readAutoFilter($xmlSheetNS, $docSheet);
+                                $this->readBackgroundImage($xmlSheetNS, $docSheet, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels');
                             }
 
+                            $this->readTables($xmlSheetNS, $docSheet, $dir, $fileWorksheet, $zip, $mainNS);
+
                             if ($xmlSheetNS && $xmlSheetNS->mergeCells && $xmlSheetNS->mergeCells->mergeCell && !$this->readDataOnly) {
                                 foreach ($xmlSheetNS->mergeCells->mergeCell as $mergeCellx) {
-                                    /** @scrutinizer ignore-call */
                                     $mergeCell = $mergeCellx->attributes();
                                     $mergeRef = (string) ($mergeCell['ref'] ?? '');
-                                    if (strpos($mergeRef, ':') !== false) {
+                                    if (str_contains($mergeRef, ':')) {
                                         $docSheet->mergeCells($mergeRef, Worksheet::MERGE_CELL_CONTENT_HIDE);
                                     }
                                 }
@@ -928,23 +989,31 @@ class Xlsx extends BaseReader
                                 $unparsedLoadedData = (new PageSetup($docSheet, $xmlSheet))->load($unparsedLoadedData);
                             }
 
-                            if ($xmlSheet !== false && isset($xmlSheet->extLst, $xmlSheet->extLst->ext, $xmlSheet->extLst->ext['uri']) && ($xmlSheet->extLst->ext['uri'] == '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}')) {
-                                // Create dataValidations node if does not exists, maybe is better inside the foreach ?
-                                if (!$xmlSheet->dataValidations) {
-                                    $xmlSheet->addChild('dataValidations');
-                                }
-
-                                foreach ($xmlSheet->extLst->ext->children(Namespaces::DATA_VALIDATIONS1)->dataValidations->dataValidation as $item) {
-                                    $item = self::testSimpleXml($item);
-                                    $node = self::testSimpleXml($xmlSheet->dataValidations)->addChild('dataValidation');
-                                    foreach ($item->attributes() ?? [] as $attr) {
-                                        $node->addAttribute($attr->getName(), $attr);
+                            if ($xmlSheet !== false && isset($xmlSheet->extLst->ext)) {
+                                foreach ($xmlSheet->extLst->ext as $extlst) {
+                                    $extAttrs = $extlst->attributes() ?? [];
+                                    $extUri = (string) ($extAttrs['uri'] ?? '');
+                                    if ($extUri !== '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}') {
+                                        continue;
                                     }
-                                    $node->addAttribute('sqref', $item->children(Namespaces::DATA_VALIDATIONS2)->sqref);
-                                    if (isset($item->formula1)) {
-                                        $childNode = $node->addChild('formula1');
-                                        if ($childNode !== null) { // null should never happen
-                                            $childNode[0] = (string) $item->formula1->children(Namespaces::DATA_VALIDATIONS2)->f; // @phpstan-ignore-line
+                                    // Create dataValidations node if does not exists, maybe is better inside the foreach ?
+                                    if (!$xmlSheet->dataValidations) {
+                                        $xmlSheet->addChild('dataValidations');
+                                    }
+
+                                    foreach ($extlst->children(Namespaces::DATA_VALIDATIONS1)->dataValidations->dataValidation as $item) {
+                                        $item = self::testSimpleXml($item);
+                                        $node = self::testSimpleXml($xmlSheet->dataValidations)->addChild('dataValidation');
+                                        foreach ($item->attributes() ?? [] as $attr) {
+                                            $node->addAttribute($attr->getName(), $attr);
+                                        }
+                                        $node->addAttribute('sqref', $item->children(Namespaces::DATA_VALIDATIONS2)->sqref);
+                                        if (isset($item->formula1)) {
+                                            $childNode = $node->addChild('formula1');
+                                            if ($childNode !== null) { // null should never happen
+                                                // see https://github.com/phpstan/phpstan/issues/8236
+                                                $childNode[0] = (string) $item->formula1->children(Namespaces::DATA_VALIDATIONS2)->f; // @phpstan-ignore-line
+                                            }
                                         }
                                     }
                                 }
@@ -970,7 +1039,7 @@ class Xlsx extends BaseReader
                                 $hyperlinkReader = new Hyperlinks($docSheet);
                                 // Locate hyperlink relations
                                 $relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
-                                if ($zip->locateName($relationsFileName)) {
+                                if ($zip->locateName($relationsFileName) !== false) {
                                     $relsWorksheet = $this->loadZip($relationsFileName, Namespaces::RELATIONSHIPS);
                                     $hyperlinkReader->readHyperlinks($relsWorksheet);
                                 }
@@ -987,7 +1056,7 @@ class Xlsx extends BaseReader
                             if (!$this->readDataOnly) {
                                 // Locate comment relations
                                 $commentRelations = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
-                                if ($zip->locateName($commentRelations)) {
+                                if ($zip->locateName($commentRelations) !== false) {
                                     $relsWorksheet = $this->loadZip($commentRelations, Namespaces::RELATIONSHIPS);
                                     foreach ($relsWorksheet->Relationship as $elex) {
                                         $ele = self::getAttributes($elex);
@@ -1029,6 +1098,7 @@ class Xlsx extends BaseReader
 
                                 // later we will remove from it real vmlComments
                                 $unparsedVmlDrawings = $vmlComments;
+                                $vmlDrawingContents = [];
 
                                 // Loop through VML comments
                                 foreach ($vmlComments as $relName => $relPath) {
@@ -1037,9 +1107,9 @@ class Xlsx extends BaseReader
 
                                     try {
                                         // no namespace okay - processed with Xpath
-                                        $vmlCommentsFile = $this->loadZip($relPath, '');
+                                        $vmlCommentsFile = $this->loadZip($relPath, '', true);
                                         $vmlCommentsFile->registerXPathNamespace('v', Namespaces::URN_VML);
-                                    } catch (Throwable $ex) {
+                                    } catch (Throwable) {
                                         //Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
                                         continue;
                                     }
@@ -1047,7 +1117,8 @@ class Xlsx extends BaseReader
                                     // Locate VML drawings image relations
                                     $drowingImages = [];
                                     $VMLDrawingsRelations = dirname($relPath) . '/_rels/' . basename($relPath) . '.rels';
-                                    if ($zip->locateName($VMLDrawingsRelations)) {
+                                    $vmlDrawingContents[$relName] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $relPath));
+                                    if ($zip->locateName($VMLDrawingsRelations) !== false) {
                                         $relsVMLDrawing = $this->loadZip($VMLDrawingsRelations, Namespaces::RELATIONSHIPS);
                                         foreach ($relsVMLDrawing->Relationship as $elex) {
                                             $ele = self::getAttributes($elex);
@@ -1066,10 +1137,19 @@ class Xlsx extends BaseReader
                                             $fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
                                             $column = null;
                                             $row = null;
+                                            $textHAlign = null;
                                             $fillImageRelId = null;
                                             $fillImageTitle = '';
 
                                             $clientData = $shape->xpath('.//x:ClientData');
+                                            $textboxDirection = '';
+                                            $textboxPath = $shape->xpath('.//v:textbox');
+                                            $textbox = (string) ($textboxPath[0]['style'] ?? '');
+                                            if (preg_match('/rtl/i', $textbox) === 1) {
+                                                $textboxDirection = Comment::TEXTBOX_DIRECTION_RTL;
+                                            } elseif (preg_match('/ltr/i', $textbox) === 1) {
+                                                $textboxDirection = Comment::TEXTBOX_DIRECTION_LTR;
+                                            }
                                             if (is_array($clientData) && !empty($clientData)) {
                                                 $clientData = $clientData[0];
 
@@ -1083,8 +1163,20 @@ class Xlsx extends BaseReader
                                                     if (is_array($temp)) {
                                                         $column = $temp[0];
                                                     }
+                                                    $temp = $clientData->xpath('.//x:TextHAlign');
+                                                    if (!empty($temp)) {
+                                                        $textHAlign = strtolower($temp[0]);
+                                                    }
                                                 }
                                             }
+                                            $rowx = (string) $row;
+                                            $colx = (string) $column;
+                                            if (is_numeric($rowx) && is_numeric($colx) && $textHAlign !== null) {
+                                                $docSheet->getComment([1 + (int) $colx, 1 + (int) $rowx], false)->setAlignment((string) $textHAlign);
+                                            }
+                                            if (is_numeric($rowx) && is_numeric($colx) && $textboxDirection !== '') {
+                                                $docSheet->getComment([1 + (int) $colx, 1 + (int) $rowx], false)->setTextboxDirection($textboxDirection);
+                                            }
 
                                             $fillImageRelNode = $shape->xpath('.//v:fill/@o:relid');
                                             if (is_array($fillImageRelNode) && !empty($fillImageRelNode)) {
@@ -1111,7 +1203,7 @@ class Xlsx extends BaseReader
                                                 if (isset($drowingImages[$fillImageRelId])) {
                                                     $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
                                                     $objDrawing->setName($fillImageTitle);
-                                                    $imagePath = str_replace('../', 'xl/', $drowingImages[$fillImageRelId]);
+                                                    $imagePath = str_replace(['../', '/xl/'], 'xl/', $drowingImages[$fillImageRelId]);
                                                     $objDrawing->setPath(
                                                         'zip://' . File::realpath($filename) . '#' . $imagePath,
                                                         true,
@@ -1156,7 +1248,7 @@ class Xlsx extends BaseReader
                                         $unparsedVmlDrawing[$rId] = [];
                                         $unparsedVmlDrawing[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $relPath);
                                         $unparsedVmlDrawing[$rId]['relFilePath'] = $relPath;
-                                        $unparsedVmlDrawing[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath']));
+                                        $unparsedVmlDrawing[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath']));
                                         unset($unparsedVmlDrawing);
                                     }
                                 }
@@ -1168,7 +1260,7 @@ class Xlsx extends BaseReader
                                     if ($vmlHfRidAttr !== null && isset($vmlHfRidAttr['id'])) {
                                         $vmlHfRid = (string) $vmlHfRidAttr['id'][0];
                                     }
-                                    if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+                                    if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') !== false) {
                                         $relsWorksheet = $this->loadZipNoNamespace(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels', Namespaces::RELATIONSHIPS);
                                         $vmlRelationship = '';
 
@@ -1241,19 +1333,20 @@ class Xlsx extends BaseReader
                                 . '/_rels/'
                                 . basename($fileWorksheet)
                                 . '.rels';
-                            if (substr($drawingFilename, 0, 7) === 'xl//xl/') {
+                            if (str_starts_with($drawingFilename, 'xl//xl/')) {
                                 $drawingFilename = substr($drawingFilename, 4);
                             }
-                            if (substr($drawingFilename, 0, 8) === '/xl//xl/') {
+                            if (str_starts_with($drawingFilename, '/xl//xl/')) {
                                 $drawingFilename = substr($drawingFilename, 5);
                             }
-                            if ($zip->locateName($drawingFilename)) {
-                                $relsWorksheet = $this->loadZipNoNamespace($drawingFilename, Namespaces::RELATIONSHIPS);
+                            if ($zip->locateName($drawingFilename) !== false) {
+                                $relsWorksheet = $this->loadZip($drawingFilename, Namespaces::RELATIONSHIPS);
                                 $drawings = [];
-                                foreach ($relsWorksheet->Relationship as $ele) {
+                                foreach ($relsWorksheet->Relationship as $elex) {
+                                    $ele = self::getAttributes($elex);
                                     if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") {
                                         $eleTarget = (string) $ele['Target'];
-                                        if (substr($eleTarget, 0, 4) === '/xl/') {
+                                        if (str_starts_with($eleTarget, '/xl/')) {
                                             $drawings[(string) $ele['Id']] = substr($eleTarget, 1);
                                         } else {
                                             $drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
@@ -1268,19 +1361,20 @@ class Xlsx extends BaseReader
                                         $drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
                                         $fileDrawing = $drawings[$drawingRelId];
                                         $drawingFilename = dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels';
-                                        $relsDrawing = $this->loadZipNoNamespace($drawingFilename, $xmlNamespaceBase);
+                                        $relsDrawing = $this->loadZip($drawingFilename, Namespaces::RELATIONSHIPS);
 
                                         $images = [];
                                         $hyperlinks = [];
                                         if ($relsDrawing && $relsDrawing->Relationship) {
-                                            foreach ($relsDrawing->Relationship as $ele) {
+                                            foreach ($relsDrawing->Relationship as $elex) {
+                                                $ele = self::getAttributes($elex);
                                                 $eleType = (string) $ele['Type'];
                                                 if ($eleType === Namespaces::HYPERLINK) {
                                                     $hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
                                                 }
                                                 if ($eleType === "$xmlNamespaceBase/image") {
                                                     $eleTarget = (string) $ele['Target'];
-                                                    if (substr($eleTarget, 0, 4) === '/xl/') {
+                                                    if (str_starts_with($eleTarget, '/xl/')) {
                                                         $eleTarget = substr($eleTarget, 1);
                                                         $images[(string) $ele['Id']] = $eleTarget;
                                                     } else {
@@ -1289,7 +1383,7 @@ class Xlsx extends BaseReader
                                                 } elseif ($eleType === "$xmlNamespaceBase/chart") {
                                                     if ($this->includeCharts) {
                                                         $eleTarget = (string) $ele['Target'];
-                                                        if (substr($eleTarget, 0, 4) === '/xl/') {
+                                                        if (str_starts_with($eleTarget, '/xl/')) {
                                                             $index = substr($eleTarget, 1);
                                                         } else {
                                                             $index = self::dirAdd($fileDrawing, $eleTarget);
@@ -1326,8 +1420,8 @@ class Xlsx extends BaseReader
                                                     );
                                                     if (isset($images[$embedImageKey])) {
                                                         $objDrawing->setPath(
-                                                            'zip://' . File::realpath($filename) . '#' .
-                                                            $images[$embedImageKey],
+                                                            'zip://' . File::realpath($filename) . '#'
+                                                            . $images[$embedImageKey],
                                                             false
                                                         );
                                                     } else {
@@ -1349,6 +1443,8 @@ class Xlsx extends BaseReader
                                                     $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cy')));
                                                     if ($xfrm) {
                                                         $objDrawing->setRotation((int) Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($xfrm), 'rot')));
+                                                        $objDrawing->setFlipVertical((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipV'));
+                                                        $objDrawing->setFlipHorizontal((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipH'));
                                                     }
                                                     if ($outerShdw) {
                                                         $shadow = $objDrawing->getShadow();
@@ -1394,11 +1490,13 @@ class Xlsx extends BaseReader
                                             foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
                                                 $twoCellAnchor = self::testSimpleXml($twoCellAnchor);
                                                 if ($twoCellAnchor->pic->blipFill) {
+                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
                                                     $blip = $twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
+                                                    if (isset($twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->srcRect)) {
+                                                        $objDrawing->setSrcRect($twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->srcRect->attributes());
+                                                    }
                                                     $xfrm = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
                                                     $outerShdw = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
-                                                    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
-                                                    /** @scrutinizer ignore-call */
                                                     $editAs = $twoCellAnchor->attributes();
                                                     if (isset($editAs, $editAs['editAs'])) {
                                                         $objDrawing->setEditAs($editAs['editAs']);
@@ -1411,8 +1509,8 @@ class Xlsx extends BaseReader
                                                     );
                                                     if (isset($images[$embedImageKey])) {
                                                         $objDrawing->setPath(
-                                                            'zip://' . File::realpath($filename) . '#' .
-                                                            $images[$embedImageKey],
+                                                            'zip://' . File::realpath($filename) . '#'
+                                                            . $images[$embedImageKey],
                                                             false
                                                         );
                                                     } else {
@@ -1441,6 +1539,8 @@ class Xlsx extends BaseReader
                                                         $objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($xfrm->ext), 'cx')));
                                                         $objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($xfrm->ext), 'cy')));
                                                         $objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($xfrm), 'rot')));
+                                                        $objDrawing->setFlipVertical((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipV'));
+                                                        $objDrawing->setFlipHorizontal((bool) self::getArrayItem(self::getAttributes($xfrm), 'flipH'));
                                                     }
                                                     if ($outerShdw) {
                                                         $shadow = $objDrawing->getShadow();
@@ -1510,7 +1610,8 @@ class Xlsx extends BaseReader
 
                                     // store original rId of drawing files
                                     $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
-                                    foreach ($relsWorksheet->Relationship as $ele) {
+                                    foreach ($relsWorksheet->Relationship as $elex) {
+                                        $ele = self::getAttributes($elex);
                                         if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") {
                                             $drawingRelId = (string) $ele['Id'];
                                             $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = $drawingRelId;
@@ -1519,6 +1620,14 @@ class Xlsx extends BaseReader
                                             }
                                         }
                                     }
+                                    if ($xmlSheet->legacyDrawing && !$this->readDataOnly) {
+                                        foreach ($xmlSheet->legacyDrawing as $drawing) {
+                                            $drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
+                                            if (isset($vmlDrawingContents[$drawingRelId])) {
+                                                $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['legacyDrawing'] = $vmlDrawingContents[$drawingRelId];
+                                            }
+                                        }
+                                    }
 
                                     // unparsed drawing AlternateContent
                                     $xmlAltDrawing = $this->loadZip((string) $fileDrawing, Namespaces::COMPATIBILITY);
@@ -1560,7 +1669,7 @@ class Xlsx extends BaseReader
                                                     $extractedRange = explode(',', $extractedRange);
                                                     foreach ($extractedRange as $range) {
                                                         $autoFilterRange = $range;
-                                                        if (strpos($autoFilterRange, ':') !== false) {
+                                                        if (str_contains($autoFilterRange, ':')) {
                                                             $docSheet->getAutoFilter()->setRange($autoFilterRange);
                                                         }
                                                     }
@@ -1594,7 +1703,7 @@ class Xlsx extends BaseReader
                                                     if (empty($rangeSet)) {
                                                         continue;
                                                     }
-                                                    if (strpos($rangeSet, ':') === false) {
+                                                    if (!str_contains($rangeSet, ':')) {
                                                         $rangeSet = $rangeSet . ':' . $rangeSet;
                                                     }
                                                     $newRangeSets[] = str_replace('$', '', $rangeSet);
@@ -1637,12 +1746,12 @@ class Xlsx extends BaseReader
                                             break;
                                         default:
                                             if ($mapSheetId[(int) $definedName['localSheetId']] !== null) {
-                                                $range = Worksheet::extractSheetTitle((string) $definedName, true);
+                                                $range = Worksheet::extractSheetTitle($extractedRange, true);
                                                 $scope = $excel->getSheet($mapSheetId[(int) $definedName['localSheetId']]);
-                                                if (strpos((string) $definedName, '!') !== false) {
+                                                if (str_contains((string) $definedName, '!')) {
                                                     $range[0] = str_replace("''", "'", $range[0]);
                                                     $range[0] = str_replace("'", '', $range[0]);
-                                                    if ($worksheet = $excel->getSheetByName($range[0])) { // @phpstan-ignore-line
+                                                    if ($worksheet = $excel->getSheetByName($range[0])) {
                                                         $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $worksheet, $extractedRange, true, $scope));
                                                     } else {
                                                         $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true, $scope));
@@ -1655,25 +1764,26 @@ class Xlsx extends BaseReader
                                             break;
                                     }
                                 } elseif (!isset($definedName['localSheetId'])) {
-                                    $definedRange = (string) $definedName;
                                     // "Global" definedNames
                                     $locatedSheet = null;
-                                    if (strpos((string) $definedName, '!') !== false) {
+                                    if (str_contains((string) $definedName, '!')) {
                                         // Modify range, and extract the first worksheet reference
                                         // Need to split on a comma or a space if not in quotes, and extract the first part.
-                                        $definedNameValueParts = preg_split("/[ ,](?=([^']*'[^']*')*[^']*$)/miuU", $definedRange);
-                                        // Extract sheet name
-                                        [$extractedSheetName] = Worksheet::extractSheetTitle((string) $definedNameValueParts[0], true); // @phpstan-ignore-line
-                                        $extractedSheetName = trim($extractedSheetName, "'");
+                                        $definedNameValueParts = preg_split("/[ ,](?=([^']*'[^']*')*[^']*$)/miuU", $extractedRange);
+                                        if (is_array($definedNameValueParts)) {
+                                            // Extract sheet name
+                                            [$extractedSheetName] = Worksheet::extractSheetTitle((string) $definedNameValueParts[0], true);
+                                            $extractedSheetName = trim((string) $extractedSheetName, "'");
 
-                                        // Locate sheet
-                                        $locatedSheet = $excel->getSheetByName($extractedSheetName);
+                                            // Locate sheet
+                                            $locatedSheet = $excel->getSheetByName($extractedSheetName);
+                                        }
                                     }
 
-                                    if ($locatedSheet === null && !DefinedName::testIfFormula($definedRange)) {
-                                        $definedRange = '#REF!';
+                                    if ($locatedSheet === null && !DefinedName::testIfFormula($extractedRange)) {
+                                        $extractedRange = '#REF!';
                                     }
-                                    $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $definedRange, false));
+                                    $excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $extractedRange, false));
                                 }
                             }
                         }
@@ -1709,8 +1819,8 @@ class Xlsx extends BaseReader
                             $objChart = $chartReader->readChart($chartElements, basename($chartEntryRef, '.xml'));
                             if (isset($charts[$chartEntryRef])) {
                                 $chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
-                                if (isset($chartDetails[$chartPositionRef])) {
-                                    $excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart); // @phpstan-ignore-line
+                                if (isset($chartDetails[$chartPositionRef]) && $excel->getSheetByName($charts[$chartEntryRef]['sheet']) !== null) {
+                                    $excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart);
                                     $objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
                                     // For oneCellAnchor or absoluteAnchor positioned charts,
                                     //     toCoordinate is not in the data. Does it need to be calculated?
@@ -1748,10 +1858,7 @@ class Xlsx extends BaseReader
         return $excel;
     }
 
-    /**
-     * @return RichText
-     */
-    private function parseRichText(?SimpleXMLElement $is)
+    private function parseRichText(?SimpleXMLElement $is): RichText
     {
         $value = new RichText();
 
@@ -1785,8 +1892,8 @@ class Xlsx extends BaseReader
                         if (isset($run->rPr->b)) {
                             $attr = $run->rPr->b->attributes();
                             if (
-                                (isset($attr['val']) && self::boolean((string) $attr['val'])) ||
-                                (!isset($attr['val']))
+                                (isset($attr['val']) && self::boolean((string) $attr['val']))
+                                || (!isset($attr['val']))
                             ) {
                                 $objFont->setBold(true);
                             }
@@ -1794,8 +1901,8 @@ class Xlsx extends BaseReader
                         if (isset($run->rPr->i)) {
                             $attr = $run->rPr->i->attributes();
                             if (
-                                (isset($attr['val']) && self::boolean((string) $attr['val'])) ||
-                                (!isset($attr['val']))
+                                (isset($attr['val']) && self::boolean((string) $attr['val']))
+                                || (!isset($attr['val']))
                             ) {
                                 $objFont->setItalic(true);
                             }
@@ -1815,7 +1922,7 @@ class Xlsx extends BaseReader
                         if (isset($run->rPr->u)) {
                             $attr = $run->rPr->u->attributes();
                             if (!isset($attr['val'])) {
-                                $objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
+                                $objFont->setUnderline(StyleFont::UNDERLINE_SINGLE);
                             } else {
                                 $objFont->setUnderline((string) $attr['val']);
                             }
@@ -1823,8 +1930,8 @@ class Xlsx extends BaseReader
                         if (isset($run->rPr->strike)) {
                             $attr = $run->rPr->strike->attributes();
                             if (
-                                (isset($attr['val']) && self::boolean((string) $attr['val'])) ||
-                                (!isset($attr['val']))
+                                (isset($attr['val']) && self::boolean((string) $attr['val']))
+                                || (!isset($attr['val']))
                             ) {
                                 $objFont->setStrikethrough(true);
                             }
@@ -1851,7 +1958,7 @@ class Xlsx extends BaseReader
         if ($dataRels) {
             // exists and not empty if the ribbon have some pictures (other than internal MSO)
             $UIRels = simplexml_load_string(
-                $this->securityScanner->scan($dataRels),
+                $this->getSecurityScannerOrThrow()->scan($dataRels),
                 'SimpleXMLElement',
                 Settings::getLibXmlLoaderOptions()
             );
@@ -1879,22 +1986,12 @@ class Xlsx extends BaseReader
         }
     }
 
-    /**
-     * @param null|array|bool|SimpleXMLElement $array
-     * @param int|string $key
-     *
-     * @return mixed
-     */
-    private static function getArrayItem($array, $key = 0)
+    private static function getArrayItem(null|array|bool|SimpleXMLElement $array, int|string $key = 0): mixed
     {
         return ($array === null || is_bool($array)) ? null : ($array[$key] ?? null);
     }
 
-    /**
-     * @param null|SimpleXMLElement|string $base
-     * @param null|SimpleXMLElement|string $add
-     */
-    private static function dirAdd($base, $add): string
+    private static function dirAdd(null|SimpleXMLElement|string $base, null|SimpleXMLElement|string $add): string
     {
         $base = (string) $base;
         $add = (string) $add;
@@ -1911,18 +2008,18 @@ class Xlsx extends BaseReader
         foreach ($temp as $item) {
             $item = explode(':', $item);
 
-            if (strpos($item[1], 'px') !== false) {
+            if (str_contains($item[1], 'px')) {
                 $item[1] = str_replace('px', '', $item[1]);
             }
-            if (strpos($item[1], 'pt') !== false) {
+            if (str_contains($item[1], 'pt')) {
                 $item[1] = str_replace('pt', '', $item[1]);
                 $item[1] = (string) Font::fontSizeToPixels((int) $item[1]);
             }
-            if (strpos($item[1], 'in') !== false) {
+            if (str_contains($item[1], 'in')) {
                 $item[1] = str_replace('in', '', $item[1]);
                 $item[1] = (string) Font::inchSizeToPixels((int) $item[1]);
             }
-            if (strpos($item[1], 'cm') !== false) {
+            if (str_contains($item[1], 'cm')) {
                 $item[1] = str_replace('cm', '', $item[1]);
                 $item[1] = (string) Font::centimeterSizeToPixels((int) $item[1]);
             }
@@ -1947,10 +2044,7 @@ class Xlsx extends BaseReader
         return $value === 'true' || $value === 'TRUE';
     }
 
-    /**
-     * @param array $hyperlinks
-     */
-    private function readHyperLinkDrawing(\PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing, SimpleXMLElement $cellAnchor, $hyperlinks): void
+    private function readHyperLinkDrawing(\PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing, SimpleXMLElement $cellAnchor, array $hyperlinks): void
     {
         $hlinkClick = $cellAnchor->pic->nvPicPr->cNvPr->children(Namespaces::DRAWINGML)->hlinkClick;
 
@@ -1991,7 +2085,7 @@ class Xlsx extends BaseReader
         }
     }
 
-    private static function getLockValue(SimpleXmlElement $protection, string $key): ?bool
+    private static function getLockValue(SimpleXMLElement $protection, string $key): ?bool
     {
         $returnValue = null;
         $protectKey = $protection[$key];
@@ -2006,7 +2100,7 @@ class Xlsx extends BaseReader
     private function readFormControlProperties(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
     {
         $zip = $this->zip;
-        if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+        if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') === false) {
             return;
         }
 
@@ -2025,7 +2119,7 @@ class Xlsx extends BaseReader
             $unparsedCtrlProps[$rId] = [];
             $unparsedCtrlProps[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $ctrlProp['Target']);
             $unparsedCtrlProps[$rId]['relFilePath'] = (string) $ctrlProp['Target'];
-            $unparsedCtrlProps[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath']));
+            $unparsedCtrlProps[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath']));
         }
         unset($unparsedCtrlProps);
     }
@@ -2033,7 +2127,7 @@ class Xlsx extends BaseReader
     private function readPrinterSettings(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
     {
         $zip = $this->zip;
-        if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+        if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') === false) {
             return;
         }
 
@@ -2049,13 +2143,14 @@ class Xlsx extends BaseReader
         $unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
         foreach ($sheetPrinterSettings as $rId => $printerSettings) {
             $rId = substr($rId, 3); // rIdXXX
-            if (substr($rId, -2) !== 'ps') {
+            if (!str_ends_with($rId, 'ps')) {
                 $rId = $rId . 'ps'; // rIdXXX, add 'ps' suffix to avoid identical resource identifier collision with unparsed vmlDrawing
             }
             $unparsedPrinterSettings[$rId] = [];
-            $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']);
-            $unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target'];
-            $unparsedPrinterSettings[$rId]['content'] = $this->securityScanner->scan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath']));
+            $target = (string) str_replace('/xl/', '../', (string) $printerSettings['Target']);
+            $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $target);
+            $unparsedPrinterSettings[$rId]['relFilePath'] = $target;
+            $unparsedPrinterSettings[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath']));
         }
         unset($unparsedPrinterSettings);
     }
@@ -2106,7 +2201,7 @@ class Xlsx extends BaseReader
 
         if ($xmlSheet->protectedRanges->protectedRange) {
             foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
-                $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
+                $docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true, (string) $protectedRange['name'], (string) $protectedRange['securityDescriptor']);
             }
         }
     }
@@ -2120,15 +2215,40 @@ class Xlsx extends BaseReader
         }
     }
 
+    private function readBackgroundImage(
+        SimpleXMLElement $xmlSheet,
+        Worksheet $docSheet,
+        string $relsName
+    ): void {
+        if ($xmlSheet && $xmlSheet->picture) {
+            $id = (string) self::getArrayItem(self::getAttributes($xmlSheet->picture, Namespaces::SCHEMA_OFFICE_DOCUMENT), 'id');
+            $rels = $this->loadZip($relsName);
+            foreach ($rels->Relationship as $rel) {
+                $attrs = $rel->attributes() ?? [];
+                $rid = (string) ($attrs['Id'] ?? '');
+                $target = (string) ($attrs['Target'] ?? '');
+                if ($rid === $id && substr($target, 0, 2) === '..') {
+                    $target = 'xl' . substr($target, 2);
+                    $content = $this->getFromZipArchive($this->zip, $target);
+                    $docSheet->setBackgroundImage($content);
+                }
+            }
+        }
+    }
+
     private function readTables(
         SimpleXMLElement $xmlSheet,
         Worksheet $docSheet,
         string $dir,
         string $fileWorksheet,
-        ZipArchive $zip
+        ZipArchive $zip,
+        string $namespaceTable
     ): void {
-        if ($xmlSheet && $xmlSheet->tableParts && (int) $xmlSheet->tableParts['count'] > 0) {
-            $this->readTablesInTablesFile($xmlSheet, $dir, $fileWorksheet, $zip, $docSheet);
+        if ($xmlSheet && $xmlSheet->tableParts) {
+            $attributes = $xmlSheet->tableParts->attributes() ?? ['count' => 0];
+            if (((int) $attributes['count']) > 0) {
+                $this->readTablesInTablesFile($xmlSheet, $dir, $fileWorksheet, $zip, $docSheet, $namespaceTable);
+            }
         }
     }
 
@@ -2137,14 +2257,15 @@ class Xlsx extends BaseReader
         string $dir,
         string $fileWorksheet,
         ZipArchive $zip,
-        Worksheet $docSheet
+        Worksheet $docSheet,
+        string $namespaceTable
     ): void {
         foreach ($xmlSheet->tableParts->tablePart as $tablePart) {
             $relation = self::getAttributes($tablePart, Namespaces::SCHEMA_OFFICE_DOCUMENT);
             $tablePartRel = (string) $relation['id'];
             $relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
 
-            if ($zip->locateName($relationsFileName)) {
+            if ($zip->locateName($relationsFileName) !== false) {
                 $relsTableReferences = $this->loadZip($relationsFileName, Namespaces::RELATIONSHIPS);
                 foreach ($relsTableReferences->Relationship as $relationship) {
                     $relationshipAttributes = self::getAttributes($relationship, '');
@@ -2155,7 +2276,7 @@ class Xlsx extends BaseReader
                         $relationshipFilePath = File::realpath($relationshipFilePath);
 
                         if ($this->fileExistsInArchive($this->zip, $relationshipFilePath)) {
-                            $tableXml = $this->loadZip($relationshipFilePath);
+                            $tableXml = $this->loadZip($relationshipFilePath, $namespaceTable);
                             (new TableReader($docSheet, $tableXml))->load();
                         }
                     }
@@ -2192,4 +2313,48 @@ class Xlsx extends BaseReader
 
         return $array;
     }
+
+    private function processIgnoredErrors(SimpleXMLElement $xml, Worksheet $sheet): void
+    {
+        $attributes = self::getAttributes($xml);
+        $sqref = (string) ($attributes['sqref'] ?? '');
+        $numberStoredAsText = (string) ($attributes['numberStoredAsText'] ?? '');
+        $formula = (string) ($attributes['formula'] ?? '');
+        $twoDigitTextYear = (string) ($attributes['twoDigitTextYear'] ?? '');
+        $evalError = (string) ($attributes['evalError'] ?? '');
+        if (!empty($sqref)) {
+            $explodedSqref = explode(' ', $sqref);
+            $pattern1 = '/^([A-Z]{1,3})([0-9]{1,7})(:([A-Z]{1,3})([0-9]{1,7}))?$/';
+            foreach ($explodedSqref as $sqref1) {
+                if (preg_match($pattern1, $sqref1, $matches) === 1) {
+                    $firstRow = $matches[2];
+                    $firstCol = $matches[1];
+                    if (array_key_exists(3, $matches)) {
+                        $lastCol = $matches[4];
+                        $lastRow = $matches[5];
+                    } else {
+                        $lastCol = $firstCol;
+                        $lastRow = $firstRow;
+                    }
+                    ++$lastCol;
+                    for ($row = $firstRow; $row <= $lastRow; ++$row) {
+                        for ($col = $firstCol; $col !== $lastCol; ++$col) {
+                            if ($numberStoredAsText === '1') {
+                                $sheet->getCell("$col$row")->getIgnoredErrors()->setNumberStoredAsText(true);
+                            }
+                            if ($formula === '1') {
+                                $sheet->getCell("$col$row")->getIgnoredErrors()->setFormula(true);
+                            }
+                            if ($twoDigitTextYear === '1') {
+                                $sheet->getCell("$col$row")->getIgnoredErrors()->setTwoDigitTextYear(true);
+                            }
+                            if ($evalError === '1') {
+                                $sheet->getCell("$col$row")->getIgnoredErrors()->setEvalError(true);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
index a6ab4d8..49fe360 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
 
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
 use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
 use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
 use PhpOffice\PhpSpreadsheet\Worksheet\Table;
@@ -10,20 +11,11 @@ use SimpleXMLElement;
 
 class AutoFilter
 {
-    /**
-     * @var Table|Worksheet
-     */
-    private $parent;
+    private Table|Worksheet $parent;
 
-    /**
-     * @var SimpleXMLElement
-     */
-    private $worksheetXml;
+    private SimpleXMLElement $worksheetXml;
 
-    /**
-     * @param Table|Worksheet $parent
-     */
-    public function __construct($parent, SimpleXMLElement $worksheetXml)
+    public function __construct(Table|Worksheet $parent, SimpleXMLElement $worksheetXml)
     {
         $this->parent = $parent;
         $this->worksheetXml = $worksheetXml;
@@ -32,36 +24,39 @@ class AutoFilter
     public function load(): void
     {
         // Remove all "$" in the auto filter range
-        $autoFilterRange = (string) preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref'] ?? '');
-        if (strpos($autoFilterRange, ':') !== false) {
-            $this->readAutoFilter($autoFilterRange, $this->worksheetXml);
+        $attrs = $this->worksheetXml->autoFilter->attributes() ?? [];
+        $autoFilterRange = (string) preg_replace('/\$/', '', $attrs['ref'] ?? '');
+        if (str_contains($autoFilterRange, ':')) {
+            $this->readAutoFilter($autoFilterRange);
         }
     }
 
-    private function readAutoFilter(string $autoFilterRange, SimpleXMLElement $xmlSheet): void
+    private function readAutoFilter(string $autoFilterRange): void
     {
         $autoFilter = $this->parent->getAutoFilter();
         $autoFilter->setRange($autoFilterRange);
 
-        foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
-            $column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
+        foreach ($this->worksheetXml->autoFilter->filterColumn as $filterColumn) {
+            $attributes = $filterColumn->attributes() ?? [];
+            $column = $autoFilter->getColumnByOffset((int) ($attributes['colId'] ?? 0));
             //    Check for standard filters
             if ($filterColumn->filters) {
                 $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
-                $filters = $filterColumn->filters;
+                $filters = Xlsx::testSimpleXml($filterColumn->filters->attributes());
                 if ((isset($filters['blank'])) && ((int) $filters['blank'] == 1)) {
                     //    Operator is undefined, but always treated as EQUAL
                     $column->createRule()->setRule('', '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
                 }
                 //    Standard filters are always an OR join, so no join rule needs to be set
                 //    Entries can be either filter elements
-                foreach ($filters->filter as $filterRule) {
+                foreach ($filterColumn->filters->filter as $filterRule) {
                     //    Operator is undefined, but always treated as EQUAL
-                    $column->createRule()->setRule('', (string) $filterRule['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
+                    $attr2 = $filterRule->attributes() ?? ['val' => ''];
+                    $column->createRule()->setRule('', (string) $attr2['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
                 }
 
                 //    Or Date Group elements
-                $this->readDateRangeAutoFilter($filters, $column);
+                $this->readDateRangeAutoFilter($filterColumn->filters, $column);
             }
 
             //    Check for custom filters
@@ -76,20 +71,23 @@ class AutoFilter
 
     private function readDateRangeAutoFilter(SimpleXMLElement $filters, Column $column): void
     {
-        foreach ($filters->dateGroupItem as $dateGroupItem) {
+        foreach ($filters->dateGroupItem as $dateGroupItemx) {
             //    Operator is undefined, but always treated as EQUAL
-            $column->createRule()->setRule(
-                '',
-                [
-                    'year' => (string) $dateGroupItem['year'],
-                    'month' => (string) $dateGroupItem['month'],
-                    'day' => (string) $dateGroupItem['day'],
-                    'hour' => (string) $dateGroupItem['hour'],
-                    'minute' => (string) $dateGroupItem['minute'],
-                    'second' => (string) $dateGroupItem['second'],
-                ],
-                (string) $dateGroupItem['dateTimeGrouping']
-            )->setRuleType(Rule::AUTOFILTER_RULETYPE_DATEGROUP);
+            $dateGroupItem = $dateGroupItemx->attributes();
+            if ($dateGroupItem !== null) {
+                $column->createRule()->setRule(
+                    '',
+                    [
+                        'year' => (string) $dateGroupItem['year'],
+                        'month' => (string) $dateGroupItem['month'],
+                        'day' => (string) $dateGroupItem['day'],
+                        'hour' => (string) $dateGroupItem['hour'],
+                        'minute' => (string) $dateGroupItem['minute'],
+                        'second' => (string) $dateGroupItem['second'],
+                    ],
+                    (string) $dateGroupItem['dateTimeGrouping']
+                )->setRuleType(Rule::AUTOFILTER_RULETYPE_DATEGROUP);
+            }
         }
     }
 
@@ -98,15 +96,17 @@ class AutoFilter
         if (isset($filterColumn, $filterColumn->customFilters)) {
             $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
             $customFilters = $filterColumn->customFilters;
+            $attributes = $customFilters->attributes();
             //    Custom filters can an AND or an OR join;
             //        and there should only ever be one or two entries
-            if ((isset($customFilters['and'])) && ((string) $customFilters['and'] === '1')) {
+            if ((isset($attributes['and'])) && ((string) $attributes['and'] === '1')) {
                 $column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
             }
             foreach ($customFilters->customFilter as $filterRule) {
+                $attr2 = $filterRule->attributes() ?? ['operator' => '', 'val' => ''];
                 $column->createRule()->setRule(
-                    (string) $filterRule['operator'],
-                    (string) $filterRule['val']
+                    (string) $attr2['operator'],
+                    (string) $attr2['val']
                 )->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
             }
         }
@@ -119,16 +119,17 @@ class AutoFilter
             //    We should only ever have one dynamic filter
             foreach ($filterColumn->dynamicFilter as $filterRule) {
                 //    Operator is undefined, but always treated as EQUAL
+                $attr2 = $filterRule->attributes() ?? [];
                 $column->createRule()->setRule(
                     '',
-                    (string) $filterRule['val'],
-                    (string) $filterRule['type']
+                    (string) ($attr2['val'] ?? ''),
+                    (string) ($attr2['type'] ?? '')
                 )->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
-                if (isset($filterRule['val'])) {
-                    $column->setAttribute('val', (string) $filterRule['val']);
+                if (isset($attr2['val'])) {
+                    $column->setAttribute('val', (string) $attr2['val']);
                 }
-                if (isset($filterRule['maxVal'])) {
-                    $column->setAttribute('maxVal', (string) $filterRule['maxVal']);
+                if (isset($attr2['maxVal'])) {
+                    $column->setAttribute('maxVal', (string) $attr2['maxVal']);
                 }
             }
         }
@@ -140,15 +141,16 @@ class AutoFilter
             $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
             //    We should only ever have one top10 filter
             foreach ($filterColumn->top10 as $filterRule) {
+                $attr2 = $filterRule->attributes() ?? [];
                 $column->createRule()->setRule(
                     (
-                        ((isset($filterRule['percent'])) && ((string) $filterRule['percent'] === '1'))
+                        ((isset($attr2['percent'])) && ((string) $attr2['percent'] === '1'))
                         ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
                         : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
                     ),
-                    (string) $filterRule['val'],
+                    (string) ($attr2['val'] ?? ''),
                     (
-                        ((isset($filterRule['top'])) && ((string) $filterRule['top'] === '1'))
+                        ((isset($attr2['top'])) && ((string) $attr2['top'] === '1'))
                         ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
                         : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
                     )
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
index 6b99877..beea6bb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
@@ -2,15 +2,14 @@
 
 namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
 
+use Stringable;
+
 class BaseParserClass
 {
-    /**
-     * @param mixed $value
-     */
-    protected static function boolean($value): bool
+    protected static function boolean(mixed $value): bool
     {
         if (is_object($value)) {
-            $value = (string) $value; // @phpstan-ignore-line
+            $value = ($value instanceof Stringable) ? ((string) $value) : 'true';
         }
 
         if (is_numeric($value)) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
index d42df9a..94d9c6a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
 use PhpOffice\PhpSpreadsheet\Chart\Axis;
+use PhpOffice\PhpSpreadsheet\Chart\AxisText;
 use PhpOffice\PhpSpreadsheet\Chart\ChartColor;
 use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
 use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
@@ -21,11 +22,9 @@ use SimpleXMLElement;
 
 class Chart
 {
-    /** @var string */
-    private $cNamespace;
+    private string $cNamespace;
 
-    /** @var string */
-    private $aNamespace;
+    private string $aNamespace;
 
     public function __construct(string $cNamespace = Namespaces::CHART, string $aNamespace = Namespaces::DRAWINGML)
     {
@@ -33,64 +32,88 @@ class Chart
         $this->aNamespace = $aNamespace;
     }
 
-    /**
-     * @param string $name
-     * @param string $format
-     *
-     * @return null|bool|float|int|string
-     */
-    private static function getAttribute(SimpleXMLElement $component, $name, $format)
+    private static function getAttributeString(SimpleXMLElement $component, string $name): string|null
     {
         $attributes = $component->attributes();
         if (@isset($attributes[$name])) {
-            if ($format == 'string') {
-                return (string) $attributes[$name];
-            } elseif ($format == 'integer') {
-                return (int) $attributes[$name];
-            } elseif ($format == 'boolean') {
-                $value = (string) $attributes[$name];
+            return (string) $attributes[$name];
+        }
 
-                return $value === 'true' || $value === '1';
-            }
+        return null;
+    }
 
+    private static function getAttributeInteger(SimpleXMLElement $component, string $name): int|null
+    {
+        $attributes = $component->attributes();
+        if (@isset($attributes[$name])) {
+            return (int) $attributes[$name];
+        }
+
+        return null;
+    }
+
+    private static function getAttributeBoolean(SimpleXMLElement $component, string $name): bool|null
+    {
+        $attributes = $component->attributes();
+        if (@isset($attributes[$name])) {
+            $value = (string) $attributes[$name];
+
+            return $value === 'true' || $value === '1';
+        }
+
+        return null;
+    }
+
+    private static function getAttributeFloat(SimpleXMLElement $component, string $name): float|null
+    {
+        $attributes = $component->attributes();
+        if (@isset($attributes[$name])) {
             return (float) $attributes[$name];
         }
 
         return null;
     }
 
-    /**
-     * @param string $chartName
-     *
-     * @return \PhpOffice\PhpSpreadsheet\Chart\Chart
-     */
-    public function readChart(SimpleXMLElement $chartElements, $chartName)
+    public function readChart(SimpleXMLElement $chartElements, string $chartName): \PhpOffice\PhpSpreadsheet\Chart\Chart
     {
         $chartElementsC = $chartElements->children($this->cNamespace);
 
         $XaxisLabel = $YaxisLabel = $legend = $title = null;
-        $dispBlanksAs = $plotVisOnly = null;
+        $dispBlanksAs = null;
+        $plotVisOnly = false;
         $plotArea = null;
         $rotX = $rotY = $rAngAx = $perspective = null;
         $xAxis = new Axis();
         $yAxis = new Axis();
         $autoTitleDeleted = null;
         $chartNoFill = false;
+        $chartBorderLines = null;
+        $chartFillColor = null;
         $gradientArray = [];
         $gradientLin = null;
         $roundedCorners = false;
+        $gapWidth = null;
+        $useUpBars = null;
+        $useDownBars = null;
         foreach ($chartElementsC as $chartElementKey => $chartElement) {
             switch ($chartElementKey) {
                 case 'spPr':
-                    $possibleNoFill = $chartElementsC->spPr->children($this->aNamespace);
-                    if (isset($possibleNoFill->noFill)) {
+                    $children = $chartElementsC->spPr->children($this->aNamespace);
+                    if (isset($children->noFill)) {
                         $chartNoFill = true;
                     }
+                    if (isset($children->solidFill)) {
+                        $chartFillColor = $this->readColor($children->solidFill);
+                    }
+                    if (isset($children->ln)) {
+                        $chartBorderLines = new GridLines();
+                        $this->readLineStyle($chartElementsC, $chartBorderLines);
+                    }
 
                     break;
                 case 'roundedCorners':
-                    /** @var bool */
-                    $roundedCorners = self::getAttribute($chartElementsC->roundedCorners, 'val', 'boolean');
+                    /** @var bool $roundedCorners */
+                    $roundedCorners = self::getAttributeBoolean($chartElementsC->roundedCorners, 'val');
 
                     break;
                 case 'chart':
@@ -98,15 +121,15 @@ class Chart
                         $chartDetails = Xlsx::testSimpleXml($chartDetails);
                         switch ($chartDetailsKey) {
                             case 'autoTitleDeleted':
-                                /** @var bool */
-                                $autoTitleDeleted = self::getAttribute($chartElementsC->chart->autoTitleDeleted, 'val', 'boolean');
+                                /** @var bool $autoTitleDeleted */
+                                $autoTitleDeleted = self::getAttributeBoolean($chartElementsC->chart->autoTitleDeleted, 'val');
 
                                 break;
                             case 'view3D':
-                                $rotX = self::getAttribute($chartDetails->rotX, 'val', 'integer');
-                                $rotY = self::getAttribute($chartDetails->rotY, 'val', 'integer');
-                                $rAngAx = self::getAttribute($chartDetails->rAngAx, 'val', 'integer');
-                                $perspective = self::getAttribute($chartDetails->perspective, 'val', 'integer');
+                                $rotX = self::getAttributeInteger($chartDetails->rotX, 'val');
+                                $rotY = self::getAttributeInteger($chartDetails->rotY, 'val');
+                                $rAngAx = self::getAttributeInteger($chartDetails->rAngAx, 'val');
+                                $perspective = self::getAttributeInteger($chartDetails->perspective, 'val');
 
                                 break;
                             case 'plotArea':
@@ -125,8 +148,8 @@ class Chart
                                             if (isset($possibleNoFill->gradFill->gsLst)) {
                                                 foreach ($possibleNoFill->gradFill->gsLst->gs as $gradient) {
                                                     $gradient = Xlsx::testSimpleXml($gradient);
-                                                    /** @var float */
-                                                    $pos = self::getAttribute($gradient, 'pos', 'float');
+                                                    /** @var float $pos */
+                                                    $pos = self::getAttributeFloat($gradient, 'pos');
                                                     $gradientArray[] = [
                                                         $pos / ChartProperties::PERCENTAGE_MULTIPLIER,
                                                         new ChartColor($this->readColor($gradient)),
@@ -134,7 +157,7 @@ class Chart
                                                 }
                                             }
                                             if (isset($possibleNoFill->gradFill->lin)) {
-                                                $gradientLin = ChartProperties::XmlToAngle((string) self::getAttribute($possibleNoFill->gradFill->lin, 'ang', 'string'));
+                                                $gradientLin = ChartProperties::XmlToAngle((string) self::getAttributeString($possibleNoFill->gradFill->lin, 'ang'));
                                             }
 
                                             break;
@@ -157,6 +180,9 @@ class Chart
                                                     $axisColorArray = $this->readColor($sppr->solidFill);
                                                     $xAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
                                                 }
+                                                if (isset($chartDetail->spPr->ln->noFill)) {
+                                                    $xAxis->setNoFill(true);
+                                                }
                                             }
                                             if (isset($chartDetail->majorGridlines)) {
                                                 $majorGridlines = new GridLines();
@@ -182,7 +208,7 @@ class Chart
                                             $whichAxis = null;
                                             $axPos = null;
                                             if (isset($chartDetail->axPos)) {
-                                                $axPos = self::getAttribute($chartDetail->axPos, 'val', 'string');
+                                                $axPos = self::getAttributeString($chartDetail->axPos, 'val');
                                             }
                                             if ($catAxRead) {
                                                 $whichAxis = $yAxis;
@@ -227,6 +253,9 @@ class Chart
                                                     $axisColorArray = $this->readColor($sppr->solidFill);
                                                     $whichAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
                                                 }
+                                                if (isset($sppr->ln->noFill)) {
+                                                    $whichAxis->setNoFill(true);
+                                                }
                                             }
                                             if ($whichAxis !== null && isset($chartDetail->majorGridlines)) {
                                                 $majorGridlines = new GridLines();
@@ -250,7 +279,7 @@ class Chart
                                             break;
                                         case 'barChart':
                                         case 'bar3DChart':
-                                            $barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
+                                            $barDirection = self::getAttributeString($chartDetail->barDir, 'val');
                                             $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
                                             $plotSer->setPlotDirection("$barDirection");
                                             $plotSeries[] = $plotSer;
@@ -272,7 +301,7 @@ class Chart
                                         case 'doughnutChart':
                                         case 'pieChart':
                                         case 'pie3DChart':
-                                            $explosion = self::getAttribute($chartDetail->ser->explosion, 'val', 'string');
+                                            $explosion = self::getAttributeString($chartDetail->ser->explosion, 'val');
                                             $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
                                             $plotSer->setPlotStyle("$explosion");
                                             $plotSeries[] = $plotSer;
@@ -280,8 +309,8 @@ class Chart
 
                                             break;
                                         case 'scatterChart':
-                                            /** @var string */
-                                            $scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
+                                            /** @var string $scatterStyle */
+                                            $scatterStyle = self::getAttributeString($chartDetail->scatterStyle, 'val');
                                             $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
                                             $plotSer->setPlotStyle($scatterStyle);
                                             $plotSeries[] = $plotSer;
@@ -289,7 +318,7 @@ class Chart
 
                                             break;
                                         case 'bubbleChart':
-                                            $bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
+                                            $bubbleScale = self::getAttributeInteger($chartDetail->bubbleScale, 'val');
                                             $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
                                             $plotSer->setPlotStyle("$bubbleScale");
                                             $plotSeries[] = $plotSer;
@@ -297,8 +326,8 @@ class Chart
 
                                             break;
                                         case 'radarChart':
-                                            /** @var string */
-                                            $radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
+                                            /** @var string $radarStyle */
+                                            $radarStyle = self::getAttributeString($chartDetail->radarStyle, 'val');
                                             $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
                                             $plotSer->setPlotStyle($radarStyle);
                                             $plotSeries[] = $plotSer;
@@ -307,7 +336,7 @@ class Chart
                                             break;
                                         case 'surfaceChart':
                                         case 'surface3DChart':
-                                            $wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
+                                            $wireFrame = self::getAttributeBoolean($chartDetail->wireframe, 'val');
                                             $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
                                             $plotSer->setPlotStyle("$wireFrame");
                                             $plotSeries[] = $plotSer;
@@ -316,6 +345,15 @@ class Chart
                                             break;
                                         case 'stockChart':
                                             $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
+                                            if (isset($chartDetail->upDownBars->gapWidth)) {
+                                                $gapWidth = self::getAttributeInteger($chartDetail->upDownBars->gapWidth, 'val');
+                                            }
+                                            if (isset($chartDetail->upDownBars->upBars)) {
+                                                $useUpBars = true;
+                                            }
+                                            if (isset($chartDetail->upDownBars->downBars)) {
+                                                $useDownBars = true;
+                                            }
                                             $plotAttributes = $this->readChartAttributes($chartDetail);
 
                                             break;
@@ -332,14 +370,23 @@ class Chart
                                 if (!empty($gradientArray)) {
                                     $plotArea->setGradientFillProperties($gradientArray, $gradientLin);
                                 }
+                                if (is_int($gapWidth)) {
+                                    $plotArea->setGapWidth($gapWidth);
+                                }
+                                if ($useUpBars === true) {
+                                    $plotArea->setUseUpBars(true);
+                                }
+                                if ($useDownBars === true) {
+                                    $plotArea->setUseDownBars(true);
+                                }
 
                                 break;
                             case 'plotVisOnly':
-                                $plotVisOnly = self::getAttribute($chartDetails, 'val', 'string');
+                                $plotVisOnly = (bool) self::getAttributeString($chartDetails, 'val');
 
                                 break;
                             case 'dispBlanksAs':
-                                $dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string');
+                                $dispBlanksAs = self::getAttributeString($chartDetails, 'val');
 
                                 break;
                             case 'title':
@@ -350,24 +397,63 @@ class Chart
                                 $legendPos = 'r';
                                 $legendLayout = null;
                                 $legendOverlay = false;
+                                $legendBorderLines = null;
+                                $legendFillColor = null;
+                                $legendText = null;
+                                $addLegendText = false;
                                 foreach ($chartDetails as $chartDetailKey => $chartDetail) {
                                     $chartDetail = Xlsx::testSimpleXml($chartDetail);
                                     switch ($chartDetailKey) {
                                         case 'legendPos':
-                                            $legendPos = self::getAttribute($chartDetail, 'val', 'string');
+                                            $legendPos = self::getAttributeString($chartDetail, 'val');
 
                                             break;
                                         case 'overlay':
-                                            $legendOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
+                                            $legendOverlay = self::getAttributeBoolean($chartDetail, 'val');
 
                                             break;
                                         case 'layout':
                                             $legendLayout = $this->chartLayoutDetails($chartDetail);
 
+                                            break;
+                                        case 'spPr':
+                                            $children = $chartDetails->spPr->children($this->aNamespace);
+                                            if (isset($children->solidFill)) {
+                                                $legendFillColor = $this->readColor($children->solidFill);
+                                            }
+                                            if (isset($children->ln)) {
+                                                $legendBorderLines = new GridLines();
+                                                $this->readLineStyle($chartDetails, $legendBorderLines);
+                                            }
+
+                                            break;
+                                        case 'txPr':
+                                            $children = $chartDetails->txPr->children($this->aNamespace);
+                                            $addLegendText = false;
+                                            $legendText = new AxisText();
+                                            if (isset($children->p->pPr->defRPr->solidFill)) {
+                                                $colorArray = $this->readColor($children->p->pPr->defRPr->solidFill);
+                                                $legendText->getFillColorObject()->setColorPropertiesArray($colorArray);
+                                                $addLegendText = true;
+                                            }
+                                            if (isset($children->p->pPr->defRPr->effectLst)) {
+                                                $this->readEffects($children->p->pPr->defRPr, $legendText, false);
+                                                $addLegendText = true;
+                                            }
+
                                             break;
                                     }
                                 }
                                 $legend = new Legend("$legendPos", $legendLayout, (bool) $legendOverlay);
+                                if ($legendFillColor !== null) {
+                                    $legend->getFillColor()->setColorPropertiesArray($legendFillColor);
+                                }
+                                if ($legendBorderLines !== null) {
+                                    $legend->setBorderLines($legendBorderLines);
+                                }
+                                if ($addLegendText) {
+                                    $legend->setLegendText($legendText);
+                                }
 
                                 break;
                         }
@@ -378,6 +464,12 @@ class Chart
         if ($chartNoFill) {
             $chart->setNoFill(true);
         }
+        if ($chartFillColor !== null) {
+            $chart->getFillColor()->setColorPropertiesArray($chartFillColor);
+        }
+        if ($chartBorderLines !== null) {
+            $chart->setBorderLines($chartBorderLines);
+        }
         $chart->setRoundedCorners($roundedCorners);
         if (is_bool($autoTitleDeleted)) {
             $chart->setAutoTitleDeleted($autoTitleDeleted);
@@ -400,12 +492,16 @@ class Chart
 
     private function chartTitle(SimpleXMLElement $titleDetails): Title
     {
-        $caption = [];
+        $caption = '';
         $titleLayout = null;
+        $titleOverlay = false;
+        $titleFormula = null;
+        $titleFont = null;
         foreach ($titleDetails as $titleDetailKey => $chartDetail) {
             $chartDetail = Xlsx::testSimpleXml($chartDetail);
             switch ($titleDetailKey) {
                 case 'tx':
+                    $caption = [];
                     if (isset($chartDetail->rich)) {
                         $titleDetails = $chartDetail->rich->children($this->aNamespace);
                         foreach ($titleDetails as $titleKey => $titleDetail) {
@@ -422,17 +518,37 @@ class Chart
                                 $caption[] = (string) $pt->v;
                             }
                         }
+                        if (isset($chartDetail->strRef->f)) {
+                            $titleFormula = (string) $chartDetail->strRef->f;
+                        }
                     }
 
+                    break;
+                case 'overlay':
+                    $titleOverlay = self::getAttributeBoolean($chartDetail, 'val');
+
                     break;
                 case 'layout':
                     $titleLayout = $this->chartLayoutDetails($chartDetail);
 
+                    break;
+                case 'txPr':
+                    if (isset($chartDetail->children($this->aNamespace)->p)) {
+                        $titleFont = $this->parseFont($chartDetail->children($this->aNamespace)->p);
+                    }
+
                     break;
             }
         }
+        $title = new Title($caption, $titleLayout, (bool) $titleOverlay);
+        if (!empty($titleFormula)) {
+            $title->setCellReference($titleFormula);
+        }
+        if ($titleFont !== null) {
+            $title->setFont($titleFont);
+        }
 
-        return new Title($caption, $titleLayout);
+        return $title;
     }
 
     private function chartLayoutDetails(SimpleXMLElement $chartDetail): ?Layout
@@ -447,7 +563,7 @@ class Chart
         $layout = [];
         foreach ($details as $detailKey => $detail) {
             $detail = Xlsx::testSimpleXml($detail);
-            $layout[$detailKey] = self::getAttribute($detail, 'val', 'string');
+            $layout[$detailKey] = self::getAttributeString($detail, 'val');
         }
 
         return new Layout($layout);
@@ -458,12 +574,13 @@ class Chart
         $multiSeriesType = null;
         $smoothLine = false;
         $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = $seriesBubbles = [];
+        $plotDirection = null;
 
         $seriesDetailSet = $chartDetail->children($this->cNamespace);
         foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
             switch ($seriesDetailKey) {
                 case 'grouping':
-                    $multiSeriesType = self::getAttribute($chartDetail->grouping, 'val', 'string');
+                    $multiSeriesType = self::getAttributeString($chartDetail->grouping, 'val');
 
                     break;
                 case 'ser':
@@ -483,16 +600,21 @@ class Chart
                         $seriesDetail = Xlsx::testSimpleXml($seriesDetail);
                         switch ($seriesKey) {
                             case 'idx':
-                                $seriesIndex = self::getAttribute($seriesDetail, 'val', 'integer');
+                                $seriesIndex = self::getAttributeInteger($seriesDetail, 'val');
 
                                 break;
                             case 'order':
-                                $seriesOrder = self::getAttribute($seriesDetail, 'val', 'integer');
-                                $plotOrder[$seriesIndex] = $seriesOrder;
+                                $seriesOrder = self::getAttributeInteger($seriesDetail, 'val');
+                                if ($seriesOrder !== null) {
+                                    $plotOrder[$seriesIndex] = $seriesOrder;
+                                }
 
                                 break;
                             case 'tx':
-                                $seriesLabel[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
+                                $temp = $this->chartDataSeriesValueSet($seriesDetail);
+                                if ($temp !== null) {
+                                    $seriesLabel[$seriesIndex] = $temp;
+                                }
 
                                 break;
                             case 'spPr':
@@ -517,7 +639,7 @@ class Chart
 
                                 break;
                             case 'dPt':
-                                $dptIdx = (int) self::getAttribute($seriesDetail->idx, 'val', 'string');
+                                $dptIdx = (int) self::getAttributeString($seriesDetail->idx, 'val');
                                 if (isset($seriesDetail->spPr)) {
                                     $children = $seriesDetail->spPr->children($this->aNamespace);
                                     if (isset($children->solidFill)) {
@@ -530,23 +652,14 @@ class Chart
                             case 'trendline':
                                 $trendLine = new TrendLine();
                                 $this->readLineStyle($seriesDetail, $trendLine);
-                                /** @var ?string */
-                                $trendLineType = self::getAttribute($seriesDetail->trendlineType, 'val', 'string');
-                                /** @var ?bool */
-                                $dispRSqr = self::getAttribute($seriesDetail->dispRSqr, 'val', 'boolean');
-                                /** @var ?bool */
-                                $dispEq = self::getAttribute($seriesDetail->dispEq, 'val', 'boolean');
-                                /** @var ?int */
-                                $order = self::getAttribute($seriesDetail->order, 'val', 'integer');
-                                /** @var ?int */
-                                $period = self::getAttribute($seriesDetail->period, 'val', 'integer');
-                                /** @var ?float */
-                                $forward = self::getAttribute($seriesDetail->forward, 'val', 'float');
-                                /** @var ?float */
-                                $backward = self::getAttribute($seriesDetail->backward, 'val', 'float');
-                                /** @var ?float */
-                                $intercept = self::getAttribute($seriesDetail->intercept, 'val', 'float');
-                                /** @var ?string */
+                                $trendLineType = self::getAttributeString($seriesDetail->trendlineType, 'val');
+                                $dispRSqr = self::getAttributeBoolean($seriesDetail->dispRSqr, 'val');
+                                $dispEq = self::getAttributeBoolean($seriesDetail->dispEq, 'val');
+                                $order = self::getAttributeInteger($seriesDetail->order, 'val');
+                                $period = self::getAttributeInteger($seriesDetail->period, 'val');
+                                $forward = self::getAttributeFloat($seriesDetail->forward, 'val');
+                                $backward = self::getAttributeFloat($seriesDetail->backward, 'val');
+                                $intercept = self::getAttributeFloat($seriesDetail->intercept, 'val');
                                 $name = (string) $seriesDetail->name;
                                 $trendLine->setTrendLineProperties(
                                     $trendLineType,
@@ -563,8 +676,8 @@ class Chart
 
                                 break;
                             case 'marker':
-                                $marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
-                                $pointSize = self::getAttribute($seriesDetail->size, 'val', 'string');
+                                $marker = self::getAttributeString($seriesDetail->symbol, 'val');
+                                $pointSize = self::getAttributeString($seriesDetail->size, 'val');
                                 $pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null;
                                 if (isset($seriesDetail->spPr)) {
                                     $children = $seriesDetail->spPr->children($this->aNamespace);
@@ -578,31 +691,46 @@ class Chart
 
                                 break;
                             case 'smooth':
-                                $smoothLine = self::getAttribute($seriesDetail, 'val', 'boolean');
+                                $smoothLine = self::getAttributeBoolean($seriesDetail, 'val') ?? false;
 
                                 break;
                             case 'cat':
-                                $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
+                                $temp = $this->chartDataSeriesValueSet($seriesDetail);
+                                if ($temp !== null) {
+                                    $seriesCategory[$seriesIndex] = $temp;
+                                }
 
                                 break;
                             case 'val':
-                                $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                $temp = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                if ($temp !== null) {
+                                    $seriesValues[$seriesIndex] = $temp;
+                                }
 
                                 break;
                             case 'xVal':
-                                $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                $temp = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                if ($temp !== null) {
+                                    $seriesCategory[$seriesIndex] = $temp;
+                                }
 
                                 break;
                             case 'yVal':
-                                $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                $temp = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                if ($temp !== null) {
+                                    $seriesValues[$seriesIndex] = $temp;
+                                }
 
                                 break;
                             case 'bubbleSize':
-                                $seriesBubbles[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                $seriesBubble = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
+                                if ($seriesBubble !== null) {
+                                    $seriesBubbles[$seriesIndex] = $seriesBubble;
+                                }
 
                                 break;
                             case 'bubble3D':
-                                $bubble3D = self::getAttribute($seriesDetail, 'val', 'boolean');
+                                $bubble3D = self::getAttributeBoolean($seriesDetail, 'val');
 
                                 break;
                             case 'dLbls':
@@ -712,17 +840,13 @@ class Chart
                     }
             }
         }
-        /** @phpstan-ignore-next-line */
-        $series = new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
+        $series = new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $plotDirection, $smoothLine);
         $series->setPlotBubbleSizes($seriesBubbles);
 
         return $series;
     }
 
-    /**
-     * @return mixed
-     */
-    private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?ChartColor $fillColor = null, ?string $pointSize = null)
+    private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?ChartColor $fillColor = null, ?string $pointSize = null): ?DataSeriesValues
     {
         if (isset($seriesDetail->strRef)) {
             $seriesSource = (string) $seriesDetail->strRef->f;
@@ -796,7 +920,7 @@ class Chart
             $seriesValue = Xlsx::testSimpleXml($seriesValue);
             switch ($seriesValueIdx) {
                 case 'ptCount':
-                    $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
+                    $pointCount = self::getAttributeInteger($seriesValue, 'val');
 
                     break;
                 case 'formatCode':
@@ -804,7 +928,7 @@ class Chart
 
                     break;
                 case 'pt':
-                    $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
+                    $pointVal = self::getAttributeInteger($seriesValue, 'idx');
                     if ($dataType == 's') {
                         $seriesVal[$pointVal] = (string) $seriesValue->v;
                     } elseif ((string) $seriesValue->v === ExcelError::NA()) {
@@ -835,7 +959,7 @@ class Chart
                 $seriesValue = Xlsx::testSimpleXml($seriesValue);
                 switch ($seriesValueIdx) {
                     case 'ptCount':
-                        $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
+                        $pointCount = self::getAttributeInteger($seriesValue, 'val');
 
                         break;
                     case 'formatCode':
@@ -843,7 +967,7 @@ class Chart
 
                         break;
                     case 'pt':
-                        $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
+                        $pointVal = self::getAttributeInteger($seriesValue, 'idx');
                         if ($dataType == 's') {
                             $seriesVal[$pointVal][] = (string) $seriesValue->v;
                         } elseif ((string) $seriesValue->v === ExcelError::NA()) {
@@ -879,32 +1003,23 @@ class Chart
         $defaultComplexScript = null;
         $defaultFontColor = null;
         if (isset($titleDetailPart->pPr->defRPr)) {
-            /** @var ?int */
-            $defaultFontSize = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer');
-            /** @var ?bool */
-            $defaultBold = self::getAttribute($titleDetailPart->pPr->defRPr, 'b', 'boolean');
-            /** @var ?bool */
-            $defaultItalic = self::getAttribute($titleDetailPart->pPr->defRPr, 'i', 'boolean');
-            /** @var ?string */
-            $defaultUnderscore = self::getAttribute($titleDetailPart->pPr->defRPr, 'u', 'string');
-            /** @var ?string */
-            $defaultStrikethrough = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string');
-            /** @var ?int */
-            $defaultBaseline = self::getAttribute($titleDetailPart->pPr->defRPr, 'baseline', 'integer');
+            $defaultFontSize = self::getAttributeInteger($titleDetailPart->pPr->defRPr, 'sz');
+            $defaultBold = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'b');
+            $defaultItalic = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'i');
+            $defaultUnderscore = self::getAttributeString($titleDetailPart->pPr->defRPr, 'u');
+            $defaultStrikethrough = self::getAttributeString($titleDetailPart->pPr->defRPr, 'strike');
+            $defaultBaseline = self::getAttributeInteger($titleDetailPart->pPr->defRPr, 'baseline');
             if (isset($titleDetailPart->defRPr->rFont['val'])) {
                 $defaultFontName = (string) $titleDetailPart->defRPr->rFont['val'];
             }
             if (isset($titleDetailPart->pPr->defRPr->latin)) {
-                /** @var ?string */
-                $defaultLatin = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string');
+                $defaultLatin = self::getAttributeString($titleDetailPart->pPr->defRPr->latin, 'typeface');
             }
             if (isset($titleDetailPart->pPr->defRPr->ea)) {
-                /** @var ?string */
-                $defaultEastAsian = self::getAttribute($titleDetailPart->pPr->defRPr->ea, 'typeface', 'string');
+                $defaultEastAsian = self::getAttributeString($titleDetailPart->pPr->defRPr->ea, 'typeface');
             }
             if (isset($titleDetailPart->pPr->defRPr->cs)) {
-                /** @var ?string */
-                $defaultComplexScript = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string');
+                $defaultComplexScript = self::getAttributeString($titleDetailPart->pPr->defRPr->cs, 'typeface');
             }
             if (isset($titleDetailPart->pPr->defRPr->solidFill)) {
                 $defaultFontColor = $this->readColor($titleDetailPart->pPr->defRPr->solidFill);
@@ -943,42 +1058,30 @@ class Chart
                     // @codeCoverageIgnoreEnd
                 }
                 if (isset($titleDetailElement->rPr->latin)) {
-                    /** @var ?string */
-                    $latinName = self::getAttribute($titleDetailElement->rPr->latin, 'typeface', 'string');
+                    $latinName = self::getAttributeString($titleDetailElement->rPr->latin, 'typeface');
                 }
                 if (isset($titleDetailElement->rPr->ea)) {
-                    /** @var ?string */
-                    $eastAsian = self::getAttribute($titleDetailElement->rPr->ea, 'typeface', 'string');
+                    $eastAsian = self::getAttributeString($titleDetailElement->rPr->ea, 'typeface');
                 }
                 if (isset($titleDetailElement->rPr->cs)) {
-                    /** @var ?string */
-                    $complexScript = self::getAttribute($titleDetailElement->rPr->cs, 'typeface', 'string');
+                    $complexScript = self::getAttributeString($titleDetailElement->rPr->cs, 'typeface');
                 }
-                /** @var ?int */
-                $fontSize = self::getAttribute($titleDetailElement->rPr, 'sz', 'integer');
+                $fontSize = self::getAttributeInteger($titleDetailElement->rPr, 'sz');
 
                 // not used now, not sure it ever was, grandfathering
                 if (isset($titleDetailElement->rPr->solidFill)) {
                     $fontColor = $this->readColor($titleDetailElement->rPr->solidFill);
                 }
 
-                /** @var ?bool */
-                $bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
-
-                /** @var ?bool */
-                $italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
-
-                /** @var ?int */
-                $baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
-
-                /** @var ?string */
-                $underscore = self::getAttribute($titleDetailElement->rPr, 'u', 'string');
+                $bold = self::getAttributeBoolean($titleDetailElement->rPr, 'b');
+                $italic = self::getAttributeBoolean($titleDetailElement->rPr, 'i');
+                $baseline = self::getAttributeInteger($titleDetailElement->rPr, 'baseline');
+                $underscore = self::getAttributeString($titleDetailElement->rPr, 'u');
                 if (isset($titleDetailElement->rPr->uFill->solidFill)) {
                     $underlineColor = $this->readColor($titleDetailElement->rPr->uFill->solidFill);
                 }
 
-                /** @var ?string */
-                $strikethrough = self::getAttribute($titleDetailElement->rPr, 'strike', 'string');
+                $strikethrough = self::getAttributeString($titleDetailElement->rPr, 'strike');
             }
 
             $fontFound = false;
@@ -1077,40 +1180,69 @@ class Chart
         return $value;
     }
 
-    /**
-     * @param ?SimpleXMLElement $chartDetail
-     */
-    private function readChartAttributes($chartDetail): array
+    private function parseFont(SimpleXMLElement $titleDetailPart): ?Font
+    {
+        if (!isset($titleDetailPart->pPr->defRPr)) {
+            return null;
+        }
+        $fontArray = [];
+        $fontArray['size'] = self::getAttributeInteger($titleDetailPart->pPr->defRPr, 'sz');
+        $fontArray['bold'] = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'b');
+        $fontArray['italic'] = self::getAttributeBoolean($titleDetailPart->pPr->defRPr, 'i');
+        $fontArray['underscore'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'u');
+        $fontArray['strikethrough'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'strike');
+        $fontArray['cap'] = self::getAttributeString($titleDetailPart->pPr->defRPr, 'cap');
+
+        if (isset($titleDetailPart->pPr->defRPr->latin)) {
+            $fontArray['latin'] = self::getAttributeString($titleDetailPart->pPr->defRPr->latin, 'typeface');
+        }
+        if (isset($titleDetailPart->pPr->defRPr->ea)) {
+            $fontArray['eastAsian'] = self::getAttributeString($titleDetailPart->pPr->defRPr->ea, 'typeface');
+        }
+        if (isset($titleDetailPart->pPr->defRPr->cs)) {
+            $fontArray['complexScript'] = self::getAttributeString($titleDetailPart->pPr->defRPr->cs, 'typeface');
+        }
+        if (isset($titleDetailPart->pPr->defRPr->solidFill)) {
+            $fontArray['chartColor'] = new ChartColor($this->readColor($titleDetailPart->pPr->defRPr->solidFill));
+        }
+        $font = new Font();
+        $font->setSize(null, true);
+        $font->applyFromArray($fontArray);
+
+        return $font;
+    }
+
+    private function readChartAttributes(?SimpleXMLElement $chartDetail): array
     {
         $plotAttributes = [];
         if (isset($chartDetail->dLbls)) {
             if (isset($chartDetail->dLbls->dLblPos)) {
-                $plotAttributes['dLblPos'] = self::getAttribute($chartDetail->dLbls->dLblPos, 'val', 'string');
+                $plotAttributes['dLblPos'] = self::getAttributeString($chartDetail->dLbls->dLblPos, 'val');
             }
             if (isset($chartDetail->dLbls->numFmt)) {
-                $plotAttributes['numFmtCode'] = self::getAttribute($chartDetail->dLbls->numFmt, 'formatCode', 'string');
-                $plotAttributes['numFmtLinked'] = self::getAttribute($chartDetail->dLbls->numFmt, 'sourceLinked', 'boolean');
+                $plotAttributes['numFmtCode'] = self::getAttributeString($chartDetail->dLbls->numFmt, 'formatCode');
+                $plotAttributes['numFmtLinked'] = self::getAttributeBoolean($chartDetail->dLbls->numFmt, 'sourceLinked');
             }
             if (isset($chartDetail->dLbls->showLegendKey)) {
-                $plotAttributes['showLegendKey'] = self::getAttribute($chartDetail->dLbls->showLegendKey, 'val', 'string');
+                $plotAttributes['showLegendKey'] = self::getAttributeString($chartDetail->dLbls->showLegendKey, 'val');
             }
             if (isset($chartDetail->dLbls->showVal)) {
-                $plotAttributes['showVal'] = self::getAttribute($chartDetail->dLbls->showVal, 'val', 'string');
+                $plotAttributes['showVal'] = self::getAttributeString($chartDetail->dLbls->showVal, 'val');
             }
             if (isset($chartDetail->dLbls->showCatName)) {
-                $plotAttributes['showCatName'] = self::getAttribute($chartDetail->dLbls->showCatName, 'val', 'string');
+                $plotAttributes['showCatName'] = self::getAttributeString($chartDetail->dLbls->showCatName, 'val');
             }
             if (isset($chartDetail->dLbls->showSerName)) {
-                $plotAttributes['showSerName'] = self::getAttribute($chartDetail->dLbls->showSerName, 'val', 'string');
+                $plotAttributes['showSerName'] = self::getAttributeString($chartDetail->dLbls->showSerName, 'val');
             }
             if (isset($chartDetail->dLbls->showPercent)) {
-                $plotAttributes['showPercent'] = self::getAttribute($chartDetail->dLbls->showPercent, 'val', 'string');
+                $plotAttributes['showPercent'] = self::getAttributeString($chartDetail->dLbls->showPercent, 'val');
             }
             if (isset($chartDetail->dLbls->showBubbleSize)) {
-                $plotAttributes['showBubbleSize'] = self::getAttribute($chartDetail->dLbls->showBubbleSize, 'val', 'string');
+                $plotAttributes['showBubbleSize'] = self::getAttributeString($chartDetail->dLbls->showBubbleSize, 'val');
             }
             if (isset($chartDetail->dLbls->showLeaderLines)) {
-                $plotAttributes['showLeaderLines'] = self::getAttribute($chartDetail->dLbls->showLeaderLines, 'val', 'string');
+                $plotAttributes['showLeaderLines'] = self::getAttributeString($chartDetail->dLbls->showLeaderLines, 'val');
             }
             if (isset($chartDetail->dLbls->spPr)) {
                 $sppr = $chartDetail->dLbls->spPr->children($this->aNamespace);
@@ -1123,8 +1255,13 @@ class Chart
             }
             if (isset($chartDetail->dLbls->txPr)) {
                 $txpr = $chartDetail->dLbls->txPr->children($this->aNamespace);
-                if (isset($txpr->p->pPr->defRPr->solidFill)) {
-                    $plotAttributes['labelFontColor'] = new ChartColor($this->readColor($txpr->p->pPr->defRPr->solidFill));
+                if (isset($txpr->p)) {
+                    $plotAttributes['labelFont'] = $this->parseFont($txpr->p);
+                    if (isset($txpr->p->pPr->defRPr->effectLst)) {
+                        $labelEffects = new GridLines();
+                        $this->readEffects($txpr->p->pPr->defRPr, $labelEffects, false);
+                        $plotAttributes['labelEffects'] = $labelEffects;
+                    }
                 }
             }
         }
@@ -1132,10 +1269,7 @@ class Chart
         return $plotAttributes;
     }
 
-    /**
-     * @param mixed $plotAttributes
-     */
-    private function setChartAttributes(Layout $plotArea, $plotAttributes): void
+    private function setChartAttributes(Layout $plotArea, array $plotAttributes): void
     {
         foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
             switch ($plotAttributeKey) {
@@ -1171,15 +1305,21 @@ class Chart
         }
     }
 
-    private function readEffects(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject): void
+    private function readEffects(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject, bool $getSppr = true): void
     {
-        if (!isset($chartObject, $chartDetail->spPr)) {
+        if (!isset($chartObject)) {
             return;
         }
-        $sppr = $chartDetail->spPr->children($this->aNamespace);
-
+        if ($getSppr) {
+            if (!isset($chartDetail->spPr)) {
+                return;
+            }
+            $sppr = $chartDetail->spPr->children($this->aNamespace);
+        } else {
+            $sppr = $chartDetail;
+        }
         if (isset($sppr->effectLst->glow)) {
-            $axisGlowSize = (float) self::getAttribute($sppr->effectLst->glow, 'rad', 'integer') / ChartProperties::POINTS_WIDTH_MULTIPLIER;
+            $axisGlowSize = (float) self::getAttributeInteger($sppr->effectLst->glow, 'rad') / ChartProperties::POINTS_WIDTH_MULTIPLIER;
             if ($axisGlowSize != 0.0) {
                 $colorArray = $this->readColor($sppr->effectLst->glow);
                 $chartObject->setGlowProperties($axisGlowSize, $colorArray['value'], $colorArray['alpha'], $colorArray['type']);
@@ -1187,8 +1327,7 @@ class Chart
         }
 
         if (isset($sppr->effectLst->softEdge)) {
-            /** @var string */
-            $softEdgeSize = self::getAttribute($sppr->effectLst->softEdge, 'rad', 'string');
+            $softEdgeSize = self::getAttributeString($sppr->effectLst->softEdge, 'rad');
             if (is_numeric($softEdgeSize)) {
                 $chartObject->setSoftEdges((float) ChartProperties::xmlToPoints($softEdgeSize));
             }
@@ -1203,20 +1342,17 @@ class Chart
             }
         }
         if ($type !== '') {
-            /** @var string */
-            $blur = self::getAttribute($sppr->effectLst->$type, 'blurRad', 'string');
+            $blur = self::getAttributeString($sppr->effectLst->$type, 'blurRad');
             $blur = is_numeric($blur) ? ChartProperties::xmlToPoints($blur) : null;
-            /** @var string */
-            $dist = self::getAttribute($sppr->effectLst->$type, 'dist', 'string');
+            $dist = self::getAttributeString($sppr->effectLst->$type, 'dist');
             $dist = is_numeric($dist) ? ChartProperties::xmlToPoints($dist) : null;
-            /** @var string */
-            $direction = self::getAttribute($sppr->effectLst->$type, 'dir', 'string');
+            $direction = self::getAttributeString($sppr->effectLst->$type, 'dir');
             $direction = is_numeric($direction) ? ChartProperties::xmlToAngle($direction) : null;
-            $algn = self::getAttribute($sppr->effectLst->$type, 'algn', 'string');
-            $rot = self::getAttribute($sppr->effectLst->$type, 'rotWithShape', 'string');
+            $algn = self::getAttributeString($sppr->effectLst->$type, 'algn');
+            $rot = self::getAttributeString($sppr->effectLst->$type, 'rotWithShape');
             $size = [];
             foreach (['sx', 'sy'] as $sizeType) {
-                $sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
+                $sizeValue = self::getAttributeString($sppr->effectLst->$type, $sizeType);
                 if (is_numeric($sizeValue)) {
                     $size[$sizeType] = ChartProperties::xmlToTenthOfPercent((string) $sizeValue);
                 } else {
@@ -1224,7 +1360,7 @@ class Chart
                 }
             }
             foreach (['kx', 'ky'] as $sizeType) {
-                $sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
+                $sizeValue = self::getAttributeString($sppr->effectLst->$type, $sizeType);
                 if (is_numeric($sizeValue)) {
                     $size[$sizeType] = ChartProperties::xmlToAngle((string) $sizeValue);
                 } else {
@@ -1260,17 +1396,15 @@ class Chart
         foreach (ChartColor::EXCEL_COLOR_TYPES as $type) {
             if (isset($colorXml->$type)) {
                 $result['type'] = $type;
-                $result['value'] = self::getAttribute($colorXml->$type, 'val', 'string');
+                $result['value'] = self::getAttributeString($colorXml->$type, 'val');
                 if (isset($colorXml->$type->alpha)) {
-                    /** @var string */
-                    $alpha = self::getAttribute($colorXml->$type->alpha, 'val', 'string');
+                    $alpha = self::getAttributeString($colorXml->$type->alpha, 'val');
                     if (is_numeric($alpha)) {
                         $result['alpha'] = ChartColor::alphaFromXml($alpha);
                     }
                 }
                 if (isset($colorXml->$type->lumMod)) {
-                    /** @var string */
-                    $brightness = self::getAttribute($colorXml->$type->lumMod, 'val', 'string');
+                    $brightness = self::getAttributeString($colorXml->$type->lumMod, 'val');
                     if (is_numeric($brightness)) {
                         $result['brightness'] = ChartColor::alphaFromXml($brightness);
                     }
@@ -1294,17 +1428,16 @@ class Chart
             return;
         }
         $lineWidth = null;
-        /** @var string */
-        $lineWidthTemp = self::getAttribute($sppr->ln, 'w', 'string');
+        $lineWidthTemp = self::getAttributeString($sppr->ln, 'w');
         if (is_numeric($lineWidthTemp)) {
             $lineWidth = ChartProperties::xmlToPoints($lineWidthTemp);
         }
-        /** @var string */
-        $compoundType = self::getAttribute($sppr->ln, 'cmpd', 'string');
-        /** @var string */
-        $dashType = self::getAttribute($sppr->ln->prstDash, 'val', 'string');
-        /** @var string */
-        $capType = self::getAttribute($sppr->ln, 'cap', 'string');
+        /** @var string $compoundType */
+        $compoundType = self::getAttributeString($sppr->ln, 'cmpd');
+        /** @var string $dashType */
+        $dashType = self::getAttributeString($sppr->ln->prstDash, 'val');
+        /** @var string $capType */
+        $capType = self::getAttributeString($sppr->ln, 'cap');
         if (isset($sppr->ln->miter)) {
             $joinType = ChartProperties::LINE_STYLE_JOIN_MITER;
         } elseif (isset($sppr->ln->bevel)) {
@@ -1312,20 +1445,14 @@ class Chart
         } else {
             $joinType = '';
         }
-        $headArrowSize = '';
-        $endArrowSize = '';
-        /** @var string */
-        $headArrowType = self::getAttribute($sppr->ln->headEnd, 'type', 'string');
-        /** @var string */
-        $headArrowWidth = self::getAttribute($sppr->ln->headEnd, 'w', 'string');
-        /** @var string */
-        $headArrowLength = self::getAttribute($sppr->ln->headEnd, 'len', 'string');
-        /** @var string */
-        $endArrowType = self::getAttribute($sppr->ln->tailEnd, 'type', 'string');
-        /** @var string */
-        $endArrowWidth = self::getAttribute($sppr->ln->tailEnd, 'w', 'string');
-        /** @var string */
-        $endArrowLength = self::getAttribute($sppr->ln->tailEnd, 'len', 'string');
+        $headArrowSize = 0;
+        $endArrowSize = 0;
+        $headArrowType = self::getAttributeString($sppr->ln->headEnd, 'type');
+        $headArrowWidth = self::getAttributeString($sppr->ln->headEnd, 'w');
+        $headArrowLength = self::getAttributeString($sppr->ln->headEnd, 'len');
+        $endArrowType = self::getAttributeString($sppr->ln->tailEnd, 'type');
+        $endArrowWidth = self::getAttributeString($sppr->ln->tailEnd, 'w');
+        $endArrowLength = self::getAttributeString($sppr->ln->tailEnd, 'len');
         $chartObject->setLineStyleProperties(
             $lineWidth,
             $compoundType,
@@ -1351,69 +1478,95 @@ class Chart
             return;
         }
         if (isset($chartDetail->delete)) {
-            $whichAxis->setAxisOption('hidden', (string) self::getAttribute($chartDetail->delete, 'val', 'string'));
+            $whichAxis->setAxisOption('hidden', (string) self::getAttributeString($chartDetail->delete, 'val'));
         }
         if (isset($chartDetail->numFmt)) {
             $whichAxis->setAxisNumberProperties(
-                (string) self::getAttribute($chartDetail->numFmt, 'formatCode', 'string'),
+                (string) self::getAttributeString($chartDetail->numFmt, 'formatCode'),
                 null,
-                (int) self::getAttribute($chartDetail->numFmt, 'sourceLinked', 'int')
+                (int) self::getAttributeInteger($chartDetail->numFmt, 'sourceLinked')
             );
         }
         if (isset($chartDetail->crossBetween)) {
-            $whichAxis->setCrossBetween((string) self::getAttribute($chartDetail->crossBetween, 'val', 'string'));
+            $whichAxis->setCrossBetween((string) self::getAttributeString($chartDetail->crossBetween, 'val'));
+        }
+        if (isset($chartDetail->dispUnits, $chartDetail->dispUnits->builtInUnit)) {
+            $whichAxis->setAxisOption('dispUnitsBuiltIn', (string) self::getAttributeString($chartDetail->dispUnits->builtInUnit, 'val'));
+            if (isset($chartDetail->dispUnits->dispUnitsLbl)) {
+                $whichAxis->setDispUnitsTitle(new Title());
+                // TODO parse title elements
+            }
         }
         if (isset($chartDetail->majorTickMark)) {
-            $whichAxis->setAxisOption('major_tick_mark', (string) self::getAttribute($chartDetail->majorTickMark, 'val', 'string'));
+            $whichAxis->setAxisOption('major_tick_mark', (string) self::getAttributeString($chartDetail->majorTickMark, 'val'));
         }
         if (isset($chartDetail->minorTickMark)) {
-            $whichAxis->setAxisOption('minor_tick_mark', (string) self::getAttribute($chartDetail->minorTickMark, 'val', 'string'));
+            $whichAxis->setAxisOption('minor_tick_mark', (string) self::getAttributeString($chartDetail->minorTickMark, 'val'));
         }
         if (isset($chartDetail->tickLblPos)) {
-            $whichAxis->setAxisOption('axis_labels', (string) self::getAttribute($chartDetail->tickLblPos, 'val', 'string'));
+            $whichAxis->setAxisOption('axis_labels', (string) self::getAttributeString($chartDetail->tickLblPos, 'val'));
         }
         if (isset($chartDetail->crosses)) {
-            $whichAxis->setAxisOption('horizontal_crosses', (string) self::getAttribute($chartDetail->crosses, 'val', 'string'));
+            $whichAxis->setAxisOption('horizontal_crosses', (string) self::getAttributeString($chartDetail->crosses, 'val'));
         }
         if (isset($chartDetail->crossesAt)) {
-            $whichAxis->setAxisOption('horizontal_crosses_value', (string) self::getAttribute($chartDetail->crossesAt, 'val', 'string'));
+            $whichAxis->setAxisOption('horizontal_crosses_value', (string) self::getAttributeString($chartDetail->crossesAt, 'val'));
+        }
+        if (isset($chartDetail->scaling->logBase)) {
+            $whichAxis->setAxisOption('logBase', (string) self::getAttributeString($chartDetail->scaling->logBase, 'val'));
         }
         if (isset($chartDetail->scaling->orientation)) {
-            $whichAxis->setAxisOption('orientation', (string) self::getAttribute($chartDetail->scaling->orientation, 'val', 'string'));
+            $whichAxis->setAxisOption('orientation', (string) self::getAttributeString($chartDetail->scaling->orientation, 'val'));
         }
         if (isset($chartDetail->scaling->max)) {
-            $whichAxis->setAxisOption('maximum', (string) self::getAttribute($chartDetail->scaling->max, 'val', 'string'));
+            $whichAxis->setAxisOption('maximum', (string) self::getAttributeString($chartDetail->scaling->max, 'val'));
         }
         if (isset($chartDetail->scaling->min)) {
-            $whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
+            $whichAxis->setAxisOption('minimum', (string) self::getAttributeString($chartDetail->scaling->min, 'val'));
         }
         if (isset($chartDetail->scaling->min)) {
-            $whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
+            $whichAxis->setAxisOption('minimum', (string) self::getAttributeString($chartDetail->scaling->min, 'val'));
         }
         if (isset($chartDetail->majorUnit)) {
-            $whichAxis->setAxisOption('major_unit', (string) self::getAttribute($chartDetail->majorUnit, 'val', 'string'));
+            $whichAxis->setAxisOption('major_unit', (string) self::getAttributeString($chartDetail->majorUnit, 'val'));
         }
         if (isset($chartDetail->minorUnit)) {
-            $whichAxis->setAxisOption('minor_unit', (string) self::getAttribute($chartDetail->minorUnit, 'val', 'string'));
+            $whichAxis->setAxisOption('minor_unit', (string) self::getAttributeString($chartDetail->minorUnit, 'val'));
         }
         if (isset($chartDetail->baseTimeUnit)) {
-            $whichAxis->setAxisOption('baseTimeUnit', (string) self::getAttribute($chartDetail->baseTimeUnit, 'val', 'string'));
+            $whichAxis->setAxisOption('baseTimeUnit', (string) self::getAttributeString($chartDetail->baseTimeUnit, 'val'));
         }
         if (isset($chartDetail->majorTimeUnit)) {
-            $whichAxis->setAxisOption('majorTimeUnit', (string) self::getAttribute($chartDetail->majorTimeUnit, 'val', 'string'));
+            $whichAxis->setAxisOption('majorTimeUnit', (string) self::getAttributeString($chartDetail->majorTimeUnit, 'val'));
         }
         if (isset($chartDetail->minorTimeUnit)) {
-            $whichAxis->setAxisOption('minorTimeUnit', (string) self::getAttribute($chartDetail->minorTimeUnit, 'val', 'string'));
+            $whichAxis->setAxisOption('minorTimeUnit', (string) self::getAttributeString($chartDetail->minorTimeUnit, 'val'));
         }
         if (isset($chartDetail->txPr)) {
             $children = $chartDetail->txPr->children($this->aNamespace);
+            $addAxisText = false;
+            $axisText = new AxisText();
             if (isset($children->bodyPr)) {
-                /** @var string */
-                $textRotation = self::getAttribute($children->bodyPr, 'rot', 'string');
+                $textRotation = self::getAttributeString($children->bodyPr, 'rot');
                 if (is_numeric($textRotation)) {
-                    $whichAxis->setAxisOption('textRotation', (string) ChartProperties::xmlToAngle($textRotation));
+                    $axisText->setRotation((int) ChartProperties::xmlToAngle($textRotation));
+                    $addAxisText = true;
                 }
             }
+            if (isset($children->p->pPr->defRPr)) {
+                $font = $this->parseFont($children->p);
+                if ($font !== null) {
+                    $axisText->setFont($font);
+                    $addAxisText = true;
+                }
+            }
+            if (isset($children->p->pPr->defRPr->effectLst)) {
+                $this->readEffects($children->p->pPr->defRPr, $axisText, false);
+                $addAxisText = true;
+            }
+            if ($addAxisText) {
+                $whichAxis->setAxisText($axisText);
+            }
         }
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
index 2b14eab..cf9046c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
@@ -10,11 +10,9 @@ use SimpleXMLElement;
 
 class ColumnAndRowAttributes extends BaseParserClass
 {
-    /** @var Worksheet */
-    private $worksheet;
+    private Worksheet $worksheet;
 
-    /** @var ?SimpleXMLElement */
-    private $worksheetXml;
+    private ?SimpleXMLElement $worksheetXml;
 
     public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
     {
@@ -29,7 +27,7 @@ class ColumnAndRowAttributes extends BaseParserClass
      * @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
      *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
      */
-    private function setColumnAttributes($columnAddress, array $columnAttributes): void
+    private function setColumnAttributes(string $columnAddress, array $columnAttributes): void
     {
         if (isset($columnAttributes['xfIndex'])) {
             $this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']);
@@ -55,7 +53,7 @@ class ColumnAndRowAttributes extends BaseParserClass
      * @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
      *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
      */
-    private function setRowAttributes($rowNumber, array $rowAttributes): void
+    private function setRowAttributes(int $rowNumber, array $rowAttributes): void
     {
         if (isset($rowAttributes['xfIndex'])) {
             $this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
@@ -74,7 +72,7 @@ class ColumnAndRowAttributes extends BaseParserClass
         }
     }
 
-    public function load(?IReadFilter $readFilter = null, bool $readDataOnly = false): void
+    public function load(?IReadFilter $readFilter = null, bool $readDataOnly = false, bool $ignoreRowsWithNoCells = false): void
     {
         if ($this->worksheetXml === null) {
             return;
@@ -87,10 +85,10 @@ class ColumnAndRowAttributes extends BaseParserClass
         }
 
         if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) {
-            $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly);
+            $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly, $ignoreRowsWithNoCells);
         }
 
-        if ($readFilter !== null && get_class($readFilter) === DefaultReadFilter::class) {
+        if ($readFilter !== null && $readFilter::class === DefaultReadFilter::class) {
             $readFilter = null;
         }
 
@@ -98,8 +96,8 @@ class ColumnAndRowAttributes extends BaseParserClass
         $columnsAttributesAreSet = [];
         foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
             if (
-                $readFilter === null ||
-                !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)
+                $readFilter === null
+                || !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)
             ) {
                 if (!isset($columnsAttributesAreSet[$columnCoordinate])) {
                     $this->setColumnAttributes($columnCoordinate, $columnAttributes);
@@ -111,8 +109,8 @@ class ColumnAndRowAttributes extends BaseParserClass
         $rowsAttributesAreSet = [];
         foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
             if (
-                $readFilter === null ||
-                !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)
+                $readFilter === null
+                || !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)
             ) {
                 if (!isset($rowsAttributesAreSet[$rowCoordinate])) {
                     $this->setRowAttributes($rowCoordinate, $rowAttributes);
@@ -138,7 +136,6 @@ class ColumnAndRowAttributes extends BaseParserClass
         $columnAttributes = [];
 
         foreach ($worksheetCols->col as $columnx) {
-            /** @scrutinizer ignore-call */
             $column = $columnx->attributes();
             if ($column !== null) {
                 $startColumn = Coordinate::stringFromColumnIndex((int) $column['min']);
@@ -192,14 +189,13 @@ class ColumnAndRowAttributes extends BaseParserClass
         return false;
     }
 
-    private function readRowAttributes(SimpleXMLElement $worksheetRow, bool $readDataOnly): array
+    private function readRowAttributes(SimpleXMLElement $worksheetRow, bool $readDataOnly, bool $ignoreRowsWithNoCells): array
     {
         $rowAttributes = [];
 
         foreach ($worksheetRow as $rowx) {
-            /** @scrutinizer ignore-call */
             $row = $rowx->attributes();
-            if ($row !== null) {
+            if ($row !== null && (!$ignoreRowsWithNoCells || isset($rowx->c))) {
                 if (isset($row['ht']) && !$readDataOnly) {
                     $rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
                 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
index aa6b62b..cb562ef 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
@@ -3,7 +3,9 @@
 namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
 
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles as StyleReader;
+use PhpOffice\PhpSpreadsheet\Style\Color;
 use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalColorScale;
 use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;
 use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormattingRuleExtension;
 use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormatValueObject;
@@ -14,25 +16,22 @@ use stdClass;
 
 class ConditionalStyles
 {
-    /** @var Worksheet */
-    private $worksheet;
+    private Worksheet $worksheet;
 
-    /** @var SimpleXMLElement */
-    private $worksheetXml;
+    private SimpleXMLElement $worksheetXml;
 
-    /**
-     * @var array
-     */
-    private $ns;
+    private array $ns;
 
-    /** @var array */
-    private $dxfs;
+    private array $dxfs;
 
-    public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml, array $dxfs = [])
+    private StyleReader $styleReader;
+
+    public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml, array $dxfs, StyleReader $styleReader)
     {
         $this->worksheet = $workSheet;
         $this->worksheetXml = $worksheetXml;
         $this->dxfs = $dxfs;
+        $this->styleReader = $styleReader;
     }
 
     public function load(): void
@@ -48,13 +47,13 @@ class ConditionalStyles
         $this->worksheet->setSelectedCells($selectedCells);
     }
 
-    public function loadFromExt(StyleReader $styleReader): void
+    public function loadFromExt(): void
     {
         $selectedCells = $this->worksheet->getSelectedCells();
 
         $this->ns = $this->worksheetXml->getNamespaces(true);
         $this->setConditionalsFromExt(
-            $this->readConditionalsFromExt($this->worksheetXml->extLst, $styleReader)
+            $this->readConditionalsFromExt($this->worksheetXml->extLst)
         );
 
         $this->worksheet->setSelectedCells($selectedCells);
@@ -71,12 +70,20 @@ class ConditionalStyles
         }
     }
 
-    private function readConditionalsFromExt(SimpleXMLElement $extLst, StyleReader $styleReader): array
+    private function readConditionalsFromExt(SimpleXMLElement $extLst): array
     {
         $conditionals = [];
+        if (!isset($extLst->ext)) {
+            return $conditionals;
+        }
 
-        if (isset($extLst->ext['uri']) && (string) $extLst->ext['uri'] === '{78C0D931-6437-407d-A8EE-F0AAD7539E65}') {
-            $conditionalFormattingRuleXml = $extLst->ext->children($this->ns['x14']);
+        foreach ($extLst->ext as $extlstcond) {
+            $extAttrs = $extlstcond->attributes() ?? [];
+            $extUri = (string) ($extAttrs['uri'] ?? '');
+            if ($extUri !== '{78C0D931-6437-407d-A8EE-F0AAD7539E65}') {
+                continue;
+            }
+            $conditionalFormattingRuleXml = $extlstcond->children($this->ns['x14']);
             if (!$conditionalFormattingRuleXml->conditionalFormattings) {
                 return [];
             }
@@ -96,8 +103,8 @@ class ConditionalStyles
                 }
                 $conditionType = (string) $attributes->type;
                 if (
-                    !Conditional::isValidConditionType($conditionType) ||
-                    $conditionType === Conditional::CONDITION_DATABAR
+                    !Conditional::isValidConditionType($conditionType)
+                    || $conditionType === Conditional::CONDITION_DATABAR
                 ) {
                     continue;
                 }
@@ -105,7 +112,7 @@ class ConditionalStyles
                 $priority = (int) $attributes->priority;
 
                 $conditional = $this->readConditionalRuleFromExt($extCfRuleXml, $attributes);
-                $cfStyle = $this->readStyleFromExt($extCfRuleXml, $styleReader);
+                $cfStyle = $this->readStyleFromExt($extCfRuleXml);
                 $conditional->setStyle($cfStyle);
                 $conditionals[$sqref][$priority] = $conditional;
             }
@@ -128,11 +135,11 @@ class ConditionalStyles
         $conditional->setConditionType($conditionType);
         $conditional->setOperatorType($operatorType);
         if (
-            $conditionType === Conditional::CONDITION_CONTAINSTEXT ||
-            $conditionType === Conditional::CONDITION_NOTCONTAINSTEXT ||
-            $conditionType === Conditional::CONDITION_BEGINSWITH ||
-            $conditionType === Conditional::CONDITION_ENDSWITH ||
-            $conditionType === Conditional::CONDITION_TIMEPERIOD
+            $conditionType === Conditional::CONDITION_CONTAINSTEXT
+            || $conditionType === Conditional::CONDITION_NOTCONTAINSTEXT
+            || $conditionType === Conditional::CONDITION_BEGINSWITH
+            || $conditionType === Conditional::CONDITION_ENDSWITH
+            || $conditionType === Conditional::CONDITION_TIMEPERIOD
         ) {
             $conditional->setText(array_pop($operands) ?? '');
         }
@@ -141,17 +148,17 @@ class ConditionalStyles
         return $conditional;
     }
 
-    private function readStyleFromExt(SimpleXMLElement $extCfRuleXml, StyleReader $styleReader): Style
+    private function readStyleFromExt(SimpleXMLElement $extCfRuleXml): Style
     {
         $cfStyle = new Style(false, true);
         if ($extCfRuleXml->dxf) {
             $styleXML = $extCfRuleXml->dxf->children();
 
             if ($styleXML->borders) {
-                $styleReader->readBorderStyle($cfStyle->getBorders(), $styleXML->borders);
+                $this->styleReader->readBorderStyle($cfStyle->getBorders(), $styleXML->borders);
             }
             if ($styleXML->fill) {
-                $styleReader->readFillStyle($cfStyle->getFill(), $styleXML->fill);
+                $this->styleReader->readFillStyle($cfStyle->getFill(), $styleXML->fill);
             }
         }
 
@@ -163,7 +170,7 @@ class ConditionalStyles
         $conditionals = [];
         foreach ($xmlSheet->conditionalFormatting as $conditional) {
             foreach ($conditional->cfRule as $cfRule) {
-                if (Conditional::isValidConditionType((string) $cfRule['type']) && isset($this->dxfs[(int) ($cfRule['dxfId'])])) {
+                if (Conditional::isValidConditionType((string) $cfRule['type']) && (!isset($cfRule['dxfId']) || isset($this->dxfs[(int) ($cfRule['dxfId'])]))) {
                     $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
                 } elseif ((string) $cfRule['type'] == Conditional::CONDITION_DATABAR) {
                     $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
@@ -181,10 +188,10 @@ class ConditionalStyles
             $conditionalStyles = $this->readStyleRules($cfRules, $xmlExtLst);
 
             // Extract all cell references in $cellRangeReference
-            $cellBlocks = explode(' ', str_replace('$', '', strtoupper($cellRangeReference)));
-            foreach ($cellBlocks as $cellBlock) {
-                $worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
-            }
+            // N.B. In Excel UI, intersection is space and union is comma.
+            // But in Xml, intersection is comma and union is space.
+            $cellRangeReference = str_replace(['$', ' ', ',', '^'], ['', '^', ' ', ','], strtoupper($cellRangeReference));
+            $worksheet->getStyle($cellRangeReference)->setConditionalStyles($conditionalStyles);
         }
     }
 
@@ -193,10 +200,12 @@ class ConditionalStyles
         $conditionalFormattingRuleExtensions = ConditionalFormattingRuleExtension::parseExtLstXml($extLst);
         $conditionalStyles = [];
 
+        /** @var SimpleXMLElement $cfRule */
         foreach ($cfRules as $cfRule) {
             $objConditional = new Conditional();
             $objConditional->setConditionType((string) $cfRule['type']);
             $objConditional->setOperatorType((string) $cfRule['operator']);
+            $objConditional->setNoFormatSet(!isset($cfRule['dxfId']));
 
             if ((string) $cfRule['text'] != '') {
                 $objConditional->setText((string) $cfRule['text']);
@@ -225,9 +234,13 @@ class ConditionalStyles
 
             if (isset($cfRule->dataBar)) {
                 $objConditional->setDataBar(
-                    $this->readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions) // @phpstan-ignore-line
+                    $this->readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions)
                 );
-            } else {
+            } elseif (isset($cfRule->colorScale)) {
+                $objConditional->setColorScale(
+                    $this->readColorScale($cfRule)
+                );
+            } elseif (isset($cfRule['dxfId'])) {
                 $objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
             }
 
@@ -237,10 +250,7 @@ class ConditionalStyles
         return $conditionalStyles;
     }
 
-    /**
-     * @param SimpleXMLElement|stdClass $cfRule
-     */
-    private function readDataBarOfConditionalRule($cfRule, array $conditionalFormattingRuleExtensions): ConditionalDataBar
+    private function readDataBarOfConditionalRule(SimpleXMLElement $cfRule, array $conditionalFormattingRuleExtensions): ConditionalDataBar
     {
         $dataBar = new ConditionalDataBar();
         //dataBar attribute
@@ -252,7 +262,7 @@ class ConditionalStyles
         //conditionalFormatValueObjects
         $cfvoXml = $cfRule->dataBar->cfvo;
         $cfvoIndex = 0;
-        foreach ((count($cfvoXml) > 1 ? $cfvoXml : [$cfvoXml]) as $cfvo) {
+        foreach ((count($cfvoXml) > 1 ? $cfvoXml : [$cfvoXml]) as $cfvo) { //* @phpstan-ignore-line
             if ($cfvoIndex === 0) {
                 $dataBar->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $cfvo['type'], (string) $cfvo['val']));
             }
@@ -264,7 +274,7 @@ class ConditionalStyles
 
         //color
         if (isset($cfRule->dataBar->color)) {
-            $dataBar->setColor((string) $cfRule->dataBar->color['rgb']);
+            $dataBar->setColor($this->styleReader->readColor($cfRule->dataBar->color));
         }
         //extLst
         $this->readDataBarExtLstOfConditionalRule($dataBar, $cfRule, $conditionalFormattingRuleExtensions);
@@ -272,14 +282,50 @@ class ConditionalStyles
         return $dataBar;
     }
 
-    /**
-     * @param SimpleXMLElement|stdClass $cfRule
-     */
-    private function readDataBarExtLstOfConditionalRule(ConditionalDataBar $dataBar, $cfRule, array $conditionalFormattingRuleExtensions): void
+    private function readColorScale(SimpleXMLElement|stdClass $cfRule): ConditionalColorScale
+    {
+        $colorScale = new ConditionalColorScale();
+        $count = count($cfRule->colorScale->cfvo);
+        $idx = 0;
+        foreach ($cfRule->colorScale->cfvo as $cfvoXml) {
+            $attr = $cfvoXml->attributes() ?? [];
+            $type = (string) ($attr['type'] ?? '');
+            $val = $attr['val'] ?? null;
+            if ($idx === 0) {
+                $method = 'setMinimumConditionalFormatValueObject';
+            } elseif ($idx === 1 && $count === 3) {
+                $method = 'setMidpointConditionalFormatValueObject';
+            } else {
+                $method = 'setMaximumConditionalFormatValueObject';
+            }
+            if ($type !== 'formula') {
+                $colorScale->$method(new ConditionalFormatValueObject($type, $val));
+            } else {
+                $colorScale->$method(new ConditionalFormatValueObject($type, null, $val));
+            }
+            ++$idx;
+        }
+        $idx = 0;
+        foreach ($cfRule->colorScale->color as $color) {
+            $rgb = $this->styleReader->readColor($color);
+            if ($idx === 0) {
+                $colorScale->setMinimumColor(new Color($rgb));
+            } elseif ($idx === 1 && $count === 3) {
+                $colorScale->setMidpointColor(new Color($rgb));
+            } else {
+                $colorScale->setMaximumColor(new Color($rgb));
+            }
+            ++$idx;
+        }
+
+        return $colorScale;
+    }
+
+    private function readDataBarExtLstOfConditionalRule(ConditionalDataBar $dataBar, SimpleXMLElement $cfRule, array $conditionalFormattingRuleExtensions): void
     {
         if (isset($cfRule->extLst)) {
             $ns = $cfRule->extLst->getNamespaces(true);
-            foreach ((count($cfRule->extLst) > 0 ? $cfRule->extLst->ext : [$cfRule->extLst->ext]) as $ext) {
+            foreach ((count($cfRule->extLst) > 0 ? $cfRule->extLst->ext : [$cfRule->extLst->ext]) as $ext) { //* @phpstan-ignore-line
                 $extId = (string) $ext->children($ns['x14'])->id;
                 if (isset($conditionalFormattingRuleExtensions[$extId]) && (string) $ext['uri'] === '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}') {
                     $dataBar->setConditionalFormattingRuleExt($conditionalFormattingRuleExtensions[$extId]);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
index dac7623..d494dc9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
@@ -8,11 +8,9 @@ use SimpleXMLElement;
 
 class DataValidations
 {
-    /** @var Worksheet */
-    private $worksheet;
+    private Worksheet $worksheet;
 
-    /** @var SimpleXMLElement */
-    private $worksheetXml;
+    private SimpleXMLElement $worksheetXml;
 
     public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
     {
@@ -22,6 +20,18 @@ class DataValidations
 
     public function load(): void
     {
+        foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
+            // Uppercase coordinate
+            $range = strtoupper((string) $dataValidation['sqref']);
+            $rangeSet = explode(' ', $range);
+            foreach ($rangeSet as $range) {
+                if (preg_match('/^[A-Z]{1,3}\\d{1,7}/', $range, $matches) === 1) {
+                    // Ensure left/top row of range exists, thereby
+                    // adjusting high row/column.
+                    $this->worksheet->getCell($matches[0]);
+                }
+            }
+        }
         foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
             // Uppercase coordinate
             $range = strtoupper((string) $dataValidation['sqref']);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
index 7d48c79..0c5b9a1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
@@ -9,11 +9,9 @@ use SimpleXMLElement;
 
 class Hyperlinks
 {
-    /** @var Worksheet */
-    private $worksheet;
+    private Worksheet $worksheet;
 
-    /** @var array */
-    private $hyperlinks = [];
+    private array $hyperlinks = [];
 
     public function __construct(Worksheet $workSheet)
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
index fb55cb2..5e6cc88 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
@@ -8,11 +8,9 @@ use SimpleXMLElement;
 
 class PageSetup extends BaseParserClass
 {
-    /** @var Worksheet */
-    private $worksheet;
+    private Worksheet $worksheet;
 
-    /** @var ?SimpleXMLElement */
-    private $worksheetXml;
+    private ?SimpleXMLElement $worksheetXml;
 
     public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
     {
@@ -69,8 +67,8 @@ class PageSetup extends BaseParserClass
                 $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
             }
             if (
-                isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
-                self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])
+                isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber'])
+                && self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])
             ) {
                 $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
             }
@@ -81,7 +79,7 @@ class PageSetup extends BaseParserClass
             $relAttributes = $xmlSheet->pageSetup->attributes(Namespaces::SCHEMA_OFFICE_DOCUMENT);
             if (isset($relAttributes['id'])) {
                 $relid = (string) $relAttributes['id'];
-                if (substr($relid, -2) !== 'ps') {
+                if (!str_ends_with($relid, 'ps')) {
                     $relid .= 'ps';
                 }
                 $unparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'] = $relid;
@@ -97,32 +95,32 @@ class PageSetup extends BaseParserClass
             $docHeaderFooter = $worksheet->getHeaderFooter();
 
             if (
-                isset($xmlSheet->headerFooter['differentOddEven']) &&
-                self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])
+                isset($xmlSheet->headerFooter['differentOddEven'])
+                && self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])
             ) {
                 $docHeaderFooter->setDifferentOddEven(true);
             } else {
                 $docHeaderFooter->setDifferentOddEven(false);
             }
             if (
-                isset($xmlSheet->headerFooter['differentFirst']) &&
-                self::boolean((string) $xmlSheet->headerFooter['differentFirst'])
+                isset($xmlSheet->headerFooter['differentFirst'])
+                && self::boolean((string) $xmlSheet->headerFooter['differentFirst'])
             ) {
                 $docHeaderFooter->setDifferentFirst(true);
             } else {
                 $docHeaderFooter->setDifferentFirst(false);
             }
             if (
-                isset($xmlSheet->headerFooter['scaleWithDoc']) &&
-                !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])
+                isset($xmlSheet->headerFooter['scaleWithDoc'])
+                && !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])
             ) {
                 $docHeaderFooter->setScaleWithDocument(false);
             } else {
                 $docHeaderFooter->setScaleWithDocument(true);
             }
             if (
-                isset($xmlSheet->headerFooter['alignWithMargins']) &&
-                !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])
+                isset($xmlSheet->headerFooter['alignWithMargins'])
+                && !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])
             ) {
                 $docHeaderFooter->setAlignWithMargins(false);
             } else {
@@ -151,8 +149,9 @@ class PageSetup extends BaseParserClass
     private function rowBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
     {
         foreach ($xmlSheet->rowBreaks->brk as $brk) {
+            $rowBreakMax = isset($brk['max']) ? ((int) $brk['max']) : -1;
             if ($brk['man']) {
-                $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW);
+                $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW, $rowBreakMax);
             }
         }
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
index 72addff..fb501e8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
@@ -9,11 +9,9 @@ use SimpleXMLElement;
 
 class Properties
 {
-    /** @var XmlScanner */
-    private $securityScanner;
+    private XmlScanner $securityScanner;
 
-    /** @var DocumentProperties */
-    private $docProps;
+    private DocumentProperties $docProps;
 
     public function __construct(XmlScanner $securityScanner, DocumentProperties $docProps)
     {
@@ -21,14 +19,6 @@ class Properties
         $this->docProps = $docProps;
     }
 
-    /**
-     * @param mixed $obj
-     */
-    private static function nullOrSimple($obj): ?SimpleXMLElement
-    {
-        return ($obj instanceof SimpleXMLElement) ? $obj : null;
-    }
-
     private function extractPropertyData(string $propertyData): ?SimpleXMLElement
     {
         // okay to omit namespace because everything will be processed by xpath
@@ -38,7 +28,7 @@ class Properties
             Settings::getLibXmlLoaderOptions()
         );
 
-        return self::nullOrSimple($obj);
+        return $obj === false ? null : $obj;
     }
 
     public function readCoreProperties(string $propertyData): void
@@ -50,15 +40,15 @@ class Properties
             $xmlCore->registerXPathNamespace('dcterms', Namespaces::DC_TERMS);
             $xmlCore->registerXPathNamespace('cp', Namespaces::CORE_PROPERTIES2);
 
-            $this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
-            $this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
-            $this->docProps->setCreated((string) self::getArrayItem($xmlCore->xpath('dcterms:created'))); //! respect xsi:type
-            $this->docProps->setModified((string) self::getArrayItem($xmlCore->xpath('dcterms:modified'))); //! respect xsi:type
-            $this->docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
-            $this->docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
-            $this->docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
-            $this->docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
-            $this->docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
+            $this->docProps->setCreator($this->getArrayItem($xmlCore->xpath('dc:creator')));
+            $this->docProps->setLastModifiedBy($this->getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
+            $this->docProps->setCreated($this->getArrayItem($xmlCore->xpath('dcterms:created'))); //! respect xsi:type
+            $this->docProps->setModified($this->getArrayItem($xmlCore->xpath('dcterms:modified'))); //! respect xsi:type
+            $this->docProps->setTitle($this->getArrayItem($xmlCore->xpath('dc:title')));
+            $this->docProps->setDescription($this->getArrayItem($xmlCore->xpath('dc:description')));
+            $this->docProps->setSubject($this->getArrayItem($xmlCore->xpath('dc:subject')));
+            $this->docProps->setKeywords($this->getArrayItem($xmlCore->xpath('cp:keywords')));
+            $this->docProps->setCategory($this->getArrayItem($xmlCore->xpath('cp:category')));
         }
     }
 
@@ -73,6 +63,9 @@ class Properties
             if (isset($xmlCore->Manager)) {
                 $this->docProps->setManager((string) $xmlCore->Manager);
             }
+            if (isset($xmlCore->HyperlinkBase)) {
+                $this->docProps->setHyperlinkBase((string) $xmlCore->HyperlinkBase);
+            }
         }
     }
 
@@ -98,12 +91,8 @@ class Properties
         }
     }
 
-    /**
-     * @param null|array|false $array
-     * @param mixed $key
-     */
-    private static function getArrayItem($array, $key = 0): ?SimpleXMLElement
+    private function getArrayItem(null|array|false $array): string
     {
-        return is_array($array) ? ($array[$key] ?? null) : null;
+        return is_array($array) ? (string) ($array[0] ?? '') : '';
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php
new file mode 100644
index 0000000..fb7a393
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+class SharedFormula
+{
+    private string $master;
+
+    private string $formula;
+
+    public function __construct(string $master, string $formula)
+    {
+        $this->master = $master;
+        $this->formula = $formula;
+    }
+
+    public function master(): string
+    {
+        return $this->master;
+    }
+
+    public function formula(): string
+    {
+        return $this->formula;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
index 5a49644..9d71443 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
@@ -7,11 +7,9 @@ use SimpleXMLElement;
 
 class SheetViewOptions extends BaseParserClass
 {
-    /** @var Worksheet */
-    private $worksheet;
+    private Worksheet $worksheet;
 
-    /** @var ?SimpleXMLElement */
-    private $worksheetXml;
+    private ?SimpleXMLElement $worksheetXml;
 
     public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
     {
@@ -62,8 +60,8 @@ class SheetViewOptions extends BaseParserClass
         if (isset($sheetPr->outlinePr)) {
             $attr = $sheetPr->outlinePr->attributes() ?? [];
             if (
-                isset($attr['summaryRight']) &&
-                !self::boolean((string) $attr['summaryRight'])
+                isset($attr['summaryRight'])
+                && !self::boolean((string) $attr['summaryRight'])
             ) {
                 $this->worksheet->setShowSummaryRight(false);
             } else {
@@ -71,8 +69,8 @@ class SheetViewOptions extends BaseParserClass
             }
 
             if (
-                isset($attr['summaryBelow']) &&
-                !self::boolean((string) $attr['summaryBelow'])
+                isset($attr['summaryBelow'])
+                && !self::boolean((string) $attr['summaryBelow'])
             ) {
                 $this->worksheet->setShowSummaryBelow(false);
             } else {
@@ -86,8 +84,8 @@ class SheetViewOptions extends BaseParserClass
         if (isset($sheetPr->pageSetUpPr)) {
             $attr = $sheetPr->pageSetUpPr->attributes() ?? [];
             if (
-                isset($attr['fitToPage']) &&
-                !self::boolean((string) $attr['fitToPage'])
+                isset($attr['fitToPage'])
+                && !self::boolean((string) $attr['fitToPage'])
             ) {
                 $this->worksheet->getPageSetup()->setFitToPage(false);
             } else {
@@ -100,9 +98,9 @@ class SheetViewOptions extends BaseParserClass
     {
         $sheetFormatPr = $sheetFormatPrx->attributes() ?? [];
         if (
-            isset($sheetFormatPr['customHeight']) &&
-            self::boolean((string) $sheetFormatPr['customHeight']) &&
-            isset($sheetFormatPr['defaultRowHeight'])
+            isset($sheetFormatPr['customHeight'])
+            && self::boolean((string) $sheetFormatPr['customHeight'])
+            && isset($sheetFormatPr['defaultRowHeight'])
         ) {
             $this->worksheet->getDefaultRowDimension()
                 ->setRowHeight((float) $sheetFormatPr['defaultRowHeight']);
@@ -114,8 +112,8 @@ class SheetViewOptions extends BaseParserClass
         }
 
         if (
-            isset($sheetFormatPr['zeroHeight']) &&
-            ((string) $sheetFormatPr['zeroHeight'] === '1')
+            isset($sheetFormatPr['zeroHeight'])
+            && ((string) $sheetFormatPr['zeroHeight'] === '1')
         ) {
             $this->worksheet->getDefaultRowDimension()->setZeroHeight(true);
         }
@@ -124,11 +122,12 @@ class SheetViewOptions extends BaseParserClass
     private function printOptions(SimpleXMLElement $printOptionsx): void
     {
         $printOptions = $printOptionsx->attributes() ?? [];
-        if (isset($printOptions['gridLinesSet']) && self::boolean((string) $printOptions['gridLinesSet'])) {
-            $this->worksheet->setShowGridlines(true);
-        }
+        // Spec is weird. gridLines (default false)
+        // and gridLinesSet (default true) must both be true.
         if (isset($printOptions['gridLines']) && self::boolean((string) $printOptions['gridLines'])) {
-            $this->worksheet->setPrintGridlines(true);
+            if (!isset($printOptions['gridLinesSet']) || self::boolean((string) $printOptions['gridLinesSet'])) {
+                $this->worksheet->setPrintGridlines(true);
+            }
         }
         if (isset($printOptions['horizontalCentered']) && self::boolean((string) $printOptions['horizontalCentered'])) {
             $this->worksheet->getPageSetup()->setHorizontalCentered(true);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
index b2bc99f..7c4b8b2 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
@@ -4,19 +4,19 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
 
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+use PhpOffice\PhpSpreadsheet\Worksheet\Pane;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 use SimpleXMLElement;
 
 class SheetViews extends BaseParserClass
 {
-    /** @var SimpleXMLElement */
-    private $sheetViewXml;
+    private SimpleXMLElement $sheetViewXml;
 
-    /** @var SimpleXMLElement */
-    private $sheetViewAttributes;
+    private SimpleXMLElement $sheetViewAttributes;
 
-    /** @var Worksheet */
-    private $worksheet;
+    private Worksheet $worksheet;
+
+    private string $activePane = '';
 
     public function __construct(SimpleXMLElement $sheetViewXml, Worksheet $workSheet)
     {
@@ -35,11 +35,15 @@ class SheetViews extends BaseParserClass
         $this->direction();
         $this->showZeros();
 
+        $usesPanes = false;
         if (isset($this->sheetViewXml->pane)) {
             $this->pane();
+            $usesPanes = true;
         }
-        if (isset($this->sheetViewXml->selection, $this->sheetViewXml->selection->attributes()->sqref)) {
-            $this->selection();
+        if (isset($this->sheetViewXml->selection)) {
+            foreach ($this->sheetViewXml->selection as $selection) {
+                $this->selection($selection, $usesPanes);
+            }
         }
     }
 
@@ -66,6 +70,20 @@ class SheetViews extends BaseParserClass
 
             $this->worksheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
         }
+
+        if (isset($this->sheetViewAttributes->zoomScalePageLayoutView)) {
+            $zoomScaleNormal = (int) ($this->sheetViewAttributes->zoomScalePageLayoutView);
+            if ($zoomScaleNormal > 0) {
+                $this->worksheet->getSheetView()->setZoomScalePageLayoutView($zoomScaleNormal);
+            }
+        }
+
+        if (isset($this->sheetViewAttributes->zoomScaleSheetLayoutView)) {
+            $zoomScaleNormal = (int) ($this->sheetViewAttributes->zoomScaleSheetLayoutView);
+            if ($zoomScaleNormal > 0) {
+                $this->worksheet->getSheetView()->setZoomScaleSheetLayoutView($zoomScaleNormal);
+            }
+        }
     }
 
     private function view(): void
@@ -127,30 +145,55 @@ class SheetViews extends BaseParserClass
 
         if (isset($paneAttributes->xSplit)) {
             $xSplit = (int) ($paneAttributes->xSplit);
+            $this->worksheet->setXSplit($xSplit);
         }
 
         if (isset($paneAttributes->ySplit)) {
             $ySplit = (int) ($paneAttributes->ySplit);
+            $this->worksheet->setYSplit($ySplit);
         }
-
+        $paneState = isset($paneAttributes->state) ? ((string) $paneAttributes->state) : '';
+        $this->worksheet->setPaneState($paneState);
         if (isset($paneAttributes->topLeftCell)) {
             $topLeftCell = (string) $paneAttributes->topLeftCell;
+            $this->worksheet->setPaneTopLeftCell($topLeftCell);
+            if ($paneState === Worksheet::PANE_FROZEN) {
+                $this->worksheet->setTopLeftCell($topLeftCell);
+            }
+        }
+        $activePane = isset($paneAttributes->activePane) ? ((string) $paneAttributes->activePane) : 'topLeft';
+        $this->worksheet->setActivePane($activePane);
+        $this->activePane = $activePane;
+        if ($paneState === Worksheet::PANE_FROZEN || $paneState === Worksheet::PANE_FROZENSPLIT) {
+            $this->worksheet->freezePane(
+                Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
+                $topLeftCell,
+                $paneState === Worksheet::PANE_FROZENSPLIT
+            );
         }
-
-        $this->worksheet->freezePane(
-            Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
-            $topLeftCell
-        );
     }
 
-    private function selection(): void
+    private function selection(?SimpleXMLElement $selection, bool $usesPanes): void
     {
-        $attributes = $this->sheetViewXml->selection->attributes();
+        $attributes = ($selection === null) ? null : $selection->attributes();
         if ($attributes !== null) {
+            $position = (string) $attributes->pane;
+            if ($usesPanes && $position === '') {
+                $position = 'topLeft';
+            }
+            $activeCell = (string) $attributes->activeCell;
             $sqref = (string) $attributes->sqref;
             $sqref = explode(' ', $sqref);
             $sqref = $sqref[0];
-            $this->worksheet->setSelectedCells($sqref);
+            if ($position === '') {
+                $this->worksheet->setSelectedCells($sqref);
+            } else {
+                $pane = new Pane($position, $sqref, $activeCell);
+                $this->worksheet->setPane($position, $pane);
+                if ($position === $this->activePane && $sqref !== '') {
+                    $this->worksheet->setSelectedCells($sqref);
+                }
+            }
         }
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
index 5b089fa..0672854 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
@@ -19,25 +19,18 @@ class Styles extends BaseParserClass
 {
     /**
      * Theme instance.
-     *
-     * @var ?Theme
      */
-    private $theme;
+    private ?Theme $theme = null;
 
-    /** @var array */
-    private $workbookPalette = [];
+    private array $workbookPalette = [];
 
-    /** @var array */
-    private $styles = [];
+    private array $styles = [];
 
-    /** @var array */
-    private $cellStyles = [];
+    private array $cellStyles = [];
 
-    /** @var SimpleXMLElement */
-    private $styleXml;
+    private SimpleXMLElement $styleXml;
 
-    /** @var string */
-    private $namespace = '';
+    private string $namespace = '';
 
     public function setNamespace(string $namespace): void
     {
@@ -49,30 +42,17 @@ class Styles extends BaseParserClass
         $this->workbookPalette = $palette;
     }
 
-    /**
-     * Cast SimpleXMLElement to bool to overcome Scrutinizer problem.
-     *
-     * @param mixed $value
-     */
-    private static function castBool($value): bool
-    {
-        return (bool) $value;
-    }
-
     private function getStyleAttributes(SimpleXMLElement $value): SimpleXMLElement
     {
-        $attr = null;
-        if (self::castBool($value)) {
-            $attr = $value->attributes('');
-            if ($attr === null || count($attr) === 0) {
-                $attr = $value->attributes($this->namespace);
-            }
+        $attr = $value->attributes('');
+        if ($attr === null || count($attr) === 0) {
+            $attr = $value->attributes($this->namespace);
         }
 
         return Xlsx::testSimpleXml($attr);
     }
 
-    public function setStyleXml(SimpleXmlElement $styleXml): void
+    public function setStyleXml(SimpleXMLElement $styleXml): void
     {
         $this->styleXml = $styleXml;
     }
@@ -136,6 +116,10 @@ class Styles extends BaseParserClass
                 }
             }
         }
+        if (isset($fontStyleXml->scheme)) {
+            $attr = $this->getStyleAttributes($fontStyleXml->scheme);
+            $fontStyle->setScheme((string) $attr['val']);
+        }
     }
 
     private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
@@ -162,8 +146,8 @@ class Styles extends BaseParserClass
             }
             $fillStyle->setRotation((float) ($attr['degree']));
             $gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
-            $fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
-            $fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
+            $fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color)); //* @phpstan-ignore-line
+            $fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color)); //* @phpstan-ignore-line
         } elseif ($fillStyleXml->patternFill) {
             $defaultFillStyle = Fill::FILL_NONE;
             if ($fillStyleXml->patternFill->fgColor) {
@@ -206,11 +190,21 @@ class Styles extends BaseParserClass
             $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
         }
 
-        $this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
-        $this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
-        $this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
-        $this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
-        $this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
+        if (isset($borderStyleXml->left)) {
+            $this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
+        }
+        if (isset($borderStyleXml->right)) {
+            $this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
+        }
+        if (isset($borderStyleXml->top)) {
+            $this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
+        }
+        if (isset($borderStyleXml->bottom)) {
+            $this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
+        }
+        if (isset($borderStyleXml->diagonal)) {
+            $this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
+        }
     }
 
     private function getAttribute(SimpleXMLElement $xml, string $attribute): string
@@ -233,6 +227,8 @@ class Styles extends BaseParserClass
         $style = $this->getAttribute($borderXml, 'style');
         if ($style !== '') {
             $border->setBorderStyle((string) $style);
+        } else {
+            $border->setBorderStyle(Border::BORDER_NONE);
         }
         if (isset($borderXml->color)) {
             $border->getColor()->setARGB($this->readColor($borderXml->color));
@@ -241,10 +237,14 @@ class Styles extends BaseParserClass
 
     public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
     {
-        $horizontal = $this->getAttribute($alignmentXml, 'horizontal');
-        $alignment->setHorizontal($horizontal);
-        $vertical = $this->getAttribute($alignmentXml, 'vertical');
-        $alignment->setVertical((string) $vertical);
+        $horizontal = (string) $this->getAttribute($alignmentXml, 'horizontal');
+        if ($horizontal !== '') {
+            $alignment->setHorizontal($horizontal);
+        }
+        $vertical = (string) $this->getAttribute($alignmentXml, 'vertical');
+        if ($vertical !== '') {
+            $alignment->setVertical($vertical);
+        }
 
         $textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
         if ($textRotation > 90) {
@@ -273,10 +273,8 @@ class Styles extends BaseParserClass
 
     /**
      * Read style.
-     *
-     * @param SimpleXMLElement|stdClass $style
      */
-    public function readStyle(Style $docStyle, $style): void
+    public function readStyle(Style $docStyle, SimpleXMLElement|stdClass $style): void
     {
         if ($style instanceof SimpleXMLElement) {
             $this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
@@ -428,11 +426,9 @@ class Styles extends BaseParserClass
      * Get array item.
      *
      * @param mixed $array (usually array, in theory can be false)
-     *
-     * @return stdClass
      */
-    private static function getArrayItem($array, int $key = 0)
+    private static function getArrayItem(mixed $array): ?SimpleXMLElement
     {
-        return is_array($array) ? ($array[$key] ?? null) : null;
+        return is_array($array) ? ($array[0] ?? null) : null;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php
index cf89e99..a63c817 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/TableReader.php
@@ -9,15 +9,12 @@ use SimpleXMLElement;
 
 class TableReader
 {
-    /**
-     * @var Worksheet
-     */
-    private $worksheet;
+    private Worksheet $worksheet;
 
-    /**
-     * @var SimpleXMLElement
-     */
-    private $tableXml;
+    private SimpleXMLElement $tableXml;
+
+    /** @var array|SimpleXMLElement */
+    private $tableAttributes;
 
     public function __construct(Worksheet $workSheet, SimpleXMLElement $tableXml)
     {
@@ -30,28 +27,29 @@ class TableReader
      */
     public function load(): void
     {
+        $this->tableAttributes = $this->tableXml->attributes() ?? [];
         // Remove all "$" in the table range
-        $tableRange = (string) preg_replace('/\$/', '', $this->tableXml['ref'] ?? '');
-        if (strpos($tableRange, ':') !== false) {
-            $this->readTable($tableRange, $this->tableXml);
+        $tableRange = (string) preg_replace('/\$/', '', $this->tableAttributes['ref'] ?? '');
+        if (str_contains($tableRange, ':')) {
+            $this->readTable($tableRange);
         }
     }
 
     /**
      * Read Table from xml.
      */
-    private function readTable(string $tableRange, SimpleXMLElement $tableXml): void
+    private function readTable(string $tableRange): void
     {
         $table = new Table($tableRange);
-        $table->setName((string) $tableXml['displayName']);
-        $table->setShowHeaderRow((string) $tableXml['headerRowCount'] !== '0');
-        $table->setShowTotalsRow((string) $tableXml['totalsRowCount'] === '1');
+        $table->setName((string) ($this->tableAttributes['displayName'] ?? ''));
+        $table->setShowHeaderRow(((string) ($this->tableAttributes['headerRowCount'] ?? '')) !== '0');
+        $table->setShowTotalsRow(((string) ($this->tableAttributes['totalsRowCount'] ?? '')) === '1');
 
-        $this->readTableAutoFilter($table, $tableXml->autoFilter);
-        $this->readTableColumns($table, $tableXml->tableColumns);
-        $this->readTableStyle($table, $tableXml->tableStyleInfo);
+        $this->readTableAutoFilter($table, $this->tableXml->autoFilter);
+        $this->readTableColumns($table, $this->tableXml->tableColumns);
+        $this->readTableStyle($table, $this->tableXml->tableStyleInfo);
 
-        (new AutoFilter($table, $tableXml))->load();
+        (new AutoFilter($table, $this->tableXml))->load();
         $this->worksheet->addTable($table);
     }
 
@@ -67,8 +65,9 @@ class TableReader
         }
 
         foreach ($autoFilterXml->filterColumn as $filterColumn) {
-            $column = $table->getColumnByOffset((int) $filterColumn['colId']);
-            $column->setShowFilterButton((string) $filterColumn['hiddenButton'] !== '1');
+            $attributes = $filterColumn->attributes() ?? ['colId' => 0, 'hiddenButton' => 0];
+            $column = $table->getColumnByOffset((int) $attributes['colId']);
+            $column->setShowFilterButton(((string) $attributes['hiddenButton']) !== '1');
         }
     }
 
@@ -79,15 +78,16 @@ class TableReader
     {
         $offset = 0;
         foreach ($tableColumnsXml->tableColumn as $tableColumn) {
+            $attributes = $tableColumn->attributes() ?? ['totalsRowLabel' => 0, 'totalsRowFunction' => 0];
             $column = $table->getColumnByOffset($offset++);
 
             if ($table->getShowTotalsRow()) {
-                if ($tableColumn['totalsRowLabel']) {
-                    $column->setTotalsRowLabel((string) $tableColumn['totalsRowLabel']);
+                if ($attributes['totalsRowLabel']) {
+                    $column->setTotalsRowLabel((string) $attributes['totalsRowLabel']);
                 }
 
-                if ($tableColumn['totalsRowFunction']) {
-                    $column->setTotalsRowFunction((string) $tableColumn['totalsRowFunction']);
+                if ($attributes['totalsRowFunction']) {
+                    $column->setTotalsRowFunction((string) $attributes['totalsRowFunction']);
                 }
             }
 
@@ -103,11 +103,14 @@ class TableReader
     private function readTableStyle(Table $table, SimpleXMLElement $tableStyleInfoXml): void
     {
         $tableStyle = new TableStyle();
-        $tableStyle->setTheme((string) $tableStyleInfoXml['name']);
-        $tableStyle->setShowRowStripes((string) $tableStyleInfoXml['showRowStripes'] === '1');
-        $tableStyle->setShowColumnStripes((string) $tableStyleInfoXml['showColumnStripes'] === '1');
-        $tableStyle->setShowFirstColumn((string) $tableStyleInfoXml['showFirstColumn'] === '1');
-        $tableStyle->setShowLastColumn((string) $tableStyleInfoXml['showLastColumn'] === '1');
+        $attributes = $tableStyleInfoXml->attributes();
+        if ($attributes !== null) {
+            $tableStyle->setTheme((string) $attributes['name']);
+            $tableStyle->setShowRowStripes((string) $attributes['showRowStripes'] === '1');
+            $tableStyle->setShowColumnStripes((string) $attributes['showColumnStripes'] === '1');
+            $tableStyle->setShowFirstColumn((string) $attributes['showFirstColumn'] === '1');
+            $tableStyle->setShowLastColumn((string) $attributes['showLastColumn'] === '1');
+        }
         $table->setStyle($tableStyle);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
index 706c4d1..4a31c3e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
@@ -6,33 +6,27 @@ class Theme
 {
     /**
      * Theme Name.
-     *
-     * @var string
      */
-    private $themeName;
+    private string $themeName;
 
     /**
      * Colour Scheme Name.
-     *
-     * @var string
      */
-    private $colourSchemeName;
+    private string $colourSchemeName;
 
     /**
      * Colour Map.
      *
      * @var string[]
      */
-    private $colourMap;
+    private array $colourMap;
 
     /**
      * Create a new Theme.
      *
-     * @param string $themeName
-     * @param string $colourSchemeName
      * @param string[] $colourMap
      */
-    public function __construct($themeName, $colourSchemeName, $colourMap)
+    public function __construct(string $themeName, string $colourSchemeName, array $colourMap)
     {
         // Initialise values
         $this->themeName = $themeName;
@@ -43,11 +37,9 @@ class Theme
     /**
      * Not called by Reader, never accessible any other time.
      *
-     * @return string
-     *
      * @codeCoverageIgnore
      */
-    public function getThemeName()
+    public function getThemeName(): string
     {
         return $this->themeName;
     }
@@ -55,23 +47,17 @@ class Theme
     /**
      * Not called by Reader, never accessible any other time.
      *
-     * @return string
-     *
      * @codeCoverageIgnore
      */
-    public function getColourSchemeName()
+    public function getColourSchemeName(): string
     {
         return $this->colourSchemeName;
     }
 
     /**
      * Get colour Map Value by Position.
-     *
-     * @param int $index
-     *
-     * @return null|string
      */
-    public function getColourByIndex($index)
+    public function getColourByIndex(int $index): ?string
     {
         return $this->colourMap[$index] ?? null;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php
index 4743afb..70146ed 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php
@@ -7,24 +7,15 @@ use SimpleXMLElement;
 
 class WorkbookView
 {
-    /**
-     * @var Spreadsheet
-     */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     public function __construct(Spreadsheet $spreadsheet)
     {
         $this->spreadsheet = $spreadsheet;
     }
 
-    /**
-     * @param mixed $mainNS
-     */
-    public function viewSettings(SimpleXMLElement $xmlWorkbook, $mainNS, array $mapSheetId, bool $readDataOnly): void
+    public function viewSettings(SimpleXMLElement $xmlWorkbook, string $mainNS, array $mapSheetId, bool $readDataOnly): void
     {
-        if ($this->spreadsheet->getSheetCount() == 0) {
-            $this->spreadsheet->createSheet();
-        }
         // Default active sheet index to the first loaded worksheet from the file
         $this->spreadsheet->setActiveSheetIndex(0);
 
@@ -49,10 +40,7 @@ class WorkbookView
         }
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function testSimpleXml($value): SimpleXMLElement
+    public static function testSimpleXml(mixed $value): SimpleXMLElement
     {
         return ($value instanceof SimpleXMLElement)
             ? $value
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
index 565a5af..979e74c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
@@ -8,7 +8,9 @@ use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\DefinedName;
+use PhpOffice\PhpSpreadsheet\Helper\Html as HelperHtml;
 use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
 use PhpOffice\PhpSpreadsheet\Reader\Xml\PageSettings;
 use PhpOffice\PhpSpreadsheet\Reader\Xml\Properties;
 use PhpOffice\PhpSpreadsheet\Reader\Xml\Style;
@@ -18,20 +20,22 @@ use PhpOffice\PhpSpreadsheet\Shared\Date;
 use PhpOffice\PhpSpreadsheet\Shared\File;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 use SimpleXMLElement;
+use Throwable;
 
 /**
  * Reader for SpreadsheetML, the XML schema for Microsoft Office Excel 2003.
  */
 class Xml extends BaseReader
 {
+    public const NAMESPACES_SS = 'urn:schemas-microsoft-com:office:spreadsheet';
+
     /**
      * Formats.
-     *
-     * @var array
      */
-    protected $styles = [];
+    protected array $styles = [];
 
     /**
      * Create a new Excel2003XML Reader instance.
@@ -42,7 +46,9 @@ class Xml extends BaseReader
         $this->securityScanner = XmlScanner::getInstance($this);
     }
 
-    private $fileContents = '';
+    private string $fileContents = '';
+
+    private string $xmlFailMessage = '';
 
     public static function xmlMappings(): array
     {
@@ -73,7 +79,7 @@ class Xml extends BaseReader
         ];
 
         // Open file
-        $data = file_get_contents($filename);
+        $data = file_get_contents($filename) ?: '';
 
         // Why?
         //$data = str_replace("'", '"', $data); // fix headers with single quote
@@ -81,7 +87,7 @@ class Xml extends BaseReader
         $valid = true;
         foreach ($signature as $match) {
             // every part of the signature must be present
-            if (strpos($data, $match) === false) {
+            if (!str_contains($data, $match)) {
                 $valid = false;
 
                 break;
@@ -104,20 +110,45 @@ class Xml extends BaseReader
     /**
      * Check if the file is a valid SimpleXML.
      *
-     * @param string $filename
-     *
      * @return false|SimpleXMLElement
+     *
+     * @deprecated 2.0.1 Should never have had public visibility
+     *
+     * @codeCoverageIgnore
      */
-    public function trySimpleXMLLoadString($filename)
+    public function trySimpleXMLLoadString(string $filename, string $fileOrString = 'file'): SimpleXMLElement|bool
     {
+        return $this->trySimpleXMLLoadStringPrivate($filename, $fileOrString);
+    }
+
+    /** @return false|SimpleXMLElement */
+    private function trySimpleXMLLoadStringPrivate(string $filename, string $fileOrString = 'file'): SimpleXMLElement|bool
+    {
+        $this->xmlFailMessage = "Cannot load invalid XML $fileOrString: " . $filename;
+        $xml = false;
+
         try {
-            $xml = simplexml_load_string(
-                $this->securityScanner->scan($this->fileContents ?: file_get_contents($filename)),
-                'SimpleXMLElement',
-                Settings::getLibXmlLoaderOptions()
-            );
-        } catch (\Exception $e) {
-            throw new Exception('Cannot load invalid XML file: ' . $filename, 0, $e);
+            $data = $this->fileContents;
+            $continue = true;
+            if ($data === '' && $fileOrString === 'file') {
+                if ($filename === '') {
+                    $this->xmlFailMessage = 'Cannot load empty path';
+                    $continue = false;
+                } else {
+                    $datax = @file_get_contents($filename);
+                    $data = $datax ?: '';
+                    $continue = $datax !== false;
+                }
+            }
+            if ($continue) {
+                $xml = @simplexml_load_string(
+                    $this->getSecurityScannerOrThrow()->scan($data),
+                    'SimpleXMLElement',
+                    Settings::getLibXmlLoaderOptions()
+                );
+            }
+        } catch (Throwable $e) {
+            throw new Exception($this->xmlFailMessage, 0, $e);
         }
         $this->fileContents = '';
 
@@ -126,12 +157,8 @@ class Xml extends BaseReader
 
     /**
      * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetNames($filename)
+    public function listWorksheetNames(string $filename): array
     {
         File::assertFile($filename);
         if (!$this->canRead($filename)) {
@@ -140,16 +167,14 @@ class Xml extends BaseReader
 
         $worksheetNames = [];
 
-        $xml = $this->trySimpleXMLLoadString($filename);
+        $xml = $this->trySimpleXMLLoadStringPrivate($filename);
         if ($xml === false) {
             throw new Exception("Problem reading {$filename}");
         }
 
-        $namespaces = $xml->getNamespaces(true);
-
-        $xml_ss = $xml->children($namespaces['ss']);
+        $xml_ss = $xml->children(self::NAMESPACES_SS);
         foreach ($xml_ss->Worksheet as $worksheet) {
-            $worksheet_ss = self::getAttributes($worksheet, $namespaces['ss']);
+            $worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
             $worksheetNames[] = (string) $worksheet_ss['Name'];
         }
 
@@ -158,12 +183,8 @@ class Xml extends BaseReader
 
     /**
      * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
-     *
-     * @param string $filename
-     *
-     * @return array
      */
-    public function listWorksheetInfo($filename)
+    public function listWorksheetInfo(string $filename): array
     {
         File::assertFile($filename);
         if (!$this->canRead($filename)) {
@@ -172,17 +193,15 @@ class Xml extends BaseReader
 
         $worksheetInfo = [];
 
-        $xml = $this->trySimpleXMLLoadString($filename);
+        $xml = $this->trySimpleXMLLoadStringPrivate($filename);
         if ($xml === false) {
             throw new Exception("Problem reading {$filename}");
         }
 
-        $namespaces = $xml->getNamespaces(true);
-
         $worksheetID = 1;
-        $xml_ss = $xml->children($namespaces['ss']);
+        $xml_ss = $xml->children(self::NAMESPACES_SS);
         foreach ($xml_ss->Worksheet as $worksheet) {
-            $worksheet_ss = self::getAttributes($worksheet, $namespaces['ss']);
+            $worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
 
             $tmpInfo = [];
             $tmpInfo['worksheetName'] = '';
@@ -230,6 +249,19 @@ class Xml extends BaseReader
         return $worksheetInfo;
     }
 
+    /**
+     * Loads Spreadsheet from string.
+     */
+    public function loadSpreadsheetFromString(string $contents): Spreadsheet
+    {
+        // Create new Spreadsheet
+        $spreadsheet = new Spreadsheet();
+        $spreadsheet->removeSheetByIndex(0);
+
+        // Load into this instance
+        return $this->loadIntoExisting($contents, $spreadsheet, true);
+    }
+
     /**
      * Loads Spreadsheet from file.
      */
@@ -244,22 +276,26 @@ class Xml extends BaseReader
     }
 
     /**
-     * Loads from file into Spreadsheet instance.
+     * Loads from file or contents into Spreadsheet instance.
      *
-     * @param string $filename
-     *
-     * @return Spreadsheet
+     * @param string $filename file name if useContents is false else file contents
      */
-    public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
+    public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet, bool $useContents = false): Spreadsheet
     {
-        File::assertFile($filename);
-        if (!$this->canRead($filename)) {
-            throw new Exception($filename . ' is an Invalid Spreadsheet file.');
+        if ($useContents) {
+            $this->fileContents = $filename;
+            $fileOrString = 'string';
+        } else {
+            File::assertFile($filename);
+            if (!$this->canRead($filename)) {
+                throw new Exception($filename . ' is an Invalid Spreadsheet file.');
+            }
+            $fileOrString = 'file';
         }
 
-        $xml = $this->trySimpleXMLLoadString($filename);
+        $xml = $this->trySimpleXMLLoadStringPrivate($filename, $fileOrString);
         if ($xml === false) {
-            throw new Exception("Problem reading {$filename}");
+            throw new Exception($this->xmlFailMessage);
         }
 
         $namespaces = $xml->getNamespaces(true);
@@ -267,18 +303,21 @@ class Xml extends BaseReader
         (new Properties($spreadsheet))->readProperties($xml, $namespaces);
 
         $this->styles = (new Style())->parseStyles($xml, $namespaces);
+        if (isset($this->styles['Default'])) {
+            $spreadsheet->getCellXfCollection()[0]->applyFromArray($this->styles['Default']);
+        }
 
         $worksheetID = 0;
-        $xml_ss = $xml->children($namespaces['ss']);
+        $xml_ss = $xml->children(self::NAMESPACES_SS);
 
         /** @var null|SimpleXMLElement $worksheetx */
         foreach ($xml_ss->Worksheet as $worksheetx) {
             $worksheet = $worksheetx ?? new SimpleXMLElement('<xml></xml>');
-            $worksheet_ss = self::getAttributes($worksheet, $namespaces['ss']);
+            $worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
 
             if (
-                isset($this->loadSheetsOnly, $worksheet_ss['Name']) &&
-                (!in_array($worksheet_ss['Name'], /** @scrutinizer ignore-type */ $this->loadSheetsOnly))
+                isset($this->loadSheetsOnly, $worksheet_ss['Name'])
+                && (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))
             ) {
                 continue;
             }
@@ -294,11 +333,15 @@ class Xml extends BaseReader
                 //        the worksheet name in line with the formula, not the reverse
                 $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
             }
+            if (isset($worksheet_ss['Protected'])) {
+                $protection = (string) $worksheet_ss['Protected'] === '1';
+                $spreadsheet->getActiveSheet()->getProtection()->setSheet($protection);
+            }
 
             // locally scoped defined names
             if (isset($worksheet->Names[0])) {
                 foreach ($worksheet->Names[0] as $definedName) {
-                    $definedName_ss = self::getAttributes($definedName, $namespaces['ss']);
+                    $definedName_ss = self::getAttributes($definedName, self::NAMESPACES_SS);
                     $name = (string) $definedName_ss['Name'];
                     $definedValue = (string) $definedName_ss['RefersTo'];
                     $convertedValue = AddressHelper::convertFormulaToA1($definedValue);
@@ -312,15 +355,35 @@ class Xml extends BaseReader
             $columnID = 'A';
             if (isset($worksheet->Table->Column)) {
                 foreach ($worksheet->Table->Column as $columnData) {
-                    $columnData_ss = self::getAttributes($columnData, $namespaces['ss']);
+                    $columnData_ss = self::getAttributes($columnData, self::NAMESPACES_SS);
+                    $colspan = 0;
+                    if (isset($columnData_ss['Span'])) {
+                        $spanAttr = (string) $columnData_ss['Span'];
+                        if (is_numeric($spanAttr)) {
+                            $colspan = max(0, (int) $spanAttr);
+                        }
+                    }
                     if (isset($columnData_ss['Index'])) {
                         $columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']);
                     }
+                    $columnWidth = null;
                     if (isset($columnData_ss['Width'])) {
                         $columnWidth = $columnData_ss['Width'];
-                        $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
                     }
-                    ++$columnID;
+                    $columnVisible = null;
+                    if (isset($columnData_ss['Hidden'])) {
+                        $columnVisible = ((string) $columnData_ss['Hidden']) !== '1';
+                    }
+                    while ($colspan >= 0) {
+                        if (isset($columnWidth)) {
+                            $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
+                        }
+                        if (isset($columnVisible)) {
+                            $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setVisible($columnVisible);
+                        }
+                        ++$columnID;
+                        --$colspan;
+                    }
                 }
             }
 
@@ -329,14 +392,18 @@ class Xml extends BaseReader
                 $additionalMergedCells = 0;
                 foreach ($worksheet->Table->Row as $rowData) {
                     $rowHasData = false;
-                    $row_ss = self::getAttributes($rowData, $namespaces['ss']);
+                    $row_ss = self::getAttributes($rowData, self::NAMESPACES_SS);
                     if (isset($row_ss['Index'])) {
                         $rowID = (int) $row_ss['Index'];
                     }
+                    if (isset($row_ss['Hidden'])) {
+                        $rowVisible = ((string) $row_ss['Hidden']) !== '1';
+                        $spreadsheet->getActiveSheet()->getRowDimension($rowID)->setVisible($rowVisible);
+                    }
 
                     $columnID = 'A';
                     foreach ($rowData->Cell as $cell) {
-                        $cell_ss = self::getAttributes($cell, $namespaces['ss']);
+                        $cell_ss = self::getAttributes($cell, self::NAMESPACES_SS);
                         if (isset($cell_ss['Index'])) {
                             $columnID = Coordinate::stringFromColumnIndex((int) $cell_ss['Index']);
                         }
@@ -378,7 +445,7 @@ class Xml extends BaseReader
                             $cellData = $cell->Data;
                             $cellValue = (string) $cellData;
                             $type = DataType::TYPE_NULL;
-                            $cellData_ss = self::getAttributes($cellData, $namespaces['ss']);
+                            $cellData_ss = self::getAttributes($cellData, self::NAMESPACES_SS);
                             if (isset($cellData_ss['Type'])) {
                                 $cellDataType = $cellData_ss['Type'];
                                 switch ($cellDataType) {
@@ -393,6 +460,14 @@ class Xml extends BaseReader
                                     */
                                     case 'String':
                                         $type = DataType::TYPE_STRING;
+                                        $rich = $cellData->children('http://www.w3.org/TR/REC-html40');
+                                        if ($rich) {
+                                            // in case of HTML content we extract the payload
+                                            // and convert it into a rich text object
+                                            $content = $cellData->asXML() ?: '';
+                                            $html = new HelperHtml();
+                                            $cellValue = $html->toRichTextObject($content, true);
+                                        }
 
                                         break;
                                     case 'Number':
@@ -422,6 +497,7 @@ class Xml extends BaseReader
                                 }
                             }
 
+                            $originalType = $type;
                             if ($hasCalculatedValue) {
                                 $type = DataType::TYPE_FORMULA;
                                 $columnNumber = Coordinate::columnIndexFromString($columnID);
@@ -430,13 +506,13 @@ class Xml extends BaseReader
 
                             $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
                             if ($hasCalculatedValue) {
-                                $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue);
+                                $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue, $originalType === DataType::TYPE_NUMERIC);
                             }
                             $rowHasData = true;
                         }
 
                         if (isset($cell->Comment)) {
-                            $this->parseCellComment($cell->Comment, $namespaces, $spreadsheet, $columnID, $rowID);
+                            $this->parseCellComment($cell->Comment, $spreadsheet, $columnID, $rowID);
                         }
 
                         if (isset($cell_ss['StyleID'])) {
@@ -465,11 +541,127 @@ class Xml extends BaseReader
 
                     ++$rowID;
                 }
+            }
 
-                if (isset($namespaces['x'])) {
-                    $xmlX = $worksheet->children($namespaces['x']);
-                    if (isset($xmlX->WorksheetOptions)) {
-                        (new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet);
+            $dataValidations = new Xml\DataValidations();
+            $dataValidations->loadDataValidations($worksheet, $spreadsheet);
+            $xmlX = $worksheet->children(Namespaces::URN_EXCEL);
+            if (isset($xmlX->WorksheetOptions)) {
+                if (isset($xmlX->WorksheetOptions->ShowPageBreakZoom)) {
+                    $spreadsheet->getActiveSheet()->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW);
+                }
+                if (isset($xmlX->WorksheetOptions->Zoom)) {
+                    $zoomScaleNormal = (int) $xmlX->WorksheetOptions->Zoom;
+                    if ($zoomScaleNormal > 0) {
+                        $spreadsheet->getActiveSheet()->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
+                        $spreadsheet->getActiveSheet()->getSheetView()->setZoomScale($zoomScaleNormal);
+                    }
+                }
+                if (isset($xmlX->WorksheetOptions->PageBreakZoom)) {
+                    $zoomScaleNormal = (int) $xmlX->WorksheetOptions->PageBreakZoom;
+                    if ($zoomScaleNormal > 0) {
+                        $spreadsheet->getActiveSheet()->getSheetView()->setZoomScaleSheetLayoutView($zoomScaleNormal);
+                    }
+                }
+                if (isset($xmlX->WorksheetOptions->ShowPageBreakZoom)) {
+                    $spreadsheet->getActiveSheet()->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW);
+                }
+                if (isset($xmlX->WorksheetOptions->FreezePanes)) {
+                    $freezeRow = $freezeColumn = 1;
+                    if (isset($xmlX->WorksheetOptions->SplitHorizontal)) {
+                        $freezeRow = (int) $xmlX->WorksheetOptions->SplitHorizontal + 1;
+                    }
+                    if (isset($xmlX->WorksheetOptions->SplitVertical)) {
+                        $freezeColumn = (int) $xmlX->WorksheetOptions->SplitVertical + 1;
+                    }
+                    $leftTopRow = (string) $xmlX->WorksheetOptions->TopRowBottomPane;
+                    $leftTopColumn = (string) $xmlX->WorksheetOptions->LeftColumnRightPane;
+                    if (is_numeric($leftTopRow) && is_numeric($leftTopColumn)) {
+                        $leftTopCoordinate = Coordinate::stringFromColumnIndex((int) $leftTopColumn + 1) . (string) ($leftTopRow + 1);
+                        $spreadsheet->getActiveSheet()->freezePane(Coordinate::stringFromColumnIndex($freezeColumn) . (string) $freezeRow, $leftTopCoordinate, !isset($xmlX->WorksheetOptions->FrozenNoSplit));
+                    } else {
+                        $spreadsheet->getActiveSheet()->freezePane(Coordinate::stringFromColumnIndex($freezeColumn) . (string) $freezeRow, null, !isset($xmlX->WorksheetOptions->FrozenNoSplit));
+                    }
+                } elseif (isset($xmlX->WorksheetOptions->SplitVertical) || isset($xmlX->WorksheetOptions->SplitHorizontal)) {
+                    if (isset($xmlX->WorksheetOptions->SplitHorizontal)) {
+                        $ySplit = (int) $xmlX->WorksheetOptions->SplitHorizontal;
+                        $spreadsheet->getActiveSheet()->setYSplit($ySplit);
+                    }
+                    if (isset($xmlX->WorksheetOptions->SplitVertical)) {
+                        $xSplit = (int) $xmlX->WorksheetOptions->SplitVertical;
+                        $spreadsheet->getActiveSheet()->setXSplit($xSplit);
+                    }
+                    if (isset($xmlX->WorksheetOptions->LeftColumnVisible) || isset($xmlX->WorksheetOptions->TopRowVisible)) {
+                        $leftTopColumn = $leftTopRow = 1;
+                        if (isset($xmlX->WorksheetOptions->LeftColumnVisible)) {
+                            $leftTopColumn = 1 + (int) $xmlX->WorksheetOptions->LeftColumnVisible;
+                        }
+                        if (isset($xmlX->WorksheetOptions->TopRowVisible)) {
+                            $leftTopRow = 1 + (int) $xmlX->WorksheetOptions->TopRowVisible;
+                        }
+                        $leftTopCoordinate = Coordinate::stringFromColumnIndex($leftTopColumn) . "$leftTopRow";
+                        $spreadsheet->getActiveSheet()->setTopLeftCell($leftTopCoordinate);
+                    }
+
+                    $leftTopColumn = $leftTopRow = 1;
+                    if (isset($xmlX->WorksheetOptions->LeftColumnRightPane)) {
+                        $leftTopColumn = 1 + (int) $xmlX->WorksheetOptions->LeftColumnRightPane;
+                    }
+                    if (isset($xmlX->WorksheetOptions->TopRowBottomPane)) {
+                        $leftTopRow = 1 + (int) $xmlX->WorksheetOptions->TopRowBottomPane;
+                    }
+                    $leftTopCoordinate = Coordinate::stringFromColumnIndex($leftTopColumn) . "$leftTopRow";
+                    $spreadsheet->getActiveSheet()->setPaneTopLeftCell($leftTopCoordinate);
+                }
+                (new PageSettings($xmlX))->loadPageSettings($spreadsheet);
+                if (isset($xmlX->WorksheetOptions->TopRowVisible, $xmlX->WorksheetOptions->LeftColumnVisible)) {
+                    $leftTopRow = (string) $xmlX->WorksheetOptions->TopRowVisible;
+                    $leftTopColumn = (string) $xmlX->WorksheetOptions->LeftColumnVisible;
+                    if (is_numeric($leftTopRow) && is_numeric($leftTopColumn)) {
+                        $leftTopCoordinate = Coordinate::stringFromColumnIndex((int) $leftTopColumn + 1) . (string) ($leftTopRow + 1);
+                        $spreadsheet->getActiveSheet()->setTopLeftCell($leftTopCoordinate);
+                    }
+                }
+                $rangeCalculated = false;
+                if (isset($xmlX->WorksheetOptions->Panes->Pane->RangeSelection)) {
+                    if (1 === preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $xmlX->WorksheetOptions->Panes->Pane->RangeSelection, $selectionMatches)) {
+                        $selectedCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
+                            . $selectionMatches[1]
+                            . ':'
+                            . Coordinate::stringFromColumnIndex((int) $selectionMatches[4])
+                            . $selectionMatches[3];
+                        $spreadsheet->getActiveSheet()->setSelectedCells($selectedCell);
+                        $rangeCalculated = true;
+                    }
+                }
+                if (!$rangeCalculated) {
+                    if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveRow)) {
+                        $activeRow = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveRow;
+                    } else {
+                        $activeRow = 0;
+                    }
+                    if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveCol)) {
+                        $activeColumn = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveCol;
+                    } else {
+                        $activeColumn = 0;
+                    }
+                    if (is_numeric($activeRow) && is_numeric($activeColumn)) {
+                        $selectedCell = Coordinate::stringFromColumnIndex((int) $activeColumn + 1) . (string) ($activeRow + 1);
+                        $spreadsheet->getActiveSheet()->setSelectedCells($selectedCell);
+                    }
+                }
+            }
+            if (isset($xmlX->PageBreaks)) {
+                if (isset($xmlX->PageBreaks->ColBreaks)) {
+                    foreach ($xmlX->PageBreaks->ColBreaks->ColBreak as $colBreak) {
+                        $colBreak = (string) $colBreak->Column;
+                        $spreadsheet->getActiveSheet()->setBreak([1 + (int) $colBreak, 1], Worksheet::BREAK_COLUMN);
+                    }
+                }
+                if (isset($xmlX->PageBreaks->RowBreaks)) {
+                    foreach ($xmlX->PageBreaks->RowBreaks->RowBreak as $rowBreak) {
+                        $rowBreak = (string) $rowBreak->Row;
+                        $spreadsheet->getActiveSheet()->setBreak([1, (int) $rowBreak], Worksheet::BREAK_ROW);
                     }
                 }
             }
@@ -477,10 +669,14 @@ class Xml extends BaseReader
         }
 
         // Globally scoped defined names
-        $activeWorksheet = $spreadsheet->setActiveSheetIndex(0);
+        $activeSheetIndex = 0;
+        if (isset($xml->ExcelWorkbook->ActiveSheet)) {
+            $activeSheetIndex = (int) (string) $xml->ExcelWorkbook->ActiveSheet;
+        }
+        $activeWorksheet = $spreadsheet->setActiveSheetIndex($activeSheetIndex);
         if (isset($xml->Names[0])) {
             foreach ($xml->Names[0] as $definedName) {
-                $definedName_ss = self::getAttributes($definedName, $namespaces['ss']);
+                $definedName_ss = self::getAttributes($definedName, self::NAMESPACES_SS);
                 $name = (string) $definedName_ss['Name'];
                 $definedValue = (string) $definedName_ss['RefersTo'];
                 $convertedValue = AddressHelper::convertFormulaToA1($definedValue);
@@ -497,12 +693,11 @@ class Xml extends BaseReader
 
     protected function parseCellComment(
         SimpleXMLElement $comment,
-        array $namespaces,
         Spreadsheet $spreadsheet,
         string $columnID,
         int $rowID
     ): void {
-        $commentAttributes = $comment->attributes($namespaces['ss']);
+        $commentAttributes = $comment->attributes(self::NAMESPACES_SS);
         $author = 'unknown';
         if (isset($commentAttributes->Author)) {
             $author = (string) $commentAttributes->Author;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php
new file mode 100644
index 0000000..531f8c3
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xml;
+
+use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
+use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use SimpleXMLElement;
+
+class DataValidations
+{
+    private const OPERATOR_MAPPINGS = [
+        'between' => DataValidation::OPERATOR_BETWEEN,
+        'equal' => DataValidation::OPERATOR_EQUAL,
+        'greater' => DataValidation::OPERATOR_GREATERTHAN,
+        'greaterorequal' => DataValidation::OPERATOR_GREATERTHANOREQUAL,
+        'less' => DataValidation::OPERATOR_LESSTHAN,
+        'lessorequal' => DataValidation::OPERATOR_LESSTHANOREQUAL,
+        'notbetween' => DataValidation::OPERATOR_NOTBETWEEN,
+        'notequal' => DataValidation::OPERATOR_NOTEQUAL,
+    ];
+
+    private const TYPE_MAPPINGS = [
+        'textlength' => DataValidation::TYPE_TEXTLENGTH,
+    ];
+
+    private int $thisRow = 0;
+
+    private int $thisColumn = 0;
+
+    private function replaceR1C1(array $matches): string
+    {
+        return AddressHelper::convertToA1($matches[0], $this->thisRow, $this->thisColumn, false);
+    }
+
+    public function loadDataValidations(SimpleXMLElement $worksheet, Spreadsheet $spreadsheet): void
+    {
+        $xmlX = $worksheet->children(Namespaces::URN_EXCEL);
+        $sheet = $spreadsheet->getActiveSheet();
+        /** @var callable $pregCallback */
+        $pregCallback = [$this, 'replaceR1C1'];
+        foreach ($xmlX->DataValidation as $dataValidation) {
+            $cells = [];
+            $validation = new DataValidation();
+
+            // set defaults
+            $validation->setShowDropDown(true);
+            $validation->setShowInputMessage(true);
+            $validation->setShowErrorMessage(true);
+            $validation->setShowDropDown(true);
+            $this->thisRow = 1;
+            $this->thisColumn = 1;
+
+            foreach ($dataValidation as $tagName => $tagValue) {
+                $tagValue = (string) $tagValue;
+                $tagValueLower = strtolower($tagValue);
+                switch ($tagName) {
+                    case 'Range':
+                        foreach (explode(',', $tagValue) as $range) {
+                            $cell = '';
+                            if (preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $range, $selectionMatches) === 1) {
+                                // range
+                                $firstCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
+                                    . $selectionMatches[1];
+                                $cell = $firstCell
+                                    . ':'
+                                    . Coordinate::stringFromColumnIndex((int) $selectionMatches[4])
+                                    . $selectionMatches[3];
+                                $this->thisRow = (int) $selectionMatches[1];
+                                $this->thisColumn = (int) $selectionMatches[2];
+                                $sheet->getCell($firstCell);
+                            } elseif (preg_match('/^R(\d+)C(\d+)$/', (string) $range, $selectionMatches) === 1) {
+                                // cell
+                                $cell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
+                                    . $selectionMatches[1];
+                                $sheet->getCell($cell);
+                                $this->thisRow = (int) $selectionMatches[1];
+                                $this->thisColumn = (int) $selectionMatches[2];
+                            } elseif (preg_match('/^C(\d+)$/', (string) $range, $selectionMatches) === 1) {
+                                // column
+                                $firstCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[1])
+                                    . '1';
+                                $cell = $firstCell
+                                    . ':'
+                                    . Coordinate::stringFromColumnIndex((int) $selectionMatches[1])
+                                    . ((string) AddressRange::MAX_ROW);
+                                $this->thisColumn = (int) $selectionMatches[1];
+                                $sheet->getCell($firstCell);
+                            } elseif (preg_match('/^R(\d+)$/', (string) $range, $selectionMatches)) {
+                                // row
+                                $firstCell = 'A'
+                                    . $selectionMatches[1];
+                                $cell = $firstCell
+                                    . ':'
+                                    . AddressRange::MAX_COLUMN
+                                    . $selectionMatches[1];
+                                $this->thisRow = (int) $selectionMatches[1];
+                                $sheet->getCell($firstCell);
+                            }
+
+                            $validation->setSqref($cell);
+                            $stRange = $sheet->shrinkRangeToFit($cell);
+                            $cells = array_merge($cells, Coordinate::extractAllCellReferencesInRange($stRange));
+                        }
+
+                        break;
+                    case 'Type':
+                        $validation->setType(self::TYPE_MAPPINGS[$tagValueLower] ?? $tagValueLower);
+
+                        break;
+                    case 'Qualifier':
+                        $validation->setOperator(self::OPERATOR_MAPPINGS[$tagValueLower] ?? $tagValueLower);
+
+                        break;
+                    case 'InputTitle':
+                        $validation->setPromptTitle($tagValue);
+
+                        break;
+                    case 'InputMessage':
+                        $validation->setPrompt($tagValue);
+
+                        break;
+                    case 'InputHide':
+                        $validation->setShowInputMessage(false);
+
+                        break;
+                    case 'ErrorStyle':
+                        $validation->setErrorStyle($tagValueLower);
+
+                        break;
+                    case 'ErrorTitle':
+                        $validation->setErrorTitle($tagValue);
+
+                        break;
+                    case 'ErrorMessage':
+                        $validation->setError($tagValue);
+
+                        break;
+                    case 'ErrorHide':
+                        $validation->setShowErrorMessage(false);
+
+                        break;
+                    case 'ComboHide':
+                        $validation->setShowDropDown(false);
+
+                        break;
+                    case 'UseBlank':
+                        $validation->setAllowBlank(true);
+
+                        break;
+                    case 'CellRangeList':
+                        // FIXME missing FIXME
+
+                        break;
+                    case 'Min':
+                    case 'Value':
+                        $tagValue = (string) preg_replace_callback(AddressHelper::R1C1_COORDINATE_REGEX, $pregCallback, $tagValue);
+                        $validation->setFormula1($tagValue);
+
+                        break;
+                    case 'Max':
+                        $tagValue = (string) preg_replace_callback(AddressHelper::R1C1_COORDINATE_REGEX, $pregCallback, $tagValue);
+                        $validation->setFormula2($tagValue);
+
+                        break;
+                }
+            }
+
+            foreach ($cells as $cell) {
+                $sheet->getCell($cell)->setDataValidation(clone $validation);
+            }
+        }
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php
index a12986c..8f9d464 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php
@@ -2,6 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Reader\Xml;
 
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
 use SimpleXMLElement;
@@ -9,14 +10,11 @@ use stdClass;
 
 class PageSettings
 {
-    /**
-     * @var stdClass
-     */
-    private $printSettings;
+    private stdClass $printSettings;
 
-    public function __construct(SimpleXMLElement $xmlX, array $namespaces)
+    public function __construct(SimpleXMLElement $xmlX)
     {
-        $printSettings = $this->pageSetup($xmlX, $namespaces, $this->getPrintDefaults());
+        $printSettings = $this->pageSetup($xmlX, $this->getPrintDefaults());
         $this->printSettings = $this->printSetup($xmlX, $printSettings);
     }
 
@@ -56,34 +54,31 @@ class PageSettings
         ];
     }
 
-    private function pageSetup(SimpleXMLElement $xmlX, array $namespaces, stdClass $printDefaults): stdClass
+    private function pageSetup(SimpleXMLElement $xmlX, stdClass $printDefaults): stdClass
     {
         if (isset($xmlX->WorksheetOptions->PageSetup)) {
             foreach ($xmlX->WorksheetOptions->PageSetup as $pageSetupData) {
                 foreach ($pageSetupData as $pageSetupKey => $pageSetupValue) {
-                    /** @scrutinizer ignore-call */
-                    $pageSetupAttributes = $pageSetupValue->attributes($namespaces['x']);
-                    if (!$pageSetupAttributes) {
-                        continue;
-                    }
+                    $pageSetupAttributes = $pageSetupValue->attributes(Namespaces::URN_EXCEL);
+                    if ($pageSetupAttributes !== null) {
+                        switch ($pageSetupKey) {
+                            case 'Layout':
+                                $this->setLayout($printDefaults, $pageSetupAttributes);
 
-                    switch ($pageSetupKey) {
-                        case 'Layout':
-                            $this->setLayout($printDefaults, $pageSetupAttributes);
+                                break;
+                            case 'Header':
+                                $printDefaults->headerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
 
-                            break;
-                        case 'Header':
-                            $printDefaults->headerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
+                                break;
+                            case 'Footer':
+                                $printDefaults->footerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
 
-                            break;
-                        case 'Footer':
-                            $printDefaults->footerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
+                                break;
+                            case 'PageMargins':
+                                $this->setMargins($printDefaults, $pageSetupAttributes);
 
-                            break;
-                        case 'PageMargins':
-                            $this->setMargins($printDefaults, $pageSetupAttributes);
-
-                            break;
+                                break;
+                        }
                     }
                 }
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php
index 9e10526..17e1121 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php
@@ -8,10 +8,7 @@ use SimpleXMLElement;
 
 class Properties
 {
-    /**
-     * @var Spreadsheet
-     */
-    protected $spreadsheet;
+    protected Spreadsheet $spreadsheet;
 
     public function __construct(Spreadsheet $spreadsheet)
     {
@@ -39,7 +36,7 @@ class Properties
 
     protected function readCustomProperties(SimpleXMLElement $xml, array $namespaces): void
     {
-        if (isset($xml->CustomDocumentProperties)) {
+        if (isset($xml->CustomDocumentProperties) && is_iterable($xml->CustomDocumentProperties[0])) {
             $docProps = $this->spreadsheet->getProperties();
 
             foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
@@ -92,6 +89,10 @@ class Properties
             case 'Manager':
                 $docProps->setManager($stringValue);
 
+                break;
+            case 'HyperlinkBase':
+                $docProps->setHyperlinkBase($stringValue);
+
                 break;
             case 'Keywords':
                 $docProps->setKeywords($stringValue);
@@ -110,17 +111,10 @@ class Properties
         ?SimpleXMLElement $propertyValue,
         SimpleXMLElement $propertyAttributes
     ): void {
-        $propertyType = DocumentProperties::PROPERTY_TYPE_UNKNOWN;
-
         switch ((string) $propertyAttributes) {
-            case 'string':
-                $propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
-                $propertyValue = trim((string) $propertyValue);
-
-                break;
             case 'boolean':
                 $propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
-                $propertyValue = (bool) $propertyValue;
+                $propertyValue = (bool) (string) $propertyValue;
 
                 break;
             case 'integer':
@@ -134,9 +128,15 @@ class Properties
 
                 break;
             case 'dateTime.tz':
+            case 'dateTime.iso8601tz':
                 $propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
                 $propertyValue = trim((string) $propertyValue);
 
+                break;
+            default:
+                $propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
+                $propertyValue = trim((string) $propertyValue);
+
                 break;
         }
 
@@ -150,8 +150,6 @@ class Properties
 
     private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
     {
-        return ($simple === null)
-            ? new SimpleXMLElement('<xml></xml>')
-            : ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
+        return ($simple === null) ? new SimpleXMLElement('<xml></xml>') : ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php
index 0e3cd16..c6b5149 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php
@@ -2,20 +2,21 @@
 
 namespace PhpOffice\PhpSpreadsheet\Reader\Xml;
 
+use PhpOffice\PhpSpreadsheet\Style\Protection;
 use SimpleXMLElement;
 
 class Style
 {
     /**
      * Formats.
-     *
-     * @var array
      */
-    protected $styles = [];
+    protected array $styles = [];
 
     public function parseStyles(SimpleXMLElement $xml, array $namespaces): array
     {
-        if (!isset($xml->Styles)) {
+        $children = $xml->children('urn:schemas-microsoft-com:office:spreadsheet');
+        $stylesXml = $children->Styles[0];
+        if (!isset($stylesXml) || !is_iterable($stylesXml)) {
             return [];
         }
 
@@ -25,15 +26,15 @@ class Style
         $fillStyleParser = new Style\Fill();
         $numberFormatStyleParser = new Style\NumberFormat();
 
-        foreach ($xml->Styles[0] as $style) {
+        foreach ($stylesXml as $style) {
             $style_ss = self::getAttributes($style, $namespaces['ss']);
             $styleID = (string) $style_ss['ID'];
             $this->styles[$styleID] = $this->styles['Default'] ?? [];
 
-            $alignment = $border = $font = $fill = $numberFormat = [];
+            $alignment = $border = $font = $fill = $numberFormat = $protection = [];
 
             foreach ($style as $styleType => $styleDatax) {
-                $styleData = $styleDatax ?? new SimpleXMLElement('<xml></xml>');
+                $styleData = self::getSxml($styleDatax);
                 $styleAttributes = $styleData->attributes($namespaces['ss']);
 
                 switch ($styleType) {
@@ -64,20 +65,43 @@ class Style
                             $numberFormat = $numberFormatStyleParser->parseStyle($styleAttributes);
                         }
 
+                        break;
+                    case 'Protection':
+                        $locked = $hidden = null;
+                        $styleAttributesP = $styleData->attributes($namespaces['x']);
+                        if (isset($styleAttributes['Protected'])) {
+                            $locked = ((bool) (string) $styleAttributes['Protected']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED;
+                        }
+                        if (isset($styleAttributesP['HideFormula'])) {
+                            $hidden = ((bool) (string) $styleAttributesP['HideFormula']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED;
+                        }
+                        if ($locked !== null || $hidden !== null) {
+                            $protection['protection'] = [];
+                            if ($locked !== null) {
+                                $protection['protection']['locked'] = $locked;
+                            }
+                            if ($hidden !== null) {
+                                $protection['protection']['hidden'] = $hidden;
+                            }
+                        }
+
                         break;
                 }
             }
 
-            $this->styles[$styleID] = array_merge($alignment, $border, $font, $fill, $numberFormat);
+            $this->styles[$styleID] = array_merge($alignment, $border, $font, $fill, $numberFormat, $protection);
         }
 
         return $this->styles;
     }
 
-    protected static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
+    private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
     {
-        return ($simple === null)
-            ? new SimpleXMLElement('<xml></xml>')
-            : ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
+        return ($simple === null) ? new SimpleXMLElement('<xml></xml>') : ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
+    }
+
+    private static function getSxml(?SimpleXMLElement $simple): SimpleXMLElement
+    {
+        return ($simple !== null) ? $simple : new SimpleXMLElement('<xml></xml>');
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php
index 8aefd9c..dfde17a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php
@@ -20,6 +20,18 @@ class Border extends StyleBase
      */
     public const BORDER_MAPPINGS = [
         'borderStyle' => [
+            'continuous' => BorderStyle::BORDER_HAIR,
+            'dash' => BorderStyle::BORDER_DASHED,
+            'dashdot' => BorderStyle::BORDER_DASHDOT,
+            'dashdotdot' => BorderStyle::BORDER_DASHDOTDOT,
+            'dot' => BorderStyle::BORDER_DOTTED,
+            'double' => BorderStyle::BORDER_DOUBLE,
+            '0continuous' => BorderStyle::BORDER_HAIR,
+            '0dash' => BorderStyle::BORDER_DASHED,
+            '0dashdot' => BorderStyle::BORDER_DASHDOT,
+            '0dashdotdot' => BorderStyle::BORDER_DASHDOTDOT,
+            '0dot' => BorderStyle::BORDER_DOTTED,
+            '0double' => BorderStyle::BORDER_DOUBLE,
             '1continuous' => BorderStyle::BORDER_THIN,
             '1dash' => BorderStyle::BORDER_DASHED,
             '1dashdot' => BorderStyle::BORDER_DASHDOT,
@@ -58,8 +70,8 @@ class Border extends StyleBase
                 $borderStyleValue = (string) $borderStyleValuex;
                 switch ($borderStyleKey) {
                     case 'Position':
-                        [$borderPosition, $diagonalDirection] =
-                            $this->parsePosition($borderStyleValue, $diagonalDirection);
+                        [$borderPosition, $diagonalDirection]
+                            = $this->parsePosition($borderStyleValue, $diagonalDirection);
 
                         break;
                     case 'Color':
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php
index 16ab44d..5f82488 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php
@@ -56,11 +56,11 @@ class Font extends StyleBase
 
                     break;
                 case 'Bold':
-                    $style['font']['bold'] = true;
+                    $style['font']['bold'] = $styleAttributeValue === '1';
 
                     break;
                 case 'Italic':
-                    $style['font']['italic'] = true;
+                    $style['font']['italic'] = $styleAttributeValue === '1';
 
                     break;
                 case 'Underline':
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
index fc9ace8..8103a71 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
@@ -25,8 +25,6 @@ abstract class StyleBase
 
     protected static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
     {
-        return ($simple === null)
-            ? new SimpleXMLElement('<xml></xml>')
-            : ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
+        return ($simple === null) ? new SimpleXMLElement('<xml></xml>') : ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
index b88199f..cdbe378 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\Style\Conditional;
@@ -14,29 +15,24 @@ class ReferenceHelper
 {
     /**    Constants                */
     /**    Regular Expressions      */
-    const REFHELPER_REGEXP_CELLREF = '((\w*|\'[^!]*\')!)?(?<![:a-z\$])(\$?[a-z]{1,3}\$?\d+)(?=[^:!\d\'])';
-    const REFHELPER_REGEXP_CELLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}\$?\d+):(\$?[a-z]{1,3}\$?\d+)';
-    const REFHELPER_REGEXP_ROWRANGE = '((\w*|\'[^!]*\')!)?(\$?\d+):(\$?\d+)';
-    const REFHELPER_REGEXP_COLRANGE = '((\w*|\'[^!]*\')!)?(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
+    private const SHEETNAME_PART = '((\w*|\'[^!]*\')!)';
+    private const SHEETNAME_PART_WITH_SLASHES = '/' . self::SHEETNAME_PART . '/';
+    const REFHELPER_REGEXP_CELLREF = self::SHEETNAME_PART . '?(?<![:a-z1-9_\.\$])(\$?[a-z]{1,3}\$?\d+)(?=[^:!\d\'])';
+    const REFHELPER_REGEXP_CELLRANGE = self::SHEETNAME_PART . '?(\$?[a-z]{1,3}\$?\d+):(\$?[a-z]{1,3}\$?\d+)';
+    const REFHELPER_REGEXP_ROWRANGE = self::SHEETNAME_PART . '?(\$?\d+):(\$?\d+)';
+    const REFHELPER_REGEXP_COLRANGE = self::SHEETNAME_PART . '?(\$?[a-z]{1,3}):(\$?[a-z]{1,3})';
 
     /**
      * Instance of this class.
-     *
-     * @var ?ReferenceHelper
      */
-    private static $instance;
+    private static ?ReferenceHelper $instance = null;
 
-    /**
-     * @var CellReferenceHelper
-     */
-    private $cellReferenceHelper;
+    private ?CellReferenceHelper $cellReferenceHelper = null;
 
     /**
      * Get an instance of this class.
-     *
-     * @return ReferenceHelper
      */
-    public static function getInstance()
+    public static function getInstance(): self
     {
         if (self::$instance === null) {
             self::$instance = new self();
@@ -58,10 +54,8 @@ class ReferenceHelper
      *
      * @param string $a First column to test (e.g. 'AA')
      * @param string $b Second column to test (e.g. 'Z')
-     *
-     * @return int
      */
-    public static function columnSort($a, $b)
+    public static function columnSort(string $a, string $b): int
     {
         return strcasecmp(strlen($a) . $a, strlen($b) . $b);
     }
@@ -72,36 +66,27 @@ class ReferenceHelper
      *
      * @param string $a First column to test (e.g. 'AA')
      * @param string $b Second column to test (e.g. 'Z')
-     *
-     * @return int
      */
-    public static function columnReverseSort($a, $b)
+    public static function columnReverseSort(string $a, string $b): int
     {
         return -strcasecmp(strlen($a) . $a, strlen($b) . $b);
     }
 
-    /** @var int */
-    private static $scrutinizer0 = 0;
-
     /**
      * Compare two cell addresses
      * Intended for use as a Callback function for sorting cell addresses by column and row.
      *
      * @param string $a First cell to test (e.g. 'AA1')
      * @param string $b Second cell to test (e.g. 'Z1')
-     *
-     * @return int
      */
-    public static function cellSort($a, $b)
+    public static function cellSort(string $a, string $b): int
     {
-        $ac = $bc = '';
-        $ar = self::$scrutinizer0;
-        $br = 0;
         sscanf($a, '%[A-Z]%d', $ac, $ar);
+        /** @var int $ar */
+        /** @var string $ac */
         sscanf($b, '%[A-Z]%d', $bc, $br);
-
-        $ac = (string) $ac;
-        $bc = (string) $bc;
+        /** @var int $br */
+        /** @var string $bc */
         if ($ar === $br) {
             return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
         }
@@ -115,19 +100,15 @@ class ReferenceHelper
      *
      * @param string $a First cell to test (e.g. 'AA1')
      * @param string $b Second cell to test (e.g. 'Z1')
-     *
-     * @return int
      */
-    public static function cellReverseSort($a, $b)
+    public static function cellReverseSort(string $a, string $b): int
     {
-        $ac = $bc = '';
-        $ar = self::$scrutinizer0;
-        $br = 0;
         sscanf($a, '%[A-Z]%d', $ac, $ar);
+        /** @var int $ar */
+        /** @var string $ac */
         sscanf($b, '%[A-Z]%d', $bc, $br);
-
-        $ac = (string) $ac;
-        $bc = (string) $bc;
+        /** @var int $br */
+        /** @var string $bc */
         if ($ar === $br) {
             return -strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
         }
@@ -142,7 +123,7 @@ class ReferenceHelper
      * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
      * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
      */
-    protected function adjustPageBreaks(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
+    protected function adjustPageBreaks(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
     {
         $aBreaks = $worksheet->getBreaks();
         ($numberOfColumns > 0 || $numberOfRows > 0)
@@ -150,7 +131,9 @@ class ReferenceHelper
             : uksort($aBreaks, [self::class, 'cellSort']);
 
         foreach ($aBreaks as $cellAddress => $value) {
-            if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
+            /** @var CellReferenceHelper */
+            $cellReferenceHelper = $this->cellReferenceHelper;
+            if ($cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
                 //    If we're deleting, then clear any defined breaks that are within the range
                 //        of rows/columns that we're deleting
                 $worksheet->setBreak($cellAddress, Worksheet::BREAK_NONE);
@@ -171,14 +154,16 @@ class ReferenceHelper
      *
      * @param Worksheet $worksheet The worksheet that we're editing
      */
-    protected function adjustComments($worksheet): void
+    protected function adjustComments(Worksheet $worksheet): void
     {
         $aComments = $worksheet->getComments();
         $aNewComments = []; // the new array of all comments
 
         foreach ($aComments as $cellAddress => &$value) {
             // Any comments inside a deleted range will be ignored
-            if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === false) {
+            /** @var CellReferenceHelper */
+            $cellReferenceHelper = $this->cellReferenceHelper;
+            if ($cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === false) {
                 // Otherwise build a new array of comments indexed by the adjusted cell reference
                 $newReference = $this->updateCellReference($cellAddress);
                 $aNewComments[$newReference] = $value;
@@ -195,7 +180,7 @@ class ReferenceHelper
      * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
      * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
      */
-    protected function adjustHyperlinks($worksheet, $numberOfColumns, $numberOfRows): void
+    protected function adjustHyperlinks(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
     {
         $aHyperlinkCollection = $worksheet->getHyperlinkCollection();
         ($numberOfColumns > 0 || $numberOfRows > 0)
@@ -204,7 +189,9 @@ class ReferenceHelper
 
         foreach ($aHyperlinkCollection as $cellAddress => $value) {
             $newReference = $this->updateCellReference($cellAddress);
-            if ($this->cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
+            /** @var CellReferenceHelper */
+            $cellReferenceHelper = $this->cellReferenceHelper;
+            if ($cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
                 $worksheet->setHyperlink($cellAddress, null);
             } elseif ($cellAddress !== $newReference) {
                 $worksheet->setHyperlink($newReference, $value);
@@ -220,7 +207,7 @@ class ReferenceHelper
      * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
      * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
      */
-    protected function adjustConditionalFormatting($worksheet, $numberOfColumns, $numberOfRows): void
+    protected function adjustConditionalFormatting(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
     {
         $aStyles = $worksheet->getConditionalStylesCollection();
         ($numberOfColumns > 0 || $numberOfRows > 0)
@@ -236,9 +223,11 @@ class ReferenceHelper
                 $conditions = $cfRule->getConditions();
                 foreach ($conditions as &$condition) {
                     if (is_string($condition)) {
+                        /** @var CellReferenceHelper */
+                        $cellReferenceHelper = $this->cellReferenceHelper;
                         $condition = $this->updateFormulaReferences(
                             $condition,
-                            $this->cellReferenceHelper->beforeCellAddress(),
+                            $cellReferenceHelper->beforeCellAddress(),
                             $numberOfColumns,
                             $numberOfRows,
                             $worksheet->getTitle(),
@@ -259,7 +248,7 @@ class ReferenceHelper
      * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
      * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
      */
-    protected function adjustDataValidations(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
+    protected function adjustDataValidations(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
     {
         $aDataValidationCollection = $worksheet->getDataValidationCollection();
         ($numberOfColumns > 0 || $numberOfRows > 0)
@@ -299,7 +288,7 @@ class ReferenceHelper
      * @param int $numberOfColumns Number of columns to insert/delete (negative values indicate deletion)
      * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
      */
-    protected function adjustProtectedCells(Worksheet $worksheet, $numberOfColumns, $numberOfRows): void
+    protected function adjustProtectedCells(Worksheet $worksheet, int $numberOfColumns, int $numberOfRows): void
     {
         $aProtectedCells = $worksheet->getProtectedCells();
         ($numberOfColumns > 0 || $numberOfRows > 0)
@@ -342,7 +331,7 @@ class ReferenceHelper
      * @param int $beforeRow Number of the row we're inserting/deleting before
      * @param int $numberOfRows Number of rows to insert/delete (negative values indicate deletion)
      */
-    protected function adjustRowDimensions(Worksheet $worksheet, $beforeRow, $numberOfRows): void
+    protected function adjustRowDimensions(Worksheet $worksheet, int $beforeRow, int $numberOfRows): void
     {
         $aRowDimensions = array_reverse($worksheet->getRowDimensions(), true);
         if (!empty($aRowDimensions)) {
@@ -385,18 +374,20 @@ class ReferenceHelper
         $remove = ($numberOfColumns < 0 || $numberOfRows < 0);
 
         if (
-            $this->cellReferenceHelper === null ||
-            $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
+            $this->cellReferenceHelper === null
+            || $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
         ) {
             $this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
         }
 
         // Get coordinate of $beforeCellAddress
-        [$beforeColumn, $beforeRow] = Coordinate::indexesFromString($beforeCellAddress);
+        [$beforeColumn, $beforeRow, $beforeColumnString] = Coordinate::indexesFromString($beforeCellAddress);
 
         // Clear cells if we are removing columns or rows
         $highestColumn = $worksheet->getHighestColumn();
+        $highestDataColumn = $worksheet->getHighestDataColumn();
         $highestRow = $worksheet->getHighestRow();
+        $highestDataRow = $worksheet->getHighestDataRow();
 
         // 1. Clear column strips if we are removing columns
         if ($numberOfColumns < 0 && $beforeColumn - 2 + $numberOfColumns > 0) {
@@ -408,21 +399,19 @@ class ReferenceHelper
             $this->clearRowStrips($highestColumn, $beforeColumn, $beforeRow, $numberOfRows, $worksheet);
         }
 
-        // Find missing coordinates. This is important when inserting column before the last column
-        $cellCollection = $worksheet->getCellCollection();
-        $missingCoordinates = array_filter(
-            array_map(function ($row) use ($highestColumn) {
-                return $highestColumn . $row;
-            }, range(1, $highestRow)),
-            function ($coordinate) use ($cellCollection) {
-                return $cellCollection->has($coordinate) === false;
-            }
-        );
-
-        // Create missing cells with null values
-        if (!empty($missingCoordinates)) {
-            foreach ($missingCoordinates as $coordinate) {
-                $worksheet->createNewCell($coordinate);
+        // Find missing coordinates. This is important when inserting or deleting column before the last column
+        $startRow = $startCol = 1;
+        $startColString = 'A';
+        if ($numberOfRows === 0) {
+            $startCol = $beforeColumn;
+            $startColString = $beforeColumnString;
+        } elseif ($numberOfColumns === 0) {
+            $startRow = $beforeRow;
+        }
+        $highColumn = Coordinate::columnIndexFromString($highestDataColumn);
+        for ($row = $startRow; $row <= $highestDataRow; ++$row) {
+            for ($col = $startCol, $colString = $startColString; $col <= $highColumn; ++$col, ++$colString) {
+                $worksheet->getCell("$colString$row"); // create cell if it doesn't exist
             }
         }
 
@@ -453,9 +442,9 @@ class ReferenceHelper
                 if ($cell->getDataType() === DataType::TYPE_FORMULA) {
                     // Formula should be adjusted
                     $worksheet->getCell($newCoordinate)
-                        ->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle()));
+                        ->setValue($this->updateFormulaReferences($cell->getValueString(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true));
                 } else {
-                    // Formula should not be adjusted
+                    // Cell value should not be adjusted
                     $worksheet->getCell($newCoordinate)->setValueExplicit($cell->getValue(), $cell->getDataType());
                 }
 
@@ -463,10 +452,10 @@ class ReferenceHelper
                 $worksheet->getCellCollection()->delete($coordinate);
             } else {
                 /*    We don't need to update styles for rows/columns before our insertion position,
-                        but we do still need to adjust any formulae    in those cells                    */
+                        but we do still need to adjust any formulae in those cells                    */
                 if ($cell->getDataType() === DataType::TYPE_FORMULA) {
                     // Formula should be adjusted
-                    $cell->setValue($this->updateFormulaReferences($cell->getValue(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle()));
+                    $cell->setValue($this->updateFormulaReferences($cell->getValueString(), $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true));
                 }
             }
         }
@@ -518,7 +507,7 @@ class ReferenceHelper
 
         // Update worksheet: freeze pane
         if ($worksheet->getFreezePane()) {
-            $splitCell = $worksheet->getFreezePane() ?? '';
+            $splitCell = $worksheet->getFreezePane();
             $topLeftCell = $worksheet->getTopLeftCell() ?? '';
 
             $splitCell = $this->updateCellReference($splitCell);
@@ -550,7 +539,7 @@ class ReferenceHelper
         }
 
         // Update workbook: define names
-        if (count($worksheet->getParent()->getDefinedNames()) > 0) {
+        if (count($worksheet->getParentOrThrow()->getDefinedNames()) > 0) {
             $this->updateDefinedNames($worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
         }
 
@@ -558,6 +547,18 @@ class ReferenceHelper
         $worksheet->garbageCollect();
     }
 
+    private static function matchSheetName(?string $match, string $worksheetName): bool
+    {
+        return $match === null || $match === '' || $match === "'\u{fffc}'" || $match === "'\u{fffb}'" || strcasecmp(trim($match, "'"), $worksheetName) === 0;
+    }
+
+    private static function sheetnameBeforeCells(string $match, string $worksheetName, string $cells): string
+    {
+        $toString = ($match > '') ? "$match!" : '';
+
+        return str_replace(["\u{fffc}", "'\u{fffb}'"], $worksheetName, $toString) . $cells;
+    }
+
     /**
      * Update references within formulas.
      *
@@ -570,16 +571,18 @@ class ReferenceHelper
      * @return string Updated formula
      */
     public function updateFormulaReferences(
-        $formula = '',
-        $beforeCellAddress = 'A1',
-        $numberOfColumns = 0,
-        $numberOfRows = 0,
-        $worksheetName = '',
-        bool $includeAbsoluteReferences = false
-    ) {
+        string $formula = '',
+        string $beforeCellAddress = 'A1',
+        int $numberOfColumns = 0,
+        int $numberOfRows = 0,
+        string $worksheetName = '',
+        bool $includeAbsoluteReferences = false,
+        bool $onlyAbsoluteReferences = false
+    ): string {
+        $callback = fn (array $matches): string => (strcasecmp(trim($matches[2], "'"), $worksheetName) === 0) ? (($matches[2][0] === "'") ? "'\u{fffc}'!" : "'\u{fffb}'!") : "'\u{fffd}'!";
         if (
-            $this->cellReferenceHelper === null ||
-            $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
+            $this->cellReferenceHelper === null
+            || $this->cellReferenceHelper->refreshRequired($beforeCellAddress, $numberOfColumns, $numberOfRows)
         ) {
             $this->cellReferenceHelper = new CellReferenceHelper($beforeCellAddress, $numberOfColumns, $numberOfRows);
         }
@@ -594,22 +597,21 @@ class ReferenceHelper
                 $adjustCount = 0;
                 $newCellTokens = $cellTokens = [];
                 //    Search for row ranges (e.g. 'Sheet1'!3:5 or 3:5) with or without $ absolutes (e.g. $3:5)
-                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+                $formulaBlockx = ' ' . (preg_replace_callback(self::SHEETNAME_PART_WITH_SLASHES, $callback, $formulaBlock) ?? $formulaBlock) . ' ';
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_ROWRANGE . '/mui', $formulaBlockx, $matches, PREG_SET_ORDER);
                 if ($matchCount > 0) {
                     foreach ($matches as $match) {
-                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
-                        $fromString .= $match[3] . ':' . $match[4];
-                        $modified3 = substr($this->updateCellReference('$A' . $match[3], $includeAbsoluteReferences), 2);
-                        $modified4 = substr($this->updateCellReference('$A' . $match[4], $includeAbsoluteReferences), 2);
+                        $fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}:{$match[4]}");
+                        $modified3 = substr($this->updateCellReference('$A' . $match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences), 2);
+                        $modified4 = substr($this->updateCellReference('$A' . $match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences), 2);
 
                         if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
-                            if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
-                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
-                                $toString .= $modified3 . ':' . $modified4;
+                            if (self::matchSheetName($match[2], $worksheetName)) {
+                                $toString = self::sheetnameBeforeCells($match[2], $worksheetName, "$modified3:$modified4");
                                 //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
                                 $column = 100000;
                                 $row = 10000000 + (int) trim($match[3], '$');
-                                $cellIndex = $column . $row;
+                                $cellIndex = "{$column}{$row}";
 
                                 $newCellTokens[$cellIndex] = preg_quote($toString, '/');
                                 $cellTokens[$cellIndex] = '/(?<!\d\$\!)' . preg_quote($fromString, '/') . '(?!\d)/i';
@@ -619,22 +621,21 @@ class ReferenceHelper
                     }
                 }
                 //    Search for column ranges (e.g. 'Sheet1'!C:E or C:E) with or without $ absolutes (e.g. $C:E)
-                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+                $formulaBlockx = ' ' . (preg_replace_callback(self::SHEETNAME_PART_WITH_SLASHES, $callback, $formulaBlock) ?? $formulaBlock) . ' ';
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_COLRANGE . '/mui', $formulaBlockx, $matches, PREG_SET_ORDER);
                 if ($matchCount > 0) {
                     foreach ($matches as $match) {
-                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
-                        $fromString .= $match[3] . ':' . $match[4];
-                        $modified3 = substr($this->updateCellReference($match[3] . '$1', $includeAbsoluteReferences), 0, -2);
-                        $modified4 = substr($this->updateCellReference($match[4] . '$1', $includeAbsoluteReferences), 0, -2);
+                        $fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}:{$match[4]}");
+                        $modified3 = substr($this->updateCellReference($match[3] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences), 0, -2);
+                        $modified4 = substr($this->updateCellReference($match[4] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences), 0, -2);
 
                         if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
-                            if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
-                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
-                                $toString .= $modified3 . ':' . $modified4;
+                            if (self::matchSheetName($match[2], $worksheetName)) {
+                                $toString = self::sheetnameBeforeCells($match[2], $worksheetName, "$modified3:$modified4");
                                 //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
                                 $column = Coordinate::columnIndexFromString(trim($match[3], '$')) + 100000;
                                 $row = 10000000;
-                                $cellIndex = $column . $row;
+                                $cellIndex = "{$column}{$row}";
 
                                 $newCellTokens[$cellIndex] = preg_quote($toString, '/');
                                 $cellTokens[$cellIndex] = '/(?<![A-Z\$\!])' . preg_quote($fromString, '/') . '(?![A-Z])/i';
@@ -644,23 +645,22 @@ class ReferenceHelper
                     }
                 }
                 //    Search for cell ranges (e.g. 'Sheet1'!A3:C5 or A3:C5) with or without $ absolutes (e.g. $A1:C$5)
-                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+                $formulaBlockx = ' ' . (preg_replace_callback(self::SHEETNAME_PART_WITH_SLASHES, $callback, "$formulaBlock") ?? "$formulaBlock") . ' ';
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLRANGE . '/mui', $formulaBlockx, $matches, PREG_SET_ORDER);
                 if ($matchCount > 0) {
                     foreach ($matches as $match) {
-                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
-                        $fromString .= $match[3] . ':' . $match[4];
-                        $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences);
-                        $modified4 = $this->updateCellReference($match[4], $includeAbsoluteReferences);
+                        $fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}:{$match[4]}");
+                        $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences);
+                        $modified4 = $this->updateCellReference($match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences);
 
                         if ($match[3] . $match[4] !== $modified3 . $modified4) {
-                            if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
-                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
-                                $toString .= $modified3 . ':' . $modified4;
+                            if (self::matchSheetName($match[2], $worksheetName)) {
+                                $toString = self::sheetnameBeforeCells($match[2], $worksheetName, "$modified3:$modified4");
                                 [$column, $row] = Coordinate::coordinateFromString($match[3]);
                                 //    Max worksheet size is 1,048,576 rows by 16,384 columns in Excel 2007, so our adjustments need to be at least one digit more
                                 $column = Coordinate::columnIndexFromString(trim($column, '$')) + 100000;
                                 $row = (int) trim($row, '$') + 10000000;
-                                $cellIndex = $column . $row;
+                                $cellIndex = "{$column}{$row}";
 
                                 $newCellTokens[$cellIndex] = preg_quote($toString, '/');
                                 $cellTokens[$cellIndex] = '/(?<![A-Z]\$\!)' . preg_quote($fromString, '/') . '(?!\d)/i';
@@ -670,18 +670,18 @@ class ReferenceHelper
                     }
                 }
                 //    Search for cell references (e.g. 'Sheet1'!A3 or C5) with or without $ absolutes (e.g. $A1 or C$5)
-                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/mui', ' ' . $formulaBlock . ' ', $matches, PREG_SET_ORDER);
+
+                $formulaBlockx = ' ' . (preg_replace_callback(self::SHEETNAME_PART_WITH_SLASHES, $callback, $formulaBlock) ?? $formulaBlock) . ' ';
+                $matchCount = preg_match_all('/' . self::REFHELPER_REGEXP_CELLREF . '/mui', $formulaBlockx, $matches, PREG_SET_ORDER);
 
                 if ($matchCount > 0) {
                     foreach ($matches as $match) {
-                        $fromString = ($match[2] > '') ? $match[2] . '!' : '';
-                        $fromString .= $match[3];
+                        $fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}");
 
-                        $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences);
+                        $modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences);
                         if ($match[3] !== $modified3) {
-                            if (($match[2] == '') || (trim($match[2], "'") == $worksheetName)) {
-                                $toString = ($match[2] > '') ? $match[2] . '!' : '';
-                                $toString .= $modified3;
+                            if (self::matchSheetName($match[2], $worksheetName)) {
+                                $toString = self::sheetnameBeforeCells($match[2], $worksheetName, "$modified3");
                                 [$column, $row] = Coordinate::coordinateFromString($match[3]);
                                 $columnAdditionalIndex = $column[0] === '$' ? 1 : 0;
                                 $rowAdditionalIndex = $row[0] === '$' ? 1 : 0;
@@ -760,11 +760,13 @@ class ReferenceHelper
             $row = $rows[$splitCount][0];
 
             if (!empty($column) && $column[0] !== '$') {
-                $column = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($column) + $numberOfColumns);
+                $column = ((Coordinate::columnIndexFromString($column) + $numberOfColumns) % AddressRange::MAX_COLUMN_INT) ?: AddressRange::MAX_COLUMN_INT;
+                $column = Coordinate::stringFromColumnIndex($column);
+                $rowOffset -= ($columnLength - strlen($column));
                 $formula = substr($formula, 0, $columnOffset) . $column . substr($formula, $columnOffset + $columnLength);
             }
             if (!empty($row) && $row[0] !== '$') {
-                $row = (int) $row + $numberOfRows;
+                $row = (((int) $row + $numberOfRows) % AddressRange::MAX_ROW) ?: AddressRange::MAX_ROW;
                 $formula = substr($formula, 0, $rowOffset) . $row . substr($formula, $rowOffset + $rowLength);
             }
         }
@@ -857,22 +859,23 @@ class ReferenceHelper
      *
      * @return string Updated cell range
      */
-    private function updateCellReference($cellReference = 'A1', bool $includeAbsoluteReferences = false)
+    private function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false): string
     {
         // Is it in another worksheet? Will not have to update anything.
-        if (strpos($cellReference, '!') !== false) {
+        if (str_contains($cellReference, '!')) {
             return $cellReference;
+        }
         // Is it a range or a single cell?
-        } elseif (!Coordinate::coordinateIsRange($cellReference)) {
+        if (!Coordinate::coordinateIsRange($cellReference)) {
             // Single cell
-            return $this->cellReferenceHelper->updateCellReference($cellReference, $includeAbsoluteReferences);
-        } elseif (Coordinate::coordinateIsRange($cellReference)) {
-            // Range
-            return $this->updateCellRange($cellReference, $includeAbsoluteReferences);
+            /** @var CellReferenceHelper */
+            $cellReferenceHelper = $this->cellReferenceHelper;
+
+            return $cellReferenceHelper->updateCellReference($cellReference, $includeAbsoluteReferences, $onlyAbsoluteReferences);
         }
 
-        // Return original
-        return $cellReference;
+        // Range
+        return $this->updateCellRange($cellReference, $includeAbsoluteReferences, $onlyAbsoluteReferences);
     }
 
     /**
@@ -882,7 +885,7 @@ class ReferenceHelper
      * @param string $oldName Old name (name to replace)
      * @param string $newName New name
      */
-    public function updateNamedFormulae(Spreadsheet $spreadsheet, $oldName = '', $newName = ''): void
+    public function updateNamedFormulae(Spreadsheet $spreadsheet, string $oldName = '', string $newName = ''): void
     {
         if ($oldName == '') {
             return;
@@ -892,8 +895,8 @@ class ReferenceHelper
             foreach ($sheet->getCoordinates(false) as $coordinate) {
                 $cell = $sheet->getCell($coordinate);
                 if ($cell->getDataType() === DataType::TYPE_FORMULA) {
-                    $formula = $cell->getValue();
-                    if (strpos($formula, $oldName) !== false) {
+                    $formula = $cell->getValueString();
+                    if (str_contains($formula, $oldName)) {
                         $formula = str_replace("'" . $oldName . "'!", "'" . $newName . "'!", $formula);
                         $formula = str_replace($oldName . '!', $newName . '!', $formula);
                         $cell->setValueExplicit($formula, DataType::TYPE_FORMULA);
@@ -905,7 +908,7 @@ class ReferenceHelper
 
     private function updateDefinedNames(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void
     {
-        foreach ($worksheet->getParent()->getDefinedNames() as $definedName) {
+        foreach ($worksheet->getParentOrThrow()->getDefinedNames() as $definedName) {
             if ($definedName->isFormula() === false) {
                 $this->updateNamedRange($definedName, $worksheet, $beforeCellAddress, $numberOfColumns, $numberOfRows);
             } else {
@@ -919,11 +922,18 @@ class ReferenceHelper
         $cellAddress = $definedName->getValue();
         $asFormula = ($cellAddress[0] === '=');
         if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) {
+            /**
+             * If we delete the entire range that is referenced by a Named Range, MS Excel sets the value to #REF!
+             * PhpSpreadsheet still only does a basic adjustment, so the Named Range will still reference Cells.
+             * Note that this applies only when deleting columns/rows; subsequent insertion won't fix the #REF!
+             * TODO Can we work out a method to identify Named Ranges that cease to be valid, so that we can replace
+             *      them with a #REF!
+             */
             if ($asFormula === true) {
-                $formula = $this->updateFormulaReferences($cellAddress, $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle());
+                $formula = $this->updateFormulaReferences($cellAddress, $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true, true);
                 $definedName->setValue($formula);
             } else {
-                $definedName->setValue($this->updateCellReference(ltrim($cellAddress, '=')));
+                $definedName->setValue($this->updateCellReference(ltrim($cellAddress, '='), true));
             }
         }
     }
@@ -931,8 +941,15 @@ class ReferenceHelper
     private function updateNamedFormula(DefinedName $definedName, Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns, int $numberOfRows): void
     {
         if ($definedName->getWorksheet() !== null && $definedName->getWorksheet()->getHashCode() === $worksheet->getHashCode()) {
+            /**
+             * If we delete the entire range that is referenced by a Named Formula, MS Excel sets the value to #REF!
+             * PhpSpreadsheet still only does a basic adjustment, so the Named Formula will still reference Cells.
+             * Note that this applies only when deleting columns/rows; subsequent insertion won't fix the #REF!
+             * TODO Can we work out a method to identify Named Ranges that cease to be valid, so that we can replace
+             *      them with a #REF!
+             */
             $formula = $definedName->getValue();
-            $formula = $this->updateFormulaReferences($formula, $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle());
+            $formula = $this->updateFormulaReferences($formula, $beforeCellAddress, $numberOfColumns, $numberOfRows, $worksheet->getTitle(), true);
             $definedName->setValue($formula);
         }
     }
@@ -944,7 +961,7 @@ class ReferenceHelper
      *
      * @return string Updated cell range
      */
-    private function updateCellRange(string $cellRange = 'A1:A1', bool $includeAbsoluteReferences = false): string
+    private function updateCellRange(string $cellRange = 'A1:A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false): string
     {
         if (!Coordinate::coordinateIsRange($cellRange)) {
             throw new Exception('Only cell ranges may be passed to this method.');
@@ -956,16 +973,18 @@ class ReferenceHelper
         for ($i = 0; $i < $ic; ++$i) {
             $jc = count($range[$i]);
             for ($j = 0; $j < $jc; ++$j) {
+                /** @var CellReferenceHelper */
+                $cellReferenceHelper = $this->cellReferenceHelper;
                 if (ctype_alpha($range[$i][$j])) {
                     $range[$i][$j] = Coordinate::coordinateFromString(
-                        $this->cellReferenceHelper->updateCellReference($range[$i][$j] . '1', $includeAbsoluteReferences)
+                        $cellReferenceHelper->updateCellReference($range[$i][$j] . '1', $includeAbsoluteReferences, $onlyAbsoluteReferences)
                     )[0];
                 } elseif (ctype_digit($range[$i][$j])) {
                     $range[$i][$j] = Coordinate::coordinateFromString(
-                        $this->cellReferenceHelper->updateCellReference('A' . $range[$i][$j], $includeAbsoluteReferences)
+                        $cellReferenceHelper->updateCellReference('A' . $range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences)
                     )[1];
                 } else {
-                    $range[$i][$j] = $this->cellReferenceHelper->updateCellReference($range[$i][$j], $includeAbsoluteReferences);
+                    $range[$i][$j] = $cellReferenceHelper->updateCellReference($range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences);
                 }
             }
         }
@@ -1179,7 +1198,9 @@ class ReferenceHelper
             if ($worksheet->cellExists($coordinate)) {
                 $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
                 for ($j = $beforeColumn; $j <= $beforeColumn - 1 + $numberOfColumns; ++$j) {
-                    $worksheet->getCell([$j, $i])->setXfIndex($xfIndex);
+                    if (!empty($xfIndex) || $worksheet->cellExists([$j, $i])) {
+                        $worksheet->getCell([$j, $i])->setXfIndex($xfIndex);
+                    }
                 }
             }
         }
@@ -1194,7 +1215,9 @@ class ReferenceHelper
             if ($worksheet->cellExists($coordinate)) {
                 $xfIndex = $worksheet->getCell($coordinate)->getXfIndex();
                 for ($j = $beforeRow; $j <= $beforeRow - 1 + $numberOfRows; ++$j) {
-                    $worksheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
+                    if (!empty($xfIndex) || $worksheet->cellExists([$j, $i])) {
+                        $worksheet->getCell(Coordinate::stringFromColumnIndex($i) . $j)->setXfIndex($xfIndex);
+                    }
                 }
             }
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php
index 39b70c8..548e68b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/ITextElement.php
@@ -2,35 +2,33 @@
 
 namespace PhpOffice\PhpSpreadsheet\RichText;
 
+use PhpOffice\PhpSpreadsheet\Style\Font;
+
 interface ITextElement
 {
     /**
      * Get text.
-     *
-     * @return string Text
      */
-    public function getText();
+    public function getText(): string;
 
     /**
      * Set text.
      *
      * @param string $text Text
      *
-     * @return ITextElement
+     * @return $this
      */
-    public function setText($text);
+    public function setText(string $text): self;
 
     /**
      * Get font.
-     *
-     * @return null|\PhpOffice\PhpSpreadsheet\Style\Font
      */
-    public function getFont();
+    public function getFont(): ?Font;
 
     /**
      * Get hash code.
      *
      * @return string Hash code
      */
-    public function getHashCode();
+    public function getHashCode(): string;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
index 88e7c79..1dc391a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
@@ -5,15 +5,16 @@ namespace PhpOffice\PhpSpreadsheet\RichText;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\IComparable;
+use Stringable;
 
-class RichText implements IComparable
+class RichText implements IComparable, Stringable
 {
     /**
      * Rich text elements.
      *
      * @var ITextElement[]
      */
-    private $richTextElements;
+    private array $richTextElements;
 
     /**
      * Create a new RichText instance.
@@ -26,8 +27,8 @@ class RichText implements IComparable
         // Rich-Text string attached to cell?
         if ($cell !== null) {
             // Add cell text and style
-            if ($cell->getValue() != '') {
-                $objRun = new Run($cell->getValue());
+            if ($cell->getValueString() !== '') {
+                $objRun = new Run($cell->getValueString());
                 $objRun->setFont(clone $cell->getWorksheet()->getStyle($cell->getCoordinate())->getFont());
                 $this->addText($objRun);
             }
@@ -44,7 +45,7 @@ class RichText implements IComparable
      *
      * @return $this
      */
-    public function addText(ITextElement $text)
+    public function addText(ITextElement $text): static
     {
         $this->richTextElements[] = $text;
 
@@ -55,10 +56,8 @@ class RichText implements IComparable
      * Create text.
      *
      * @param string $text Text
-     *
-     * @return TextElement
      */
-    public function createText($text)
+    public function createText(string $text): TextElement
     {
         $objText = new TextElement($text);
         $this->addText($objText);
@@ -70,10 +69,8 @@ class RichText implements IComparable
      * Create text run.
      *
      * @param string $text Text
-     *
-     * @return Run
      */
-    public function createTextRun($text)
+    public function createTextRun(string $text): Run
     {
         $objText = new Run($text);
         $this->addText($objText);
@@ -83,10 +80,8 @@ class RichText implements IComparable
 
     /**
      * Get plain text.
-     *
-     * @return string
      */
-    public function getPlainText()
+    public function getPlainText(): string
     {
         // Return value
         $returnValue = '';
@@ -101,10 +96,8 @@ class RichText implements IComparable
 
     /**
      * Convert to string.
-     *
-     * @return string
      */
-    public function __toString()
+    public function __toString(): string
     {
         return $this->getPlainText();
     }
@@ -114,7 +107,7 @@ class RichText implements IComparable
      *
      * @return ITextElement[]
      */
-    public function getRichTextElements()
+    public function getRichTextElements(): array
     {
         return $this->richTextElements;
     }
@@ -126,7 +119,7 @@ class RichText implements IComparable
      *
      * @return $this
      */
-    public function setRichTextElements(array $textElements)
+    public function setRichTextElements(array $textElements): static
     {
         $this->richTextElements = $textElements;
 
@@ -138,7 +131,7 @@ class RichText implements IComparable
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         $hashElements = '';
         foreach ($this->richTextElements as $element) {
@@ -146,8 +139,8 @@ class RichText implements IComparable
         }
 
         return md5(
-            $hashElements .
-            __CLASS__
+            $hashElements
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
index 9c9f807..6a6ccdd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
@@ -2,23 +2,22 @@
 
 namespace PhpOffice\PhpSpreadsheet\RichText;
 
+use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
 use PhpOffice\PhpSpreadsheet\Style\Font;
 
 class Run extends TextElement implements ITextElement
 {
     /**
      * Font.
-     *
-     * @var Font
      */
-    private $font;
+    private ?Font $font;
 
     /**
      * Create a new Run instance.
      *
      * @param string $text Text
      */
-    public function __construct($text = '')
+    public function __construct(string $text = '')
     {
         parent::__construct($text);
         // Initialise variables
@@ -27,22 +26,29 @@ class Run extends TextElement implements ITextElement
 
     /**
      * Get font.
-     *
-     * @return null|\PhpOffice\PhpSpreadsheet\Style\Font
      */
-    public function getFont()
+    public function getFont(): ?Font
     {
         return $this->font;
     }
 
+    public function getFontOrThrow(): Font
+    {
+        if ($this->font === null) {
+            throw new SpreadsheetException('unexpected null font');
+        }
+
+        return $this->font;
+    }
+
     /**
      * Set font.
      *
-     * @param Font $font Font
+     * @param ?Font $font Font
      *
      * @return $this
      */
-    public function setFont(?Font $font = null)
+    public function setFont(?Font $font = null): static
     {
         $this->font = $font;
 
@@ -54,12 +60,12 @@ class Run extends TextElement implements ITextElement
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->getText() .
-            $this->font->getHashCode() .
-            __CLASS__
+            $this->getText()
+            . (($this->font === null) ? '' : $this->font->getHashCode())
+            . __CLASS__
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
index 2373343..e509d27 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
@@ -2,21 +2,21 @@
 
 namespace PhpOffice\PhpSpreadsheet\RichText;
 
+use PhpOffice\PhpSpreadsheet\Style\Font;
+
 class TextElement implements ITextElement
 {
     /**
      * Text.
-     *
-     * @var string
      */
-    private $text;
+    private string $text;
 
     /**
      * Create a new TextElement instance.
      *
      * @param string $text Text
      */
-    public function __construct($text = '')
+    public function __construct(string $text = '')
     {
         // Initialise variables
         $this->text = $text;
@@ -27,7 +27,7 @@ class TextElement implements ITextElement
      *
      * @return string Text
      */
-    public function getText()
+    public function getText(): string
     {
         return $this->text;
     }
@@ -39,7 +39,7 @@ class TextElement implements ITextElement
      *
      * @return $this
      */
-    public function setText($text)
+    public function setText(string $text): self
     {
         $this->text = $text;
 
@@ -47,11 +47,9 @@ class TextElement implements ITextElement
     }
 
     /**
-     * Get font.
-     *
-     * @return null|\PhpOffice\PhpSpreadsheet\Style\Font
+     * Get font. For this class, the return value is always null.
      */
-    public function getFont()
+    public function getFont(): ?Font
     {
         return null;
     }
@@ -61,11 +59,11 @@ class TextElement implements ITextElement
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->text .
-            __CLASS__
+            $this->text
+            . __CLASS__
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
index 0baf446..7f727a8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
@@ -16,35 +16,26 @@ class Settings
      * Class name of the chart renderer used for rendering charts
      * eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph.
      *
-     * @var ?string
+     * @var null|class-string<IRenderer>
      */
-    private static $chartRenderer;
+    private static ?string $chartRenderer = null;
 
     /**
      * Default options for libxml loader.
-     *
-     * @var ?int
      */
-    private static $libXmlLoaderOptions;
+    private static ?int $libXmlLoaderOptions = null;
 
     /**
      * The cache implementation to be used for cell collection.
-     *
-     * @var ?CacheInterface
      */
-    private static $cache;
+    private static ?CacheInterface $cache = null;
 
     /**
      * The HTTP client implementation to be used for network request.
-     *
-     * @var null|ClientInterface
      */
-    private static $httpClient;
+    private static ?ClientInterface $httpClient = null;
 
-    /**
-     * @var null|RequestFactoryInterface
-     */
-    private static $requestFactory;
+    private static ?RequestFactoryInterface $requestFactory = null;
 
     /**
      * Set the locale code to use for formula translations and any special formatting.
@@ -53,7 +44,7 @@ class Settings
      *
      * @return bool Success or failure
      */
-    public static function setLocale(string $locale)
+    public static function setLocale(string $locale): bool
     {
         return Calculation::getInstance()->setLocale($locale);
     }
@@ -66,7 +57,7 @@ class Settings
     /**
      * Identify to PhpSpreadsheet the external library to use for rendering charts.
      *
-     * @param string $rendererClassName Class name of the chart renderer
+     * @param class-string<IRenderer> $rendererClassName Class name of the chart renderer
      *    eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
      */
     public static function setChartRenderer(string $rendererClassName): void
@@ -78,10 +69,15 @@ class Settings
         self::$chartRenderer = $rendererClassName;
     }
 
+    public static function unsetChartRenderer(): void
+    {
+        self::$chartRenderer = null;
+    }
+
     /**
      * Return the Chart Rendering Library that PhpSpreadsheet is currently configured to use.
      *
-     * @return null|string Class name of the chart renderer
+     * @return null|class-string<IRenderer> Class name of the chart renderer
      *    eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
      */
     public static function getChartRenderer(): ?string
@@ -91,7 +87,7 @@ class Settings
 
     public static function htmlEntityFlags(): int
     {
-        return \ENT_COMPAT;
+        return ENT_COMPAT;
     }
 
     /**
@@ -99,7 +95,7 @@ class Settings
      *
      * @param ?int $options Default options for libxml loader
      */
-    public static function setLibXmlLoaderOptions($options): int
+    public static function setLibXmlLoaderOptions(?int $options): int
     {
         if ($options === null) {
             $options = defined('LIBXML_DTDLOAD') ? (LIBXML_DTDLOAD | LIBXML_DTDATTR) : 0;
@@ -124,38 +120,10 @@ class Settings
         return self::$libXmlLoaderOptions;
     }
 
-    /**
-     * Deprecated, has no effect.
-     *
-     * @param bool $state
-     *
-     * @deprecated will be removed without replacement as it is no longer necessary on PHP 7.3.0+
-     *
-     * @codeCoverageIgnore
-     */
-    public static function setLibXmlDisableEntityLoader(/** @scrutinizer ignore-unused */ $state): void
-    {
-        // noop
-    }
-
-    /**
-     * Deprecated, has no effect.
-     *
-     * @return bool $state
-     *
-     * @deprecated will be removed without replacement as it is no longer necessary on PHP 7.3.0+
-     *
-     * @codeCoverageIgnore
-     */
-    public static function getLibXmlDisableEntityLoader(): bool
-    {
-        return true;
-    }
-
     /**
      * Sets the implementation of cache that should be used for cell collection.
      */
-    public static function setCache(CacheInterface $cache): void
+    public static function setCache(?CacheInterface $cache): void
     {
         self::$cache = $cache;
     }
@@ -174,9 +142,7 @@ class Settings
 
     public static function useSimpleCacheVersion3(): bool
     {
-        return
-            PHP_MAJOR_VERSION === 8 &&
-            (new ReflectionClass(CacheInterface::class))->getMethod('get')->getReturnType() !== null;
+        return (new ReflectionClass(CacheInterface::class))->getMethod('get')->getReturnType() !== null;
     }
 
     /**
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php
index 8718a61..307f8d9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php
@@ -8,8 +8,7 @@ class CodePage
 {
     public const DEFAULT_CODE_PAGE = 'CP1252';
 
-    /** @var array */
-    private static $pageArray = [
+    private static array $pageArray = [
         0 => 'CP1252', //    CodePage is not always correctly set when the xls file was saved by Apple's Numbers program
         367 => 'ASCII', //    ASCII
         437 => 'CP437', //    OEM US
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
index 0ae5f1e..b8feeb9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
@@ -10,7 +10,6 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Exception;
 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
-use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
 class Date
@@ -25,7 +24,7 @@ class Date
      *
      * @var string[]
      */
-    public static $monthNames = [
+    public static array $monthNames = [
         'Jan' => 'January',
         'Feb' => 'February',
         'Mar' => 'March',
@@ -43,7 +42,7 @@ class Date
     /**
      * @var string[]
      */
-    public static $numberSuffixes = [
+    public static array $numberSuffixes = [
         'st',
         'nd',
         'rd',
@@ -53,30 +52,26 @@ class Date
     /**
      * Base calendar year to use for calculations
      * Value is either CALENDAR_WINDOWS_1900 (1900) or CALENDAR_MAC_1904 (1904).
-     *
-     * @var int
      */
-    protected static $excelCalendar = self::CALENDAR_WINDOWS_1900;
+    protected static int $excelCalendar = self::CALENDAR_WINDOWS_1900;
 
     /**
      * Default timezone to use for DateTime objects.
-     *
-     * @var null|DateTimeZone
      */
-    protected static $defaultTimeZone;
+    protected static ?DateTimeZone $defaultTimeZone = null;
 
     /**
      * Set the Excel calendar (Windows 1900 or Mac 1904).
      *
-     * @param int $baseYear Excel base date (1900 or 1904)
+     * @param ?int $baseYear Excel base date (1900 or 1904)
      *
      * @return bool Success or failure
      */
-    public static function setExcelCalendar($baseYear)
+    public static function setExcelCalendar(?int $baseYear): bool
     {
         if (
-            ($baseYear == self::CALENDAR_WINDOWS_1900) ||
-            ($baseYear == self::CALENDAR_MAC_1904)
+            ($baseYear === self::CALENDAR_WINDOWS_1900)
+            || ($baseYear === self::CALENDAR_MAC_1904)
         ) {
             self::$excelCalendar = $baseYear;
 
@@ -91,7 +86,7 @@ class Date
      *
      * @return int Excel base date (1900 or 1904)
      */
-    public static function getExcelCalendar()
+    public static function getExcelCalendar(): int
     {
         return self::$excelCalendar;
     }
@@ -103,13 +98,13 @@ class Date
      *
      * @return bool Success or failure
      */
-    public static function setDefaultTimezone($timeZone)
+    public static function setDefaultTimezone($timeZone): bool
     {
         try {
             $timeZone = self::validateTimeZone($timeZone);
             self::$defaultTimeZone = $timeZone;
             $retval = true;
-        } catch (PhpSpreadsheetException $e) {
+        } catch (PhpSpreadsheetException) {
             $retval = false;
         }
 
@@ -147,7 +142,7 @@ class Date
      *
      * @return ?DateTimeZone The timezone as a timezone object
      */
-    private static function validateTimeZone($timeZone)
+    private static function validateTimeZone($timeZone): ?DateTimeZone
     {
         if ($timeZone instanceof DateTimeZone || $timeZone === null) {
             return $timeZone;
@@ -160,11 +155,11 @@ class Date
     }
 
     /**
-     * @param mixed $value
-     *
-     * @return float|int
+     * @param mixed $value Converts a date/time in ISO-8601 standard format date string to an Excel
+     *                         serialized timestamp.
+     *                     See https://en.wikipedia.org/wiki/ISO_8601 for details of the ISO-8601 standard format.
      */
-    public static function convertIsoDate($value)
+    public static function convertIsoDate(mixed $value): float|int
     {
         if (!is_string($value)) {
             throw new Exception('Non-string value supplied for Iso Date conversion');
@@ -177,12 +172,12 @@ class Date
             throw new Exception("Invalid string $value supplied for datatype Date");
         }
 
-        $newValue = SharedDate::PHPToExcel($date);
+        $newValue = self::PHPToExcel($date);
         if ($newValue === false) {
             throw new Exception("Invalid string $value supplied for datatype Date");
         }
 
-        if (preg_match('/^\\d\\d:\\d\\d:\\d\\d/', $value) == 1) {
+        if (preg_match('/^\\s*\\d?\\d:\\d\\d(:\\d\\d([.]\\d+)?)?\\s*(am|pm)?\\s*$/i', $value) == 1) {
             $newValue = fmod($newValue, 1.0);
         }
 
@@ -194,12 +189,12 @@ class Date
      *
      * @param float|int $excelTimestamp MS Excel serialized date/time value
      * @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
-     *                                                                        if you don't want to treat it as a UTC value
-     *                                                                    Use the default (UTC) unless you absolutely need a conversion
+     *                                           if you don't want to treat it as a UTC value
+     *                                           Use the default (UTC) unless you absolutely need a conversion
      *
      * @return DateTime PHP date/time object
      */
-    public static function excelToDateTimeObject($excelTimestamp, $timeZone = null)
+    public static function excelToDateTimeObject(float|int $excelTimestamp, null|DateTimeZone|string $timeZone = null): DateTime
     {
         $timeZone = ($timeZone === null) ? self::getDefaultTimezone() : self::validateTimeZone($timeZone);
         if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
@@ -221,11 +216,13 @@ class Date
 
         $days = floor($excelTimestamp);
         $partDay = $excelTimestamp - $days;
-        $hours = floor($partDay * 24);
-        $partDay = $partDay * 24 - $hours;
-        $minutes = floor($partDay * 60);
-        $partDay = $partDay * 60 - $minutes;
-        $seconds = round($partDay * 60);
+        $hms = 86400 * $partDay;
+        $microseconds = (int) round(fmod($hms, 1) * 1000000);
+        $hms = (int) floor($hms);
+        $hours = intdiv($hms, 3600);
+        $hms -= $hours * 3600;
+        $minutes = intdiv($hms, 60);
+        $seconds = $hms % 60;
 
         if ($days >= 0) {
             $days = '+' . $days;
@@ -233,7 +230,7 @@ class Date
         $interval = $days . ' days';
 
         return $baseDate->modify($interval)
-            ->setTime((int) $hours, (int) $minutes, (int) $seconds);
+            ->setTime($hours, $minutes, $seconds, $microseconds);
     }
 
     /**
@@ -243,15 +240,17 @@ class Date
      *
      * @param float|int $excelTimestamp MS Excel serialized date/time value
      * @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
-     *                                                                        if you don't want to treat it as a UTC value
-     *                                                                    Use the default (UTC) unless you absolutely need a conversion
+     *                                               if you don't want to treat it as a UTC value
+     *                                               Use the default (UTC) unless you absolutely need a conversion
      *
      * @return int Unix timetamp for this date/time
      */
-    public static function excelToTimestamp($excelTimestamp, $timeZone = null)
+    public static function excelToTimestamp($excelTimestamp, $timeZone = null): int
     {
-        return (int) self::excelToDateTimeObject($excelTimestamp, $timeZone)
-            ->format('U');
+        $dto = self::excelToDateTimeObject($excelTimestamp, $timeZone);
+        self::roundMicroseconds($dto);
+
+        return (int) $dto->format('U');
     }
 
     /**
@@ -263,7 +262,7 @@ class Date
      * @return false|float Excel date/time value
      *                                  or boolean FALSE on failure
      */
-    public static function PHPToExcel($dateValue)
+    public static function PHPToExcel(mixed $dateValue)
     {
         if ((is_object($dateValue)) && ($dateValue instanceof DateTimeInterface)) {
             return self::dateTimeToExcel($dateValue);
@@ -283,15 +282,17 @@ class Date
      *
      * @return float MS Excel serialized date/time value
      */
-    public static function dateTimeToExcel(DateTimeInterface $dateValue)
+    public static function dateTimeToExcel(DateTimeInterface $dateValue): float
     {
+        $seconds = (float) sprintf('%d.%06d', $dateValue->format('s'), $dateValue->format('u'));
+
         return self::formattedPHPToExcel(
             (int) $dateValue->format('Y'),
             (int) $dateValue->format('m'),
             (int) $dateValue->format('d'),
             (int) $dateValue->format('H'),
             (int) $dateValue->format('i'),
-            (int) $dateValue->format('s')
+            $seconds
         );
     }
 
@@ -304,7 +305,7 @@ class Date
      *
      * @return false|float MS Excel serialized date/time value
      */
-    public static function timestampToExcel($unixTimestamp)
+    public static function timestampToExcel($unixTimestamp): bool|float
     {
         if (!is_numeric($unixTimestamp)) {
             return false;
@@ -316,16 +317,9 @@ class Date
     /**
      * formattedPHPToExcel.
      *
-     * @param int $year
-     * @param int $month
-     * @param int $day
-     * @param int $hours
-     * @param int $minutes
-     * @param int $seconds
-     *
      * @return float Excel date/time value
      */
-    public static function formattedPHPToExcel($year, $month, $day, $hours = 0, $minutes = 0, $seconds = 0)
+    public static function formattedPHPToExcel(int $year, int $month, int $day, int $hours = 0, int $minutes = 0, float|int $seconds = 0): float
     {
         if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
             //
@@ -362,12 +356,8 @@ class Date
 
     /**
      * Is a given cell a date/time?
-     *
-     * @param mixed $value
-     *
-     * @return bool
      */
-    public static function isDateTime(Cell $cell, $value = null, bool $dateWithoutTimeOkay = true)
+    public static function isDateTime(Cell $cell, mixed $value = null, bool $dateWithoutTimeOkay = true): bool
     {
         $result = false;
         $worksheet = $cell->getWorksheetOrNull();
@@ -375,13 +365,18 @@ class Date
         if ($worksheet !== null && $spreadsheet !== null) {
             $index = $spreadsheet->getActiveSheetIndex();
             $selected = $worksheet->getSelectedCells();
-            $result = is_numeric($value ?? $cell->getCalculatedValue()) &&
-                self::isDateTimeFormat(
-                    $worksheet->getStyle(
-                        $cell->getCoordinate()
-                    )->getNumberFormat(),
-                    $dateWithoutTimeOkay
-                );
+
+            try {
+                $result = is_numeric($value ?? $cell->getCalculatedValue())
+                    && self::isDateTimeFormat(
+                        $worksheet->getStyle(
+                            $cell->getCoordinate()
+                        )->getNumberFormat(),
+                        $dateWithoutTimeOkay
+                    );
+            } catch (Exception) {
+                // Result is already false, so no need to actually do anything here
+            }
             $worksheet->setSelectedCells($selected);
             $spreadsheet->setActiveSheetIndex($index);
         }
@@ -390,11 +385,9 @@ class Date
     }
 
     /**
-     * Is a given number format a date/time?
-     *
-     * @return bool
+     * Is a given NumberFormat code a date/time format code?
      */
-    public static function isDateTimeFormat(NumberFormat $excelFormatCode, bool $dateWithoutTimeOkay = true)
+    public static function isDateTimeFormat(NumberFormat $excelFormatCode, bool $dateWithoutTimeOkay = true): bool
     {
         return self::isDateTimeFormatCode((string) $excelFormatCode->getFormatCode(), $dateWithoutTimeOkay);
     }
@@ -404,12 +397,8 @@ class Date
 
     /**
      * Is a given number format code a date/time?
-     *
-     * @param string $excelFormatCode
-     *
-     * @return bool
      */
-    public static function isDateTimeFormatCode($excelFormatCode, bool $dateWithoutTimeOkay = true)
+    public static function isDateTimeFormatCode(string $excelFormatCode, bool $dateWithoutTimeOkay = true): bool
     {
         if (strtolower($excelFormatCode) === strtolower(NumberFormat::FORMAT_GENERAL)) {
             //    "General" contains an epoch letter 'e', so we trap for it explicitly here (case-insensitive check)
@@ -421,17 +410,18 @@ class Date
         }
 
         // Switch on formatcode
+        $excelFormatCode = (string) NumberFormat::convertSystemFormats($excelFormatCode);
         if (in_array($excelFormatCode, NumberFormat::DATE_TIME_OR_DATETIME_ARRAY, true)) {
             return $dateWithoutTimeOkay || in_array($excelFormatCode, NumberFormat::TIME_OR_DATETIME_ARRAY);
         }
 
         //    Typically number, currency or accounting (or occasionally fraction) formats
-        if ((substr($excelFormatCode, 0, 1) == '_') || (substr($excelFormatCode, 0, 2) == '0 ')) {
+        if ((str_starts_with($excelFormatCode, '_')) || (str_starts_with($excelFormatCode, '0 '))) {
             return false;
         }
         // Some "special formats" provided in German Excel versions were detected as date time value,
         // so filter them out here - "\C\H\-00000" (Switzerland) and "\D-00000" (Germany).
-        if (\strpos($excelFormatCode, '-00000') !== false) {
+        if (str_contains($excelFormatCode, '-00000')) {
             return false;
         }
         $possibleFormatCharacters = $dateWithoutTimeOkay ? self::POSSIBLE_DATETIME_FORMAT_CHARACTERS : self::POSSIBLE_TIME_FORMAT_CHARACTERS;
@@ -439,14 +429,14 @@ class Date
         if (preg_match('/(^|\])[^\[]*[' . $possibleFormatCharacters . ']/i', $excelFormatCode)) {
             //    We might also have a format mask containing quoted strings...
             //        we don't want to test for any of our characters within the quoted blocks
-            if (strpos($excelFormatCode, '"') !== false) {
+            if (str_contains($excelFormatCode, '"')) {
                 $segMatcher = false;
                 foreach (explode('"', $excelFormatCode) as $subVal) {
                     //    Only test in alternate array entries (the non-quoted blocks)
                     $segMatcher = $segMatcher === false;
                     if (
-                        $segMatcher &&
-                        (preg_match('/(^|\])[^\[]*[' . $possibleFormatCharacters . ']/i', $subVal))
+                        $segMatcher
+                        && (preg_match('/(^|\])[^\[]*[' . $possibleFormatCharacters . ']/i', $subVal))
                     ) {
                         return true;
                     }
@@ -469,7 +459,7 @@ class Date
      *
      * @return false|float Excel date/time serial value
      */
-    public static function stringToExcel($dateValue)
+    public static function stringToExcel(string $dateValue): bool|float
     {
         if (strlen($dateValue) < 2) {
             return false;
@@ -484,7 +474,7 @@ class Date
             return false;
         }
 
-        if (strpos($dateValue, ':') !== false) {
+        if (str_contains($dateValue, ':')) {
             $timeValue = DateTimeExcel\TimeValue::fromString($dateValue);
             if (!is_float($timeValue)) {
                 return false;
@@ -502,7 +492,7 @@ class Date
      *
      * @return int|string Month number (1 - 12), or the original string argument if it isn't a valid month name
      */
-    public static function monthStringToNumber($monthName)
+    public static function monthStringToNumber(string $monthName)
     {
         $monthIndex = 1;
         foreach (self::$monthNames as $shortMonthName => $longMonthName) {
@@ -522,7 +512,7 @@ class Date
      *
      * @return int|string The integer value with any ordinal stripped, or the original string argument if it isn't a valid numeric
      */
-    public static function dayStringToNumber($day)
+    public static function dayStringToNumber(string $day)
     {
         $strippedDayValue = (str_replace(self::$numberSuffixes, '', $day));
         if (is_numeric($strippedDayValue)) {
@@ -546,4 +536,12 @@ class Date
 
         return $dtobj->format($format);
     }
+
+    public static function roundMicroseconds(DateTime $dti): void
+    {
+        $microseconds = (int) $dti->format('u');
+        if ($microseconds >= 500000) {
+            $dti->modify('+1 second');
+        }
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php
index f69310f..4eef791 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php
@@ -2,8 +2,6 @@
 
 namespace PhpOffice\PhpSpreadsheet\Shared;
 
-use GdImage;
-use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
 use SimpleXMLElement;
 
 class Drawing
@@ -13,9 +11,9 @@ class Drawing
      *
      * @param int $pixelValue Value in pixels
      *
-     * @return int Value in EMU
+     * @return float|int Value in EMU
      */
-    public static function pixelsToEMU($pixelValue)
+    public static function pixelsToEMU(int $pixelValue): int|float
     {
         return $pixelValue * 9525;
     }
@@ -27,7 +25,7 @@ class Drawing
      *
      * @return int Value in pixels
      */
-    public static function EMUToPixels($emuValue)
+    public static function EMUToPixels($emuValue): int
     {
         $emuValue = (int) $emuValue;
         if ($emuValue != 0) {
@@ -46,22 +44,22 @@ class Drawing
      *
      * @return float|int Value in cell dimension
      */
-    public static function pixelsToCellDimension($pixelValue, \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont)
+    public static function pixelsToCellDimension(int $pixelValue, \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont): int|float
     {
         // Font name and size
         $name = $defaultFont->getName();
         $size = $defaultFont->getSize();
 
-        if (isset(Font::$defaultColumnWidths[$name][$size])) {
+        if (isset(Font::DEFAULT_COLUMN_WIDTHS[$name][$size])) {
             // Exact width can be determined
-            return $pixelValue * Font::$defaultColumnWidths[$name][$size]['width']
-                / Font::$defaultColumnWidths[$name][$size]['px'];
+            return $pixelValue * Font::DEFAULT_COLUMN_WIDTHS[$name][$size]['width']
+                / Font::DEFAULT_COLUMN_WIDTHS[$name][$size]['px'];
         }
 
         // We don't have data for this particular font and size, use approximation by
         // extrapolating from Calibri 11
-        return $pixelValue * 11 * Font::$defaultColumnWidths['Calibri'][11]['width']
-            / Font::$defaultColumnWidths['Calibri'][11]['px'] / $size;
+        return $pixelValue * 11 * Font::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['width']
+            / Font::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['px'] / $size;
     }
 
     /**
@@ -72,21 +70,21 @@ class Drawing
      *
      * @return int Value in pixels
      */
-    public static function cellDimensionToPixels($cellWidth, \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont)
+    public static function cellDimensionToPixels(float $cellWidth, \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont): int
     {
         // Font name and size
         $name = $defaultFont->getName();
         $size = $defaultFont->getSize();
 
-        if (isset(Font::$defaultColumnWidths[$name][$size])) {
+        if (isset(Font::DEFAULT_COLUMN_WIDTHS[$name][$size])) {
             // Exact width can be determined
-            $colWidth = $cellWidth * Font::$defaultColumnWidths[$name][$size]['px']
-                / Font::$defaultColumnWidths[$name][$size]['width'];
+            $colWidth = $cellWidth * Font::DEFAULT_COLUMN_WIDTHS[$name][$size]['px']
+                / Font::DEFAULT_COLUMN_WIDTHS[$name][$size]['width'];
         } else {
             // We don't have data for this particular font and size, use approximation by
             // extrapolating from Calibri 11
-            $colWidth = $cellWidth * $size * Font::$defaultColumnWidths['Calibri'][11]['px']
-                / Font::$defaultColumnWidths['Calibri'][11]['width'] / 11;
+            $colWidth = $cellWidth * $size * Font::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['px']
+                / Font::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['width'] / 11;
         }
 
         // Round pixels to closest integer
@@ -102,7 +100,7 @@ class Drawing
      *
      * @return float Value in points
      */
-    public static function pixelsToPoints($pixelValue)
+    public static function pixelsToPoints(int $pixelValue): float
     {
         return $pixelValue * 0.75;
     }
@@ -110,11 +108,11 @@ class Drawing
     /**
      * Convert points to pixels.
      *
-     * @param int $pointValue Value in points
+     * @param float|int $pointValue Value in points
      *
      * @return int Value in pixels
      */
-    public static function pointsToPixels($pointValue)
+    public static function pointsToPixels($pointValue): int
     {
         if ($pointValue != 0) {
             return (int) ceil($pointValue / 0.75);
@@ -130,7 +128,7 @@ class Drawing
      *
      * @return int Angle
      */
-    public static function degreesToAngle($degrees)
+    public static function degreesToAngle(int $degrees): int
     {
         return (int) round($degrees * 60000);
     }
@@ -142,7 +140,7 @@ class Drawing
      *
      * @return int Degrees
      */
-    public static function angleToDegrees($angle)
+    public static function angleToDegrees($angle): int
     {
         $angle = (int) $angle;
         if ($angle != 0) {
@@ -151,27 +149,4 @@ class Drawing
 
         return 0;
     }
-
-    /**
-     * Create a new image from file. By alexander at alexauto dot nl.
-     *
-     * @see http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
-     *
-     * @param string $bmpFilename Path to Windows DIB (BMP) image
-     *
-     * @return GdImage|resource
-     *
-     * @deprecated 1.26 use Php function imagecreatefrombmp instead
-     *
-     * @codeCoverageIgnore
-     */
-    public static function imagecreatefrombmp($bmpFilename)
-    {
-        $retVal = @imagecreatefrombmp($bmpFilename);
-        if ($retVal === false) {
-            throw new ReaderException("Unable to create image from $bmpFilename");
-        }
-
-        return $retVal;
-    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php
index c6d0a6f..9eb9956 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php
@@ -6,58 +6,42 @@ class Escher
 {
     /**
      * Drawing Group Container.
-     *
-     * @var Escher\DggContainer
      */
-    private $dggContainer;
+    private ?Escher\DggContainer $dggContainer = null;
 
     /**
      * Drawing Container.
-     *
-     * @var Escher\DgContainer
      */
-    private $dgContainer;
+    private ?Escher\DgContainer $dgContainer = null;
 
     /**
      * Get Drawing Group Container.
-     *
-     * @return Escher\DggContainer
      */
-    public function getDggContainer()
+    public function getDggContainer(): ?Escher\DggContainer
     {
         return $this->dggContainer;
     }
 
     /**
      * Set Drawing Group Container.
-     *
-     * @param Escher\DggContainer $dggContainer
-     *
-     * @return Escher\DggContainer
      */
-    public function setDggContainer($dggContainer)
+    public function setDggContainer(Escher\DggContainer $dggContainer): Escher\DggContainer
     {
         return $this->dggContainer = $dggContainer;
     }
 
     /**
      * Get Drawing Container.
-     *
-     * @return Escher\DgContainer
      */
-    public function getDgContainer()
+    public function getDgContainer(): ?Escher\DgContainer
     {
         return $this->dgContainer;
     }
 
     /**
      * Set Drawing Container.
-     *
-     * @param Escher\DgContainer $dgContainer
-     *
-     * @return Escher\DgContainer
      */
-    public function setDgContainer($dgContainer)
+    public function setDgContainer(Escher\DgContainer $dgContainer): Escher\DgContainer
     {
         return $this->dgContainer = $dgContainer;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php
index b0d75d7..0f4b7a8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php
@@ -2,50 +2,58 @@
 
 namespace PhpOffice\PhpSpreadsheet\Shared\Escher;
 
+use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
+use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
+
 class DgContainer
 {
     /**
      * Drawing index, 1-based.
-     *
-     * @var int
      */
-    private $dgId;
+    private ?int $dgId = null;
 
     /**
      * Last shape index in this drawing.
-     *
-     * @var int
      */
-    private $lastSpId;
+    private ?int $lastSpId = null;
 
-    private $spgrContainer;
+    private ?SpgrContainer $spgrContainer = null;
 
-    public function getDgId()
+    public function getDgId(): ?int
     {
         return $this->dgId;
     }
 
-    public function setDgId($value): void
+    public function setDgId(int $value): void
     {
         $this->dgId = $value;
     }
 
-    public function getLastSpId()
+    public function getLastSpId(): ?int
     {
         return $this->lastSpId;
     }
 
-    public function setLastSpId($value): void
+    public function setLastSpId(int $value): void
     {
         $this->lastSpId = $value;
     }
 
-    public function getSpgrContainer()
+    public function getSpgrContainer(): ?SpgrContainer
     {
         return $this->spgrContainer;
     }
 
-    public function setSpgrContainer($spgrContainer)
+    public function getSpgrContainerOrThrow(): SpgrContainer
+    {
+        if ($this->spgrContainer !== null) {
+            return $this->spgrContainer;
+        }
+
+        throw new SpreadsheetException('spgrContainer is unexpectedly null');
+    }
+
+    public function setSpgrContainer(SpgrContainer $spgrContainer): SpgrContainer
     {
         return $this->spgrContainer = $spgrContainer;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
index 6bdc8f7..84363ab 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
@@ -6,17 +6,13 @@ class SpgrContainer
 {
     /**
      * Parent Shape Group Container.
-     *
-     * @var null|SpgrContainer
      */
-    private $parent;
+    private ?self $parent = null;
 
     /**
      * Shape Container collection.
-     *
-     * @var array
      */
-    private $children = [];
+    private array $children = [];
 
     /**
      * Set parent Shape Group Container.
@@ -37,9 +33,9 @@ class SpgrContainer
     /**
      * Add a child. This will be either spgrContainer or spContainer.
      *
-     * @param mixed $child
+     * @param SpgrContainer|SpgrContainer\SpContainer $child child to be added
      */
-    public function addChild($child): void
+    public function addChild(mixed $child): void
     {
         $this->children[] = $child;
         $child->setParent($this);
@@ -48,7 +44,7 @@ class SpgrContainer
     /**
      * Get collection of Shape Containers.
      */
-    public function getChildren()
+    public function getChildren(): array
     {
         return $this->children;
     }
@@ -58,7 +54,7 @@ class SpgrContainer
      *
      * @return SpgrContainer\SpContainer[]
      */
-    public function getAllSpContainers()
+    public function getAllSpContainers(): array
     {
         $allSpContainers = [];
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
index 8a81ff5..c462d45 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
@@ -8,184 +8,140 @@ class SpContainer
 {
     /**
      * Parent Shape Group Container.
-     *
-     * @var SpgrContainer
      */
-    private $parent;
+    private SpgrContainer $parent;
 
     /**
      * Is this a group shape?
-     *
-     * @var bool
      */
-    private $spgr = false;
+    private bool $spgr = false;
 
     /**
      * Shape type.
-     *
-     * @var int
      */
-    private $spType;
+    private int $spType;
 
     /**
      * Shape flag.
-     *
-     * @var int
      */
-    private $spFlag;
+    private int $spFlag;
 
     /**
      * Shape index (usually group shape has index 0, and the rest: 1,2,3...).
-     *
-     * @var int
      */
-    private $spId;
+    private int $spId;
 
     /**
      * Array of options.
-     *
-     * @var array
      */
-    private $OPT;
+    private array $OPT = [];
 
     /**
      * Cell coordinates of upper-left corner of shape, e.g. 'A1'.
-     *
-     * @var string
      */
-    private $startCoordinates;
+    private string $startCoordinates = '';
 
     /**
      * Horizontal offset of upper-left corner of shape measured in 1/1024 of column width.
-     *
-     * @var int
      */
-    private $startOffsetX;
+    private int|float $startOffsetX;
 
     /**
      * Vertical offset of upper-left corner of shape measured in 1/256 of row height.
-     *
-     * @var int
      */
-    private $startOffsetY;
+    private int|float $startOffsetY;
 
     /**
      * Cell coordinates of bottom-right corner of shape, e.g. 'B2'.
-     *
-     * @var string
      */
-    private $endCoordinates;
+    private string $endCoordinates;
 
     /**
      * Horizontal offset of bottom-right corner of shape measured in 1/1024 of column width.
-     *
-     * @var int
      */
-    private $endOffsetX;
+    private int|float $endOffsetX;
 
     /**
      * Vertical offset of bottom-right corner of shape measured in 1/256 of row height.
-     *
-     * @var int
      */
-    private $endOffsetY;
+    private int|float $endOffsetY;
 
     /**
      * Set parent Shape Group Container.
-     *
-     * @param SpgrContainer $parent
      */
-    public function setParent($parent): void
+    public function setParent(SpgrContainer $parent): void
     {
         $this->parent = $parent;
     }
 
     /**
      * Get the parent Shape Group Container.
-     *
-     * @return SpgrContainer
      */
-    public function getParent()
+    public function getParent(): SpgrContainer
     {
         return $this->parent;
     }
 
     /**
      * Set whether this is a group shape.
-     *
-     * @param bool $value
      */
-    public function setSpgr($value): void
+    public function setSpgr(bool $value): void
     {
         $this->spgr = $value;
     }
 
     /**
      * Get whether this is a group shape.
-     *
-     * @return bool
      */
-    public function getSpgr()
+    public function getSpgr(): bool
     {
         return $this->spgr;
     }
 
     /**
      * Set the shape type.
-     *
-     * @param int $value
      */
-    public function setSpType($value): void
+    public function setSpType(int $value): void
     {
         $this->spType = $value;
     }
 
     /**
      * Get the shape type.
-     *
-     * @return int
      */
-    public function getSpType()
+    public function getSpType(): int
     {
         return $this->spType;
     }
 
     /**
      * Set the shape flag.
-     *
-     * @param int $value
      */
-    public function setSpFlag($value): void
+    public function setSpFlag(int $value): void
     {
         $this->spFlag = $value;
     }
 
     /**
      * Get the shape flag.
-     *
-     * @return int
      */
-    public function getSpFlag()
+    public function getSpFlag(): int
     {
         return $this->spFlag;
     }
 
     /**
      * Set the shape index.
-     *
-     * @param int $value
      */
-    public function setSpId($value): void
+    public function setSpId(int $value): void
     {
         $this->spId = $value;
     }
 
     /**
      * Get the shape index.
-     *
-     * @return int
      */
-    public function getSpId()
+    public function getSpId(): int
     {
         return $this->spId;
     }
@@ -194,9 +150,8 @@ class SpContainer
      * Set an option for the Shape Group Container.
      *
      * @param int $property The number specifies the option
-     * @param mixed $value
      */
-    public function setOPT($property, $value): void
+    public function setOPT(int $property, mixed $value): void
     {
         $this->OPT[$property] = $value;
     }
@@ -205,10 +160,8 @@ class SpContainer
      * Get an option for the Shape Group Container.
      *
      * @param int $property The number specifies the option
-     *
-     * @return mixed
      */
-    public function getOPT($property)
+    public function getOPT(int $property): mixed
     {
         if (isset($this->OPT[$property])) {
             return $this->OPT[$property];
@@ -219,10 +172,8 @@ class SpContainer
 
     /**
      * Get the collection of options.
-     *
-     * @return array
      */
-    public function getOPTCollection()
+    public function getOPTCollection(): array
     {
         return $this->OPT;
     }
@@ -232,57 +183,47 @@ class SpContainer
      *
      * @param string $value eg: 'A1'
      */
-    public function setStartCoordinates($value): void
+    public function setStartCoordinates(string $value): void
     {
         $this->startCoordinates = $value;
     }
 
     /**
      * Get cell coordinates of upper-left corner of shape.
-     *
-     * @return string
      */
-    public function getStartCoordinates()
+    public function getStartCoordinates(): string
     {
         return $this->startCoordinates;
     }
 
     /**
      * Set offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
-     *
-     * @param int $startOffsetX
      */
-    public function setStartOffsetX($startOffsetX): void
+    public function setStartOffsetX(int|float $startOffsetX): void
     {
         $this->startOffsetX = $startOffsetX;
     }
 
     /**
      * Get offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
-     *
-     * @return int
      */
-    public function getStartOffsetX()
+    public function getStartOffsetX(): int|float
     {
         return $this->startOffsetX;
     }
 
     /**
      * Set offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
-     *
-     * @param int $startOffsetY
      */
-    public function setStartOffsetY($startOffsetY): void
+    public function setStartOffsetY(int|float $startOffsetY): void
     {
         $this->startOffsetY = $startOffsetY;
     }
 
     /**
      * Get offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
-     *
-     * @return int
      */
-    public function getStartOffsetY()
+    public function getStartOffsetY(): int|float
     {
         return $this->startOffsetY;
     }
@@ -292,57 +233,47 @@ class SpContainer
      *
      * @param string $value eg: 'A1'
      */
-    public function setEndCoordinates($value): void
+    public function setEndCoordinates(string $value): void
     {
         $this->endCoordinates = $value;
     }
 
     /**
      * Get cell coordinates of bottom-right corner of shape.
-     *
-     * @return string
      */
-    public function getEndCoordinates()
+    public function getEndCoordinates(): string
     {
         return $this->endCoordinates;
     }
 
     /**
      * Set offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
-     *
-     * @param int $endOffsetX
      */
-    public function setEndOffsetX($endOffsetX): void
+    public function setEndOffsetX(int|float $endOffsetX): void
     {
         $this->endOffsetX = $endOffsetX;
     }
 
     /**
      * Get offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
-     *
-     * @return int
      */
-    public function getEndOffsetX()
+    public function getEndOffsetX(): int|float
     {
         return $this->endOffsetX;
     }
 
     /**
      * Set offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
-     *
-     * @param int $endOffsetY
      */
-    public function setEndOffsetY($endOffsetY): void
+    public function setEndOffsetY(int|float $endOffsetY): void
     {
         $this->endOffsetY = $endOffsetY;
     }
 
     /**
      * Get offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
-     *
-     * @return int
      */
-    public function getEndOffsetY()
+    public function getEndOffsetY(): int|float
     {
         return $this->endOffsetY;
     }
@@ -354,7 +285,7 @@ class SpContainer
      *
      * @return int Nesting level
      */
-    public function getNestingLevel()
+    public function getNestingLevel(): int
     {
         $nestingLevel = 0;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php
index 36806aa..d0bf1bb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php
@@ -6,122 +6,94 @@ class DggContainer
 {
     /**
      * Maximum shape index of all shapes in all drawings increased by one.
-     *
-     * @var int
      */
-    private $spIdMax;
+    private int $spIdMax;
 
     /**
      * Total number of drawings saved.
-     *
-     * @var int
      */
-    private $cDgSaved;
+    private int $cDgSaved;
 
     /**
      * Total number of shapes saved (including group shapes).
-     *
-     * @var int
      */
-    private $cSpSaved;
+    private int $cSpSaved;
 
     /**
      * BLIP Store Container.
-     *
-     * @var DggContainer\BstoreContainer
      */
-    private $bstoreContainer;
+    private ?DggContainer\BstoreContainer $bstoreContainer = null;
 
     /**
      * Array of options for the drawing group.
-     *
-     * @var array
      */
-    private $OPT = [];
+    private array $OPT = [];
 
     /**
      * Array of identifier clusters containg information about the maximum shape identifiers.
-     *
-     * @var array
      */
-    private $IDCLs = [];
+    private array $IDCLs = [];
 
     /**
      * Get maximum shape index of all shapes in all drawings (plus one).
-     *
-     * @return int
      */
-    public function getSpIdMax()
+    public function getSpIdMax(): int
     {
         return $this->spIdMax;
     }
 
     /**
      * Set maximum shape index of all shapes in all drawings (plus one).
-     *
-     * @param int $value
      */
-    public function setSpIdMax($value): void
+    public function setSpIdMax(int $value): void
     {
         $this->spIdMax = $value;
     }
 
     /**
      * Get total number of drawings saved.
-     *
-     * @return int
      */
-    public function getCDgSaved()
+    public function getCDgSaved(): int
     {
         return $this->cDgSaved;
     }
 
     /**
      * Set total number of drawings saved.
-     *
-     * @param int $value
      */
-    public function setCDgSaved($value): void
+    public function setCDgSaved(int $value): void
     {
         $this->cDgSaved = $value;
     }
 
     /**
      * Get total number of shapes saved (including group shapes).
-     *
-     * @return int
      */
-    public function getCSpSaved()
+    public function getCSpSaved(): int
     {
         return $this->cSpSaved;
     }
 
     /**
      * Set total number of shapes saved (including group shapes).
-     *
-     * @param int $value
      */
-    public function setCSpSaved($value): void
+    public function setCSpSaved(int $value): void
     {
         $this->cSpSaved = $value;
     }
 
     /**
      * Get BLIP Store Container.
-     *
-     * @return DggContainer\BstoreContainer
      */
-    public function getBstoreContainer()
+    public function getBstoreContainer(): ?DggContainer\BstoreContainer
     {
         return $this->bstoreContainer;
     }
 
     /**
      * Set BLIP Store Container.
-     *
-     * @param DggContainer\BstoreContainer $bstoreContainer
      */
-    public function setBstoreContainer($bstoreContainer): void
+    public function setBstoreContainer(DggContainer\BstoreContainer $bstoreContainer): void
     {
         $this->bstoreContainer = $bstoreContainer;
     }
@@ -130,9 +102,8 @@ class DggContainer
      * Set an option for the drawing group.
      *
      * @param int $property The number specifies the option
-     * @param mixed $value
      */
-    public function setOPT($property, $value): void
+    public function setOPT(int $property, mixed $value): void
     {
         $this->OPT[$property] = $value;
     }
@@ -141,10 +112,8 @@ class DggContainer
      * Get an option for the drawing group.
      *
      * @param int $property The number specifies the option
-     *
-     * @return mixed
      */
-    public function getOPT($property)
+    public function getOPT(int $property): mixed
     {
         if (isset($this->OPT[$property])) {
             return $this->OPT[$property];
@@ -155,20 +124,16 @@ class DggContainer
 
     /**
      * Get identifier clusters.
-     *
-     * @return array
      */
-    public function getIDCLs()
+    public function getIDCLs(): array
     {
         return $this->IDCLs;
     }
 
     /**
      * Set identifier clusters. [<drawingId> => <max shape id>, ...].
-     *
-     * @param array $IDCLs
      */
-    public function setIDCLs($IDCLs): void
+    public function setIDCLs(array $IDCLs): void
     {
         $this->IDCLs = $IDCLs;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
index 7203b66..2d13b42 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php
@@ -9,7 +9,7 @@ class BstoreContainer
      *
      * @var BstoreContainer\BSE[]
      */
-    private $BSECollection = [];
+    private array $BSECollection = [];
 
     /**
      * Add a BLIP Store Entry.
@@ -25,7 +25,7 @@ class BstoreContainer
      *
      * @return BstoreContainer\BSE[]
      */
-    public function getBSECollection()
+    public function getBSECollection(): array
     {
         return $this->BSECollection;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
index d24af3f..98e3656 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php
@@ -19,24 +19,19 @@ class BSE
 
     /**
      * The parent BLIP Store Entry Container.
-     *
-     * @var BstoreContainer
+     * Property is currently unused.
      */
-    private $parent;
+    private BstoreContainer $parent;
 
     /**
      * The BLIP (Big Large Image or Picture).
-     *
-     * @var BSE\Blip
      */
-    private $blip;
+    private ?BSE\Blip $blip = null;
 
     /**
      * The BLIP type.
-     *
-     * @var int
      */
-    private $blipType;
+    private int $blipType;
 
     /**
      * Set parent BLIP Store Entry Container.
@@ -46,12 +41,15 @@ class BSE
         $this->parent = $parent;
     }
 
+    public function getParent(): BstoreContainer
+    {
+        return $this->parent;
+    }
+
     /**
      * Get the BLIP.
-     *
-     * @return BSE\Blip
      */
-    public function getBlip()
+    public function getBlip(): ?BSE\Blip
     {
         return $this->blip;
     }
@@ -67,20 +65,16 @@ class BSE
 
     /**
      * Get the BLIP type.
-     *
-     * @return int
      */
-    public function getBlipType()
+    public function getBlipType(): int
     {
         return $this->blipType;
     }
 
     /**
      * Set the BLIP type.
-     *
-     * @param int $blipType
      */
-    public function setBlipType($blipType): void
+    public function setBlipType(int $blipType): void
     {
         $this->blipType = $blipType;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
index 03b261f..763bfe5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php
@@ -8,34 +8,26 @@ class Blip
 {
     /**
      * The parent BSE.
-     *
-     * @var BSE
      */
-    private $parent;
+    private BSE $parent;
 
     /**
      * Raw image data.
-     *
-     * @var string
      */
-    private $data;
+    private string $data;
 
     /**
      * Get the raw image data.
-     *
-     * @return string
      */
-    public function getData()
+    public function getData(): string
     {
         return $this->data;
     }
 
     /**
      * Set the raw image data.
-     *
-     * @param string $data
      */
-    public function setData($data): void
+    public function setData(string $data): void
     {
         $this->data = $data;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php
index f2fe8ca..022c1bb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/File.php
@@ -10,10 +10,8 @@ class File
 {
     /**
      * Use Temp or File Upload Temp for temporary files.
-     *
-     * @var bool
      */
-    protected static $useUploadTempDirectory = false;
+    protected static bool $useUploadTempDirectory = false;
 
     /**
      * Set the flag indicating whether the File Upload Temp directory should be used for temporary files.
@@ -94,9 +92,9 @@ class File
             $pathArray = explode('/', $filename);
             while (in_array('..', $pathArray) && $pathArray[0] != '..') {
                 $iMax = count($pathArray);
-                for ($i = 0; $i < $iMax; ++$i) {
-                    if ($pathArray[$i] == '..' && $i > 0) {
-                        unset($pathArray[$i], $pathArray[$i - 1]);
+                for ($i = 1; $i < $iMax; ++$i) {
+                    if ($pathArray[$i] == '..') {
+                        array_splice($pathArray, $i - 1, 2);
 
                         break;
                     }
@@ -156,7 +154,11 @@ class File
         if ($zipMember !== '') {
             $zipfile = "zip://$filename#$zipMember";
             if (!self::fileExists($zipfile)) {
-                throw new ReaderException("Could not find zip member $zipfile");
+                // Has the file been saved with Windoze directory separators rather than unix?
+                $zipfile = "zip://$filename#" . str_replace('/', '\\', $zipMember);
+                if (!self::fileExists($zipfile)) {
+                    throw new ReaderException("Could not find zip member $zipfile");
+                }
             }
         }
     }
@@ -180,6 +182,14 @@ class File
             return self::validateZipFirst4($filename);
         }
 
-        return self::fileExists("zip://$filename#$zipMember");
+        $zipfile = "zip://$filename#$zipMember";
+        if (self::fileExists($zipfile)) {
+            return true;
+        }
+
+        // Has the file been saved with Windoze directory separators rather than unix?
+        $zipfile = "zip://$filename#" . str_replace('/', '\\', $zipMember);
+
+        return self::fileExists($zipfile);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
index dfe9f77..8a1225b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php
@@ -6,6 +6,8 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Style\Alignment;
 use PhpOffice\PhpSpreadsheet\Style\Font as FontStyle;
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
 
 class Font
 {
@@ -201,18 +203,33 @@ class Font
     ];
 
     /**
-     * AutoSize method.
+     * Array that can be used to supplement FONT_FILE_NAMES for calculating exact width.
      *
-     * @var string
+     * @var array<string, array<string, string>>
      */
-    private static $autoSizeMethod = self::AUTOSIZE_METHOD_APPROX;
+    private static array $extraFontArray = [];
+
+    /** @param array<string, array<string, string>> $extraFontArray */
+    public static function setExtraFontArray(array $extraFontArray): void
+    {
+        self::$extraFontArray = $extraFontArray;
+    }
+
+    /** @return array<string, array<string, string>> */
+    public static function getExtraFontArray(): array
+    {
+        return self::$extraFontArray;
+    }
+
+    /**
+     * AutoSize method.
+     */
+    private static string $autoSizeMethod = self::AUTOSIZE_METHOD_APPROX;
 
     /**
      * Path to folder containing TrueType font .ttf files.
-     *
-     * @var string
      */
-    private static $trueTypeFontPath = '';
+    private static string $trueTypeFontPath = '';
 
     /**
      * How wide is a default column for a given default font and size?
@@ -261,17 +278,6 @@ class Font
         ],
     ];
 
-    /**
-     * List of column widths. Replaced by constant;
-     * previously it was public and updateable, allowing
-     * user to make inappropriate alterations.
-     *
-     * @deprecated 1.25.0 Use DEFAULT_COLUMN_WIDTHS constant instead.
-     *
-     * @var array
-     */
-    public static $defaultColumnWidths = self::DEFAULT_COLUMN_WIDTHS;
-
     /**
      * Set autoSize method.
      *
@@ -279,7 +285,7 @@ class Font
      *
      * @return bool Success or failure
      */
-    public static function setAutoSizeMethod($method)
+    public static function setAutoSizeMethod(string $method): bool
     {
         if (!in_array($method, self::AUTOSIZE_METHODS)) {
             return false;
@@ -291,40 +297,56 @@ class Font
 
     /**
      * Get autoSize method.
-     *
-     * @return string
      */
-    public static function getAutoSizeMethod()
+    public static function getAutoSizeMethod(): string
     {
         return self::$autoSizeMethod;
     }
 
     /**
      * Set the path to the folder containing .ttf files. There should be a trailing slash.
-     * Typical locations on variout some platforms:
+     * Path will be recursively searched for font file.
+     * Typical locations on various platforms:
      *    <ul>
      *        <li>C:/Windows/Fonts/</li>
      *        <li>/usr/share/fonts/truetype/</li>
      *        <li>~/.fonts/</li>
      * </ul>.
-     *
-     * @param string $folderPath
      */
-    public static function setTrueTypeFontPath($folderPath): void
+    public static function setTrueTypeFontPath(string $folderPath): void
     {
         self::$trueTypeFontPath = $folderPath;
     }
 
     /**
      * Get the path to the folder containing .ttf files.
-     *
-     * @return string
      */
-    public static function getTrueTypeFontPath()
+    public static function getTrueTypeFontPath(): string
     {
         return self::$trueTypeFontPath;
     }
 
+    /**
+     * Pad amount for exact in pixels; use best guess if null.
+     */
+    private static null|float|int $paddingAmountExact = null;
+
+    /**
+     * Set pad amount for exact in pixels; use best guess if null.
+     */
+    public static function setPaddingAmountExact(null|float|int $paddingAmountExact): void
+    {
+        self::$paddingAmountExact = $paddingAmountExact;
+    }
+
+    /**
+     * Get pad amount for exact in pixels; or null if using best guess.
+     */
+    public static function getPaddingAmountExact(): null|float|int
+    {
+        return self::$paddingAmountExact;
+    }
+
     /**
      * Calculate an (approximate) OpenXML column width, based on font size and text contained.
      *
@@ -337,11 +359,11 @@ class Font
     public static function calculateColumnWidth(
         FontStyle $font,
         $cellText = '',
-        $rotation = 0,
+        int $rotation = 0,
         ?FontStyle $defaultFont = null,
         bool $filterAdjustment = false,
         int $indentAdjustment = 0
-    ): int {
+    ): float {
         // If it is rich text, use plain text
         if ($cellText instanceof RichText) {
             $cellText = $cellText->getPlainText();
@@ -349,7 +371,7 @@ class Font
 
         // Special case if there are one or more newline characters ("\n")
         $cellText = (string) $cellText;
-        if (strpos($cellText, "\n") !== false) {
+        if (str_contains($cellText, "\n")) {
             $lineTexts = explode("\n", $cellText);
             $lineWidths = [];
             foreach ($lineTexts as $lineText) {
@@ -363,19 +385,19 @@ class Font
         $approximate = self::$autoSizeMethod === self::AUTOSIZE_METHOD_APPROX;
         $columnWidth = 0;
         if (!$approximate) {
-            $columnWidthAdjust = ceil(
-                self::getTextWidthPixelsExact(
-                    str_repeat('n', 1 * (($filterAdjustment ? 3 : 1) + ($indentAdjustment * 2))),
-                    $font,
-                    0
-                ) * 1.07
-            );
-
             try {
+                $columnWidthAdjust = ceil(
+                    self::getTextWidthPixelsExact(
+                        str_repeat('n', 1 * (($filterAdjustment ? 3 : 1) + ($indentAdjustment * 2))),
+                        $font,
+                        0
+                    ) * 1.07
+                );
+
                 // Width of text in pixels excl. padding
                 // and addition because Excel adds some padding, just use approx width of 'n' glyph
-                $columnWidth = self::getTextWidthPixelsExact($cellText, $font, $rotation) + $columnWidthAdjust;
-            } catch (PhpSpreadsheetException $e) {
+                $columnWidth = self::getTextWidthPixelsExact($cellText, $font, $rotation) + (self::$paddingAmountExact ?? $columnWidthAdjust);
+            } catch (PhpSpreadsheetException) {
                 $approximate = true;
             }
         }
@@ -395,18 +417,14 @@ class Font
         $columnWidth = Drawing::pixelsToCellDimension((int) $columnWidth, $defaultFont ?? new FontStyle());
 
         // Return
-        return (int) round($columnWidth, 6);
+        return round($columnWidth, 4);
     }
 
     /**
      * Get GD text width in pixels for a string of text in a certain font at a certain rotation angle.
      */
-    public static function getTextWidthPixelsExact(string $text, FontStyle $font, int $rotation = 0): int
+    public static function getTextWidthPixelsExact(string $text, FontStyle $font, int $rotation = 0): float
     {
-        if (!function_exists('imagettfbbox')) {
-            throw new PhpSpreadsheetException('GD library needs to be enabled');
-        }
-
         // font size should really be supplied in pixels in GD2,
         // but since GD2 seems to assume 72dpi, pixels and points are the same
         $fontFile = self::getTrueTypeFontFileFromFont($font);
@@ -424,45 +442,39 @@ class Font
         $upperLeftCornerX = $textBox[6];
 
         // Consider the rotation when calculating the width
-        return max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX);
+        return round(max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX), 4);
     }
 
     /**
      * Get approximate width in pixels for a string of text in a certain font at a certain rotation angle.
      *
-     * @param string $columnText
-     * @param int $rotation
-     *
      * @return int Text width in pixels (no padding added)
      */
-    public static function getTextWidthPixelsApprox($columnText, FontStyle $font, $rotation = 0)
+    public static function getTextWidthPixelsApprox(string $columnText, FontStyle $font, int $rotation = 0): int
     {
         $fontName = $font->getName();
         $fontSize = $font->getSize();
 
-        // Calculate column width in pixels. We assume fixed glyph width. Result varies with font name and size.
+        // Calculate column width in pixels.
+        // We assume fixed glyph width, but count double for "fullwidth" characters.
+        // Result varies with font name and size.
         switch ($fontName) {
-            case 'Calibri':
-                // value 8.26 was found via interpolation by inspecting real Excel files with Calibri 11 font.
-                $columnWidth = (int) (8.26 * StringHelper::countCharacters($columnText));
-                $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
-
-                break;
             case 'Arial':
                 // value 8 was set because of experience in different exports at Arial 10 font.
-                $columnWidth = (int) (8 * StringHelper::countCharacters($columnText));
+                $columnWidth = (int) (8 * StringHelper::countCharactersDbcs($columnText));
                 $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
 
                 break;
             case 'Verdana':
                 // value 8 was found via interpolation by inspecting real Excel files with Verdana 10 font.
-                $columnWidth = (int) (8 * StringHelper::countCharacters($columnText));
+                $columnWidth = (int) (8 * StringHelper::countCharactersDbcs($columnText));
                 $columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
 
                 break;
             default:
                 // just assume Calibri
-                $columnWidth = (int) (8.26 * StringHelper::countCharacters($columnText));
+                // value 8.26 was found via interpolation by inspecting real Excel files with Calibri 11 font.
+                $columnWidth = (int) (8.26 * StringHelper::countCharactersDbcs($columnText));
                 $columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
 
                 break;
@@ -487,11 +499,11 @@ class Font
     /**
      * Calculate an (approximate) pixel size, based on a font points size.
      *
-     * @param int $fontSizeInPoints Font size (in points)
+     * @param float|int $fontSizeInPoints Font size (in points)
      *
      * @return int Font size (in pixels)
      */
-    public static function fontSizeToPixels($fontSizeInPoints)
+    public static function fontSizeToPixels(float|int $fontSizeInPoints): int
     {
         return (int) ((4 / 3) * $fontSizeInPoints);
     }
@@ -499,11 +511,11 @@ class Font
     /**
      * Calculate an (approximate) pixel size, based on inch size.
      *
-     * @param int $sizeInInch Font size (in inch)
+     * @param float|int $sizeInInch Font size (in inch)
      *
-     * @return int Size (in pixels)
+     * @return float|int Size (in pixels)
      */
-    public static function inchSizeToPixels($sizeInInch)
+    public static function inchSizeToPixels(int|float $sizeInInch): int|float
     {
         return $sizeInInch * 96;
     }
@@ -511,11 +523,11 @@ class Font
     /**
      * Calculate an (approximate) pixel size, based on centimeter size.
      *
-     * @param int $sizeInCm Font size (in centimeters)
+     * @param float|int $sizeInCm Font size (in centimeters)
      *
      * @return float Size (in pixels)
      */
-    public static function centimeterSizeToPixels($sizeInCm)
+    public static function centimeterSizeToPixels(int|float $sizeInCm): float
     {
         return $sizeInCm * 37.795275591;
     }
@@ -525,14 +537,15 @@ class Font
      *
      * @return string Path to TrueType font file
      */
-    public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkPath = true)
+    public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkPath = true): string
     {
         if ($checkPath && (!file_exists(self::$trueTypeFontPath) || !is_dir(self::$trueTypeFontPath))) {
             throw new PhpSpreadsheetException('Valid directory to TrueType Font files not specified');
         }
 
         $name = $font->getName();
-        if (!isset(self::FONT_FILE_NAMES[$name])) {
+        $fontArray = array_merge(self::FONT_FILE_NAMES, self::$extraFontArray);
+        if (!isset($fontArray[$name])) {
             throw new PhpSpreadsheetException('Unknown font name "' . $name . '". Cannot map to TrueType font file');
         }
         $bold = $font->getBold();
@@ -544,17 +557,44 @@ class Font
         if ($italic) {
             $index .= 'i';
         }
-        $fontFile = self::FONT_FILE_NAMES[$name][$index];
+        $fontFile = $fontArray[$name][$index];
 
         $separator = '';
         if (mb_strlen(self::$trueTypeFontPath) > 1 && mb_substr(self::$trueTypeFontPath, -1) !== '/' && mb_substr(self::$trueTypeFontPath, -1) !== '\\') {
             $separator = DIRECTORY_SEPARATOR;
         }
-        $fontFile = self::$trueTypeFontPath . $separator . $fontFile;
+        $fontFileAbsolute = preg_match('~^([A-Za-z]:)?[/\\\\]~', $fontFile) === 1;
+        if (!$fontFileAbsolute) {
+            $fontFile = self::findFontFile(self::$trueTypeFontPath, $fontFile) ?? self::$trueTypeFontPath . $separator . $fontFile;
+        }
 
         // Check if file actually exists
-        if ($checkPath && !file_exists($fontFile)) {
-            throw new PhpSpreadsheetException('TrueType Font file not found');
+        if ($checkPath && !file_exists($fontFile) && !$fontFileAbsolute) {
+            $alternateName = $name;
+            if ($index !== 'x' && $fontArray[$name][$index] !== $fontArray[$name]['x']) {
+                // Bold but no italic:
+                //   Comic Sans
+                //   Tahoma
+                // Neither bold nor italic:
+                //   Impact
+                //   Lucida Console
+                //   Lucida Sans Unicode
+                //   Microsoft Sans Serif
+                //   Symbol
+                if ($index === 'xb') {
+                    $alternateName .= ' Bold';
+                } elseif ($index === 'xi') {
+                    $alternateName .= ' Italic';
+                } elseif ($fontArray[$name]['xb'] === $fontArray[$name]['xbi']) {
+                    $alternateName .= ' Bold';
+                } else {
+                    $alternateName .= ' Bold Italic';
+                }
+            }
+            $fontFile = self::$trueTypeFontPath . $separator . $alternateName . '.ttf';
+            if (!file_exists($fontFile)) {
+                throw new PhpSpreadsheetException('TrueType Font file not found');
+            }
         }
 
         return $fontFile;
@@ -574,7 +614,7 @@ class Font
      *
      * @return int Character set code
      */
-    public static function getCharsetFromFontName($fontName)
+    public static function getCharsetFromFontName(string $fontName): int
     {
         return self::CHARSET_FROM_FONT_NAME[$fontName] ?? self::CHARSET_ANSI_LATIN;
     }
@@ -586,20 +626,20 @@ class Font
      * @param FontStyle $font The workbooks default font
      * @param bool $returnAsPixels true = return column width in pixels, false = return in OOXML units
      *
-     * @return mixed Column width
+     * @return ($returnAsPixels is true ? int : float) Column width
      */
-    public static function getDefaultColumnWidthByFont(FontStyle $font, $returnAsPixels = false)
+    public static function getDefaultColumnWidthByFont(FontStyle $font, bool $returnAsPixels = false): float|int
     {
         if (isset(self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()])) {
             // Exact width can be determined
-            $columnWidth = $returnAsPixels ?
-                self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['px']
+            $columnWidth = $returnAsPixels
+                ? self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['px']
                     : self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['width'];
         } else {
             // We don't have data for this particular font and size, use approximation by
             // extrapolating from Calibri 11
-            $columnWidth = $returnAsPixels ?
-                self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['px']
+            $columnWidth = $returnAsPixels
+                ? self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['px']
                     : self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['width'];
             $columnWidth = $columnWidth * $font->getSize() / 11;
 
@@ -620,7 +660,7 @@ class Font
      *
      * @return float Row height in points
      */
-    public static function getDefaultRowHeightByFont(FontStyle $font)
+    public static function getDefaultRowHeightByFont(FontStyle $font): float
     {
         $name = $font->getName();
         $size = $font->getSize();
@@ -634,4 +674,45 @@ class Font
 
         return $rowHeight;
     }
+
+    private static function findFontFile(string $startDirectory, string $desiredFont): ?string
+    {
+        $fontPath = null;
+        if ($startDirectory === '') {
+            return null;
+        }
+        if (file_exists("$startDirectory/$desiredFont")) {
+            $fontPath = "$startDirectory/$desiredFont";
+        } else {
+            $iterations = 0;
+            $it = new RecursiveDirectoryIterator(
+                $startDirectory,
+                RecursiveDirectoryIterator::SKIP_DOTS
+                | RecursiveDirectoryIterator::FOLLOW_SYMLINKS
+            );
+            foreach (
+                new RecursiveIteratorIterator(
+                    $it,
+                    RecursiveIteratorIterator::LEAVES_ONLY,
+                    RecursiveIteratorIterator::CATCH_GET_CHILD
+                ) as $filex
+            ) {
+                /** @var string */
+                $file = $filex;
+                if (basename($file) === $desiredFont) {
+                    $fontPath = $file;
+
+                    break;
+                }
+                ++$iterations;
+                if ($iterations > 5000) {
+                    // @codeCoverageIgnoreStart
+                    break;
+                    // @codeCoverageIgnoreEnd
+                }
+            }
+        }
+
+        return $fontPath;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php
index 060f09c..2e20c9c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php
@@ -7,12 +7,8 @@ class IntOrFloat
     /**
      * Help some functions with large results operate correctly on 32-bit,
      * by returning result as int when possible, float otherwise.
-     *
-     * @param float|int $value
-     *
-     * @return float|int
      */
-    public static function evaluate($value)
+    public static function evaluate(float|int $value): float|int
     {
         $iValue = (int) $value;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT
deleted file mode 100644
index 1c18a5d..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/CHANGELOG.TXT
+++ /dev/null
@@ -1,16 +0,0 @@
-Mar 1, 2005 11:15 AST by PM
-
-+ For consistency, renamed Math.php to Maths.java, utils to util, 
-  tests to test, docs to doc - 
-
-+ Removed conditional logic from top of Matrix class.
-
-+ Switched to using hypo function in Maths.php for all php-hypot calls.
-  NOTE TO SELF: Need to make sure that all decompositions have been 
-  switched over to using the bundled hypo.
-
-Feb 25, 2005 at 10:00 AST by PM
-
-+ Recommend using simpler Error.php instead of JAMA_Error.php but 
-  can be persuaded otherwise.
-
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php
deleted file mode 100644
index cc09b64..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/LUDecomposition.php
+++ /dev/null
@@ -1,286 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
-
-use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
-
-/**
- *    For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n
- *    unit lower triangular matrix L, an n-by-n upper triangular matrix U,
- *    and a permutation vector piv of length m so that A(piv,:) = L*U.
- *    If m < n, then L is m-by-m and U is m-by-n.
- *
- *    The LU decompostion with pivoting always exists, even if the matrix is
- *    singular, so the constructor will never fail. The primary use of the
- *    LU decomposition is in the solution of square systems of simultaneous
- *    linear equations. This will fail if isNonsingular() returns false.
- *
- *    @author Paul Meagher
- *    @author Bartosz Matosiuk
- *    @author Michael Bommarito
- *
- *    @version 1.1
- */
-class LUDecomposition
-{
-    const MATRIX_SINGULAR_EXCEPTION = 'Can only perform operation on singular matrix.';
-    const MATRIX_SQUARE_EXCEPTION = 'Mismatched Row dimension';
-
-    /**
-     * Decomposition storage.
-     *
-     * @var array
-     */
-    private $LU = [];
-
-    /**
-     * Row dimension.
-     *
-     * @var int
-     */
-    private $m;
-
-    /**
-     * Column dimension.
-     *
-     * @var int
-     */
-    private $n;
-
-    /**
-     * Pivot sign.
-     *
-     * @var int
-     */
-    private $pivsign;
-
-    /**
-     * Internal storage of pivot vector.
-     *
-     * @var array
-     */
-    private $piv = [];
-
-    /**
-     * LU Decomposition constructor.
-     *
-     * @param ?Matrix $A Rectangular matrix
-     */
-    public function __construct($A)
-    {
-        if ($A instanceof Matrix) {
-            // Use a "left-looking", dot-product, Crout/Doolittle algorithm.
-            $this->LU = $A->getArray();
-            $this->m = $A->getRowDimension();
-            $this->n = $A->getColumnDimension();
-            for ($i = 0; $i < $this->m; ++$i) {
-                $this->piv[$i] = $i;
-            }
-            $this->pivsign = 1;
-            $LUcolj = [];
-
-            // Outer loop.
-            for ($j = 0; $j < $this->n; ++$j) {
-                // Make a copy of the j-th column to localize references.
-                for ($i = 0; $i < $this->m; ++$i) {
-                    $LUcolj[$i] = &$this->LU[$i][$j];
-                }
-                // Apply previous transformations.
-                for ($i = 0; $i < $this->m; ++$i) {
-                    $LUrowi = $this->LU[$i];
-                    // Most of the time is spent in the following dot product.
-                    $kmax = min($i, $j);
-                    $s = 0.0;
-                    for ($k = 0; $k < $kmax; ++$k) {
-                        $s += $LUrowi[$k] * $LUcolj[$k];
-                    }
-                    $LUrowi[$j] = $LUcolj[$i] -= $s;
-                }
-                // Find pivot and exchange if necessary.
-                $p = $j;
-                for ($i = $j + 1; $i < $this->m; ++$i) {
-                    if (abs($LUcolj[$i]) > abs($LUcolj[$p])) {
-                        $p = $i;
-                    }
-                }
-                if ($p != $j) {
-                    for ($k = 0; $k < $this->n; ++$k) {
-                        $t = $this->LU[$p][$k];
-                        $this->LU[$p][$k] = $this->LU[$j][$k];
-                        $this->LU[$j][$k] = $t;
-                    }
-                    $k = $this->piv[$p];
-                    $this->piv[$p] = $this->piv[$j];
-                    $this->piv[$j] = $k;
-                    $this->pivsign = $this->pivsign * -1;
-                }
-                // Compute multipliers.
-                if (($j < $this->m) && ($this->LU[$j][$j] != 0.0)) {
-                    for ($i = $j + 1; $i < $this->m; ++$i) {
-                        $this->LU[$i][$j] /= $this->LU[$j][$j];
-                    }
-                }
-            }
-        } else {
-            throw new CalculationException(Matrix::ARGUMENT_TYPE_EXCEPTION);
-        }
-    }
-
-    //    function __construct()
-
-    /**
-     * Get lower triangular factor.
-     *
-     * @return Matrix Lower triangular factor
-     */
-    public function getL()
-    {
-        $L = [];
-        for ($i = 0; $i < $this->m; ++$i) {
-            for ($j = 0; $j < $this->n; ++$j) {
-                if ($i > $j) {
-                    $L[$i][$j] = $this->LU[$i][$j];
-                } elseif ($i == $j) {
-                    $L[$i][$j] = 1.0;
-                } else {
-                    $L[$i][$j] = 0.0;
-                }
-            }
-        }
-
-        return new Matrix($L);
-    }
-
-    //    function getL()
-
-    /**
-     * Get upper triangular factor.
-     *
-     * @return Matrix Upper triangular factor
-     */
-    public function getU()
-    {
-        $U = [];
-        for ($i = 0; $i < $this->n; ++$i) {
-            for ($j = 0; $j < $this->n; ++$j) {
-                if ($i <= $j) {
-                    $U[$i][$j] = $this->LU[$i][$j];
-                } else {
-                    $U[$i][$j] = 0.0;
-                }
-            }
-        }
-
-        return new Matrix($U);
-    }
-
-    //    function getU()
-
-    /**
-     * Return pivot permutation vector.
-     *
-     * @return array Pivot vector
-     */
-    public function getPivot()
-    {
-        return $this->piv;
-    }
-
-    //    function getPivot()
-
-    /**
-     * Alias for getPivot.
-     *
-     * @see getPivot
-     *
-     * @return array Pivot vector
-     */
-    public function getDoublePivot()
-    {
-        return $this->getPivot();
-    }
-
-    //    function getDoublePivot()
-
-    /**
-     *    Is the matrix nonsingular?
-     *
-     * @return bool true if U, and hence A, is nonsingular
-     */
-    public function isNonsingular()
-    {
-        for ($j = 0; $j < $this->n; ++$j) {
-            if ($this->LU[$j][$j] == 0) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    //    function isNonsingular()
-
-    /**
-     * Count determinants.
-     *
-     * @return float
-     */
-    public function det()
-    {
-        if ($this->m == $this->n) {
-            $d = $this->pivsign;
-            for ($j = 0; $j < $this->n; ++$j) {
-                $d *= $this->LU[$j][$j];
-            }
-
-            return $d;
-        }
-
-        throw new CalculationException(Matrix::MATRIX_DIMENSION_EXCEPTION);
-    }
-
-    //    function det()
-
-    /**
-     * Solve A*X = B.
-     *
-     * @param Matrix $B a Matrix with as many rows as A and any number of columns
-     *
-     * @return Matrix X so that L*U*X = B(piv,:)
-     */
-    public function solve(Matrix $B)
-    {
-        if ($B->getRowDimension() == $this->m) {
-            if ($this->isNonsingular()) {
-                // Copy right hand side with pivoting
-                $nx = $B->getColumnDimension();
-                $X = $B->getMatrix($this->piv, 0, $nx - 1);
-                // Solve L*Y = B(piv,:)
-                for ($k = 0; $k < $this->n; ++$k) {
-                    for ($i = $k + 1; $i < $this->n; ++$i) {
-                        for ($j = 0; $j < $nx; ++$j) {
-                            $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
-                        }
-                    }
-                }
-                // Solve U*X = Y;
-                for ($k = $this->n - 1; $k >= 0; --$k) {
-                    for ($j = 0; $j < $nx; ++$j) {
-                        $X->A[$k][$j] /= $this->LU[$k][$k];
-                    }
-                    for ($i = 0; $i < $k; ++$i) {
-                        for ($j = 0; $j < $nx; ++$j) {
-                            $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
-                        }
-                    }
-                }
-
-                return $X;
-            }
-
-            throw new CalculationException(self::MATRIX_SINGULAR_EXCEPTION);
-        }
-
-        throw new CalculationException(self::MATRIX_SQUARE_EXCEPTION);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php
deleted file mode 100644
index 75e4424..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/Matrix.php
+++ /dev/null
@@ -1,1167 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
-
-use PhpOffice\PhpSpreadsheet\Calculation\Engine\FormattedNumber;
-use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
-use PhpOffice\PhpSpreadsheet\Calculation\Functions;
-use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
-
-/**
- * Matrix class.
- *
- * @author Paul Meagher
- * @author Michael Bommarito
- * @author Lukasz Karapuda
- * @author Bartek Matosiuk
- *
- * @version 1.8
- *
- * @see https://math.nist.gov/javanumerics/jama/
- */
-class Matrix
-{
-    const POLYMORPHIC_ARGUMENT_EXCEPTION = 'Invalid argument pattern for polymorphic function.';
-    const ARGUMENT_TYPE_EXCEPTION = 'Invalid argument type.';
-    const ARGUMENT_BOUNDS_EXCEPTION = 'Invalid argument range.';
-    const MATRIX_DIMENSION_EXCEPTION = 'Matrix dimensions are not equal.';
-    const ARRAY_LENGTH_EXCEPTION = 'Array length must be a multiple of m.';
-    const MATRIX_SPD_EXCEPTION = 'Can only perform operation on symmetric positive definite matrix.';
-
-    /**
-     * Matrix storage.
-     *
-     * @var array
-     */
-    public $A = [];
-
-    /**
-     * Matrix row dimension.
-     *
-     * @var int
-     */
-    private $m;
-
-    /**
-     * Matrix column dimension.
-     *
-     * @var int
-     */
-    private $n;
-
-    /**
-     * Polymorphic constructor.
-     *
-     * As PHP has no support for polymorphic constructors, we use tricks to make our own sort of polymorphism using func_num_args, func_get_arg, and gettype. In essence, we're just implementing a simple RTTI filter and calling the appropriate constructor.
-     */
-    public function __construct(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                //Rectangular matrix - m x n initialized from 2D array
-                case 'array':
-                    $this->m = count($args[0]);
-                    $this->n = count($args[0][0]);
-                    $this->A = $args[0];
-
-                    break;
-                    //Square matrix - n x n
-                case 'integer':
-                    $this->m = $args[0];
-                    $this->n = $args[0];
-                    $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
-
-                    break;
-                    //Rectangular matrix - m x n
-                case 'integer,integer':
-                    $this->m = $args[0];
-                    $this->n = $args[1];
-                    $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
-
-                    break;
-                    //Rectangular matrix - m x n initialized from packed array
-                case 'array,integer':
-                    $this->m = $args[1];
-                    if ($this->m != 0) {
-                        $this->n = count($args[0]) / $this->m;
-                    } else {
-                        $this->n = 0;
-                    }
-                    if (($this->m * $this->n) == count($args[0])) {
-                        for ($i = 0; $i < $this->m; ++$i) {
-                            for ($j = 0; $j < $this->n; ++$j) {
-                                $this->A[$i][$j] = $args[0][$i + $j * $this->m];
-                            }
-                        }
-                    } else {
-                        throw new CalculationException(self::ARRAY_LENGTH_EXCEPTION);
-                    }
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-        } else {
-            throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-        }
-    }
-
-    /**
-     * getArray.
-     *
-     * @return array Matrix array
-     */
-    public function getArray()
-    {
-        return $this->A;
-    }
-
-    /**
-     * getRowDimension.
-     *
-     * @return int Row dimension
-     */
-    public function getRowDimension()
-    {
-        return $this->m;
-    }
-
-    /**
-     * getColumnDimension.
-     *
-     * @return int Column dimension
-     */
-    public function getColumnDimension()
-    {
-        return $this->n;
-    }
-
-    /**
-     * get.
-     *
-     * Get the i,j-th element of the matrix.
-     *
-     * @param int $i Row position
-     * @param int $j Column position
-     *
-     * @return float|int
-     */
-    public function get($i = null, $j = null)
-    {
-        return $this->A[$i][$j];
-    }
-
-    /**
-     * getMatrix.
-     *
-     *    Get a submatrix
-     *
-     * @return Matrix Submatrix
-     */
-    public function getMatrix(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                //A($i0...; $j0...)
-                case 'integer,integer':
-                    [$i0, $j0] = $args;
-                    if ($i0 >= 0) {
-                        $m = $this->m - $i0;
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    if ($j0 >= 0) {
-                        $n = $this->n - $j0;
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    $R = new self($m, $n);
-                    for ($i = $i0; $i < $this->m; ++$i) {
-                        for ($j = $j0; $j < $this->n; ++$j) {
-                            $R->set($i, $j, $this->A[$i][$j]);
-                        }
-                    }
-
-                    return $R;
-                    //A($i0...$iF; $j0...$jF)
-                case 'integer,integer,integer,integer':
-                    [$i0, $iF, $j0, $jF] = $args;
-                    if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
-                        $m = $iF - $i0;
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    if (($jF > $j0) && ($this->n >= $jF) && ($j0 >= 0)) {
-                        $n = $jF - $j0;
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    $R = new self($m + 1, $n + 1);
-                    for ($i = $i0; $i <= $iF; ++$i) {
-                        for ($j = $j0; $j <= $jF; ++$j) {
-                            $R->set($i - $i0, $j - $j0, $this->A[$i][$j]);
-                        }
-                    }
-
-                    return $R;
-                    //$R = array of row indices; $C = array of column indices
-                case 'array,array':
-                    [$RL, $CL] = $args;
-                    if (count($RL) > 0) {
-                        $m = count($RL);
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    if (count($CL) > 0) {
-                        $n = count($CL);
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    $R = new self($m, $n);
-                    for ($i = 0; $i < $m; ++$i) {
-                        for ($j = 0; $j < $n; ++$j) {
-                            $R->set($i, $j, $this->A[$RL[$i]][$CL[$j]]);
-                        }
-                    }
-
-                    return $R;
-                    //A($i0...$iF); $CL = array of column indices
-                case 'integer,integer,array':
-                    [$i0, $iF, $CL] = $args;
-                    if (($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0)) {
-                        $m = $iF - $i0;
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    if (count($CL) > 0) {
-                        $n = count($CL);
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    $R = new self($m, $n);
-                    for ($i = $i0; $i < $iF; ++$i) {
-                        for ($j = 0; $j < $n; ++$j) {
-                            $R->set($i - $i0, $j, $this->A[$i][$CL[$j]]);
-                        }
-                    }
-
-                    return $R;
-                    //$RL = array of row indices
-                case 'array,integer,integer':
-                    [$RL, $j0, $jF] = $args;
-                    if (count($RL) > 0) {
-                        $m = count($RL);
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    if (($jF >= $j0) && ($this->n >= $jF) && ($j0 >= 0)) {
-                        $n = $jF - $j0;
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_BOUNDS_EXCEPTION);
-                    }
-                    $R = new self($m, $n + 1);
-                    for ($i = 0; $i < $m; ++$i) {
-                        for ($j = $j0; $j <= $jF; ++$j) {
-                            $R->set($i, $j - $j0, $this->A[$RL[$i]][$j]);
-                        }
-                    }
-
-                    return $R;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-        } else {
-            throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-        }
-    }
-
-    /**
-     * checkMatrixDimensions.
-     *
-     *    Is matrix B the same size?
-     *
-     * @param Matrix $B Matrix B
-     *
-     * @return bool
-     */
-    public function checkMatrixDimensions($B = null)
-    {
-        if ($B instanceof self) {
-            if (($this->m == $B->getRowDimension()) && ($this->n == $B->getColumnDimension())) {
-                return true;
-            }
-
-            throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
-        }
-
-        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-    }
-
-    //    function checkMatrixDimensions()
-
-    /**
-     * set.
-     *
-     * Set the i,j-th element of the matrix.
-     *
-     * @param int $i Row position
-     * @param int $j Column position
-     * @param float|int $c value
-     */
-    public function set($i = null, $j = null, $c = null): void
-    {
-        // Optimized set version just has this
-        $this->A[$i][$j] = $c;
-    }
-
-    //    function set()
-
-    /**
-     * identity.
-     *
-     * Generate an identity matrix.
-     *
-     * @param int $m Row dimension
-     * @param int $n Column dimension
-     *
-     * @return Matrix Identity matrix
-     */
-    public function identity($m = null, $n = null)
-    {
-        return $this->diagonal($m, $n, 1);
-    }
-
-    /**
-     * diagonal.
-     *
-     *    Generate a diagonal matrix
-     *
-     * @param int $m Row dimension
-     * @param int $n Column dimension
-     * @param mixed $c Diagonal value
-     *
-     * @return Matrix Diagonal matrix
-     */
-    public function diagonal($m = null, $n = null, $c = 1)
-    {
-        $R = new self($m, $n);
-        for ($i = 0; $i < $m; ++$i) {
-            $R->set($i, $i, $c);
-        }
-
-        return $R;
-    }
-
-    /**
-     * getMatrixByRow.
-     *
-     *    Get a submatrix by row index/range
-     *
-     * @param int $i0 Initial row index
-     * @param int $iF Final row index
-     *
-     * @return Matrix Submatrix
-     */
-    public function getMatrixByRow($i0 = null, $iF = null)
-    {
-        if (is_int($i0)) {
-            if (is_int($iF)) {
-                return $this->getMatrix($i0, 0, $iF + 1, $this->n);
-            }
-
-            return $this->getMatrix($i0, 0, $i0 + 1, $this->n);
-        }
-
-        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-    }
-
-    /**
-     * getMatrixByCol.
-     *
-     *    Get a submatrix by column index/range
-     *
-     * @param int $j0 Initial column index
-     * @param int $jF Final column index
-     *
-     * @return Matrix Submatrix
-     */
-    public function getMatrixByCol($j0 = null, $jF = null)
-    {
-        if (is_int($j0)) {
-            if (is_int($jF)) {
-                return $this->getMatrix(0, $j0, $this->m, $jF + 1);
-            }
-
-            return $this->getMatrix(0, $j0, $this->m, $j0 + 1);
-        }
-
-        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-    }
-
-    /**
-     * transpose.
-     *
-     *    Tranpose matrix
-     *
-     * @return Matrix Transposed matrix
-     */
-    public function transpose()
-    {
-        $R = new self($this->n, $this->m);
-        for ($i = 0; $i < $this->m; ++$i) {
-            for ($j = 0; $j < $this->n; ++$j) {
-                $R->set($j, $i, $this->A[$i][$j]);
-            }
-        }
-
-        return $R;
-    }
-
-    //    function transpose()
-
-    /**
-     * trace.
-     *
-     *    Sum of diagonal elements
-     *
-     * @return float Sum of diagonal elements
-     */
-    public function trace()
-    {
-        $s = 0;
-        $n = min($this->m, $this->n);
-        for ($i = 0; $i < $n; ++$i) {
-            $s += $this->A[$i][$i];
-        }
-
-        return $s;
-    }
-
-    /**
-     * plus.
-     *
-     *    A + B
-     *
-     * @return Matrix Sum
-     */
-    public function plus(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]);
-                }
-            }
-
-            return $M;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * plusEquals.
-     *
-     *    A = A + B
-     *
-     * @return $this
-     */
-    public function plusEquals(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $validValues = true;
-                    $value = $M->get($i, $j);
-                    [$this->A[$i][$j], $validValues] = $this->validateExtractedValue($this->A[$i][$j], $validValues);
-                    [$value, $validValues] = $this->validateExtractedValue($value, /** @scrutinizer ignore-type */ $validValues);
-                    if ($validValues) {
-                        $this->A[$i][$j] += $value;
-                    } else {
-                        $this->A[$i][$j] = ExcelError::NAN();
-                    }
-                }
-            }
-
-            return $this;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * minus.
-     *
-     *    A - B
-     *
-     * @return Matrix Sum
-     */
-    public function minus(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $M->set($i, $j, $M->get($i, $j) - $this->A[$i][$j]);
-                }
-            }
-
-            return $M;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * minusEquals.
-     *
-     *    A = A - B
-     *
-     * @return $this
-     */
-    public function minusEquals(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $validValues = true;
-                    $value = $M->get($i, $j);
-                    [$this->A[$i][$j], $validValues] = $this->validateExtractedValue($this->A[$i][$j], $validValues);
-                    [$value, $validValues] = $this->validateExtractedValue($value, /** @scrutinizer ignore-type */ $validValues);
-                    if ($validValues) {
-                        $this->A[$i][$j] -= $value;
-                    } else {
-                        $this->A[$i][$j] = ExcelError::NAN();
-                    }
-                }
-            }
-
-            return $this;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * arrayTimes.
-     *
-     *    Element-by-element multiplication
-     *    Cij = Aij * Bij
-     *
-     * @return Matrix Matrix Cij
-     */
-    public function arrayTimes(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $M->set($i, $j, $M->get($i, $j) * $this->A[$i][$j]);
-                }
-            }
-
-            return $M;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * arrayTimesEquals.
-     *
-     *    Element-by-element multiplication
-     *    Aij = Aij * Bij
-     *
-     * @return $this
-     */
-    public function arrayTimesEquals(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $validValues = true;
-                    $value = $M->get($i, $j);
-                    [$this->A[$i][$j], $validValues] = $this->validateExtractedValue($this->A[$i][$j], $validValues);
-                    [$value, $validValues] = $this->validateExtractedValue($value, /** @scrutinizer ignore-type */ $validValues);
-                    if ($validValues) {
-                        $this->A[$i][$j] *= $value;
-                    } else {
-                        $this->A[$i][$j] = ExcelError::NAN();
-                    }
-                }
-            }
-
-            return $this;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * arrayRightDivide.
-     *
-     *    Element-by-element right division
-     *    A / B
-     *
-     * @return Matrix Division result
-     */
-    public function arrayRightDivide(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $validValues = true;
-                    $value = $M->get($i, $j);
-                    [$this->A[$i][$j], $validValues] = $this->validateExtractedValue($this->A[$i][$j], $validValues);
-                    [$value, $validValues] = $this->validateExtractedValue($value, /** @scrutinizer ignore-type */ $validValues);
-                    if ($validValues) {
-                        if ($value == 0) {
-                            //    Trap for Divide by Zero error
-                            $M->set($i, $j, /** @scrutinizer ignore-type */ '#DIV/0!');
-                        } else {
-                            $M->set($i, $j, $this->A[$i][$j] / $value);
-                        }
-                    } else {
-                        $M->set($i, $j, /** @scrutinizer ignore-type */ ExcelError::NAN());
-                    }
-                }
-            }
-
-            return $M;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * arrayRightDivideEquals.
-     *
-     *    Element-by-element right division
-     *    Aij = Aij / Bij
-     *
-     * @return Matrix Matrix Aij
-     */
-    public function arrayRightDivideEquals(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $this->A[$i][$j] = $this->A[$i][$j] / $M->get($i, $j);
-                }
-            }
-
-            return $M;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * arrayLeftDivide.
-     *
-     *    Element-by-element Left division
-     *    A / B
-     *
-     * @return Matrix Division result
-     */
-    public function arrayLeftDivide(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $M->set($i, $j, $M->get($i, $j) / $this->A[$i][$j]);
-                }
-            }
-
-            return $M;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * arrayLeftDivideEquals.
-     *
-     *    Element-by-element Left division
-     *    Aij = Aij / Bij
-     *
-     * @return Matrix Matrix Aij
-     */
-    public function arrayLeftDivideEquals(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $this->A[$i][$j] = $M->get($i, $j) / $this->A[$i][$j];
-                }
-            }
-
-            return $M;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * times.
-     *
-     *    Matrix multiplication
-     *
-     * @return Matrix Product
-     */
-    public function times(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $B = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-                    if ($this->n == $B->m) {
-                        $C = new self($this->m, $B->n);
-                        for ($j = 0; $j < $B->n; ++$j) {
-                            $Bcolj = [];
-                            for ($k = 0; $k < $this->n; ++$k) {
-                                $Bcolj[$k] = $B->A[$k][$j];
-                            }
-                            for ($i = 0; $i < $this->m; ++$i) {
-                                $Arowi = $this->A[$i];
-                                $s = 0;
-                                for ($k = 0; $k < $this->n; ++$k) {
-                                    $s += $Arowi[$k] * $Bcolj[$k];
-                                }
-                                $C->A[$i][$j] = $s;
-                            }
-                        }
-
-                        return $C;
-                    }
-
-                    throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
-                case 'array':
-                    $B = new self($args[0]);
-                    if ($this->n == $B->m) {
-                        $C = new self($this->m, $B->n);
-                        for ($i = 0; $i < $C->m; ++$i) {
-                            for ($j = 0; $j < $C->n; ++$j) {
-                                $s = '0';
-                                for ($k = 0; $k < $C->n; ++$k) {
-                                    $s += $this->A[$i][$k] * $B->A[$k][$j];
-                                }
-                                $C->A[$i][$j] = $s;
-                            }
-                        }
-
-                        return $C;
-                    }
-
-                    throw new CalculationException(self::MATRIX_DIMENSION_EXCEPTION);
-                case 'integer':
-                    $C = new self($this->A);
-                    for ($i = 0; $i < $C->m; ++$i) {
-                        for ($j = 0; $j < $C->n; ++$j) {
-                            $C->A[$i][$j] *= $args[0];
-                        }
-                    }
-
-                    return $C;
-                case 'double':
-                    $C = new self($this->m, $this->n);
-                    for ($i = 0; $i < $C->m; ++$i) {
-                        for ($j = 0; $j < $C->n; ++$j) {
-                            $C->A[$i][$j] = $args[0] * $this->A[$i][$j];
-                        }
-                    }
-
-                    return $C;
-                case 'float':
-                    $C = new self($this->A);
-                    for ($i = 0; $i < $C->m; ++$i) {
-                        for ($j = 0; $j < $C->n; ++$j) {
-                            $C->A[$i][$j] *= $args[0];
-                        }
-                    }
-
-                    return $C;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-            }
-        } else {
-            throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-        }
-    }
-
-    /**
-     * power.
-     *
-     *    A = A ^ B
-     *
-     * @return $this
-     */
-    public function power(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    $validValues = true;
-                    $value = $M->get($i, $j);
-                    [$this->A[$i][$j], $validValues] = $this->validateExtractedValue($this->A[$i][$j], $validValues);
-                    [$value, $validValues] = $this->validateExtractedValue($value, /** @scrutinizer ignore-type */ $validValues);
-                    if ($validValues) {
-                        $this->A[$i][$j] = $this->A[$i][$j] ** $value;
-                    } else {
-                        $this->A[$i][$j] = ExcelError::NAN();
-                    }
-                }
-            }
-
-            return $this;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * concat.
-     *
-     *    A = A & B
-     *
-     * @return $this
-     */
-    public function concat(...$args)
-    {
-        if (count($args) > 0) {
-            $match = implode(',', array_map('gettype', $args));
-
-            switch ($match) {
-                case 'object':
-                    if ($args[0] instanceof self) {
-                        $M = $args[0];
-                    } else {
-                        throw new CalculationException(self::ARGUMENT_TYPE_EXCEPTION);
-                    }
-
-                    break;
-                case 'array':
-                    $M = new self($args[0]);
-
-                    break;
-                default:
-                    throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-
-                    break;
-            }
-            $this->checkMatrixDimensions($M);
-            for ($i = 0; $i < $this->m; ++$i) {
-                for ($j = 0; $j < $this->n; ++$j) {
-                    // @phpstan-ignore-next-line
-                    $this->A[$i][$j] = trim($this->A[$i][$j], '"') . trim($M->get($i, $j), '"');
-                }
-            }
-
-            return $this;
-        }
-
-        throw new CalculationException(self::POLYMORPHIC_ARGUMENT_EXCEPTION);
-    }
-
-    /**
-     * Solve A*X = B.
-     *
-     * @param Matrix $B Right hand side
-     *
-     * @return Matrix ... Solution if A is square, least squares solution otherwise
-     */
-    public function solve(self $B)
-    {
-        if ($this->m == $this->n) {
-            $LU = new LUDecomposition($this);
-
-            return $LU->solve($B);
-        }
-        $QR = new QRDecomposition($this);
-
-        return $QR->solve($B);
-    }
-
-    /**
-     * Matrix inverse or pseudoinverse.
-     *
-     * @return Matrix ... Inverse(A) if A is square, pseudoinverse otherwise.
-     */
-    public function inverse()
-    {
-        return $this->solve($this->identity($this->m, $this->m));
-    }
-
-    /**
-     * det.
-     *
-     *    Calculate determinant
-     *
-     * @return float Determinant
-     */
-    public function det()
-    {
-        $L = new LUDecomposition($this);
-
-        return $L->det();
-    }
-
-    /**
-     * @param mixed $value
-     */
-    private function validateExtractedValue($value, bool $validValues): array
-    {
-        if (!is_numeric($value) && is_array($value)) {
-            $value = Functions::flattenArray($value)[0];
-        }
-        if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
-            $value = trim($value, '"');
-            $validValues &= FormattedNumber::convertToNumberIfFormatted($value);
-        }
-
-        return [$value, $validValues];
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
deleted file mode 100644
index 9b51f41..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
+++ /dev/null
@@ -1,245 +0,0 @@
-<?php
-
-namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
-
-use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
-
-/**
- *    For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
- *    orthogonal matrix Q and an n-by-n upper triangular matrix R so that
- *    A = Q*R.
- *
- *    The QR decompostion always exists, even if the matrix does not have
- *    full rank, so the constructor will never fail.  The primary use of the
- *    QR decomposition is in the least squares solution of nonsquare systems
- *    of simultaneous linear equations.  This will fail if isFullRank()
- *    returns false.
- *
- * @author  Paul Meagher
- *
- * @version 1.1
- */
-class QRDecomposition
-{
-    const MATRIX_RANK_EXCEPTION = 'Can only perform operation on full-rank matrix.';
-
-    /**
-     * Array for internal storage of decomposition.
-     *
-     * @var array
-     */
-    private $QR = [];
-
-    /**
-     * Row dimension.
-     *
-     * @var int
-     */
-    private $m;
-
-    /**
-     * Column dimension.
-     *
-     * @var int
-     */
-    private $n;
-
-    /**
-     * Array for internal storage of diagonal of R.
-     *
-     * @var array
-     */
-    private $Rdiag = [];
-
-    /**
-     * QR Decomposition computed by Householder reflections.
-     *
-     * @param Matrix $A Rectangular matrix
-     */
-    public function __construct(Matrix $A)
-    {
-        // Initialize.
-        $this->QR = $A->getArray();
-        $this->m = $A->getRowDimension();
-        $this->n = $A->getColumnDimension();
-        // Main loop.
-        for ($k = 0; $k < $this->n; ++$k) {
-            // Compute 2-norm of k-th column without under/overflow.
-            $nrm = 0.0;
-            for ($i = $k; $i < $this->m; ++$i) {
-                $nrm = hypo($nrm, $this->QR[$i][$k]);
-            }
-            if ($nrm != 0.0) {
-                // Form k-th Householder vector.
-                if ($this->QR[$k][$k] < 0) {
-                    $nrm = -$nrm;
-                }
-                for ($i = $k; $i < $this->m; ++$i) {
-                    $this->QR[$i][$k] /= $nrm;
-                }
-                $this->QR[$k][$k] += 1.0;
-                // Apply transformation to remaining columns.
-                for ($j = $k + 1; $j < $this->n; ++$j) {
-                    $s = 0.0;
-                    for ($i = $k; $i < $this->m; ++$i) {
-                        $s += $this->QR[$i][$k] * $this->QR[$i][$j];
-                    }
-                    $s = -$s / $this->QR[$k][$k];
-                    for ($i = $k; $i < $this->m; ++$i) {
-                        $this->QR[$i][$j] += $s * $this->QR[$i][$k];
-                    }
-                }
-            }
-            $this->Rdiag[$k] = -$nrm;
-        }
-    }
-
-    //    function __construct()
-
-    /**
-     *    Is the matrix full rank?
-     *
-     * @return bool true if R, and hence A, has full rank, else false
-     */
-    public function isFullRank()
-    {
-        for ($j = 0; $j < $this->n; ++$j) {
-            if ($this->Rdiag[$j] == 0) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    //    function isFullRank()
-
-    /**
-     * Return the Householder vectors.
-     *
-     * @return Matrix Lower trapezoidal matrix whose columns define the reflections
-     */
-    public function getH()
-    {
-        $H = [];
-        for ($i = 0; $i < $this->m; ++$i) {
-            for ($j = 0; $j < $this->n; ++$j) {
-                if ($i >= $j) {
-                    $H[$i][$j] = $this->QR[$i][$j];
-                } else {
-                    $H[$i][$j] = 0.0;
-                }
-            }
-        }
-
-        return new Matrix($H);
-    }
-
-    //    function getH()
-
-    /**
-     * Return the upper triangular factor.
-     *
-     * @return Matrix upper triangular factor
-     */
-    public function getR()
-    {
-        $R = [];
-        for ($i = 0; $i < $this->n; ++$i) {
-            for ($j = 0; $j < $this->n; ++$j) {
-                if ($i < $j) {
-                    $R[$i][$j] = $this->QR[$i][$j];
-                } elseif ($i == $j) {
-                    $R[$i][$j] = $this->Rdiag[$i];
-                } else {
-                    $R[$i][$j] = 0.0;
-                }
-            }
-        }
-
-        return new Matrix($R);
-    }
-
-    //    function getR()
-
-    /**
-     * Generate and return the (economy-sized) orthogonal factor.
-     *
-     * @return Matrix orthogonal factor
-     */
-    public function getQ()
-    {
-        $Q = [];
-        for ($k = $this->n - 1; $k >= 0; --$k) {
-            for ($i = 0; $i < $this->m; ++$i) {
-                $Q[$i][$k] = 0.0;
-            }
-            $Q[$k][$k] = 1.0;
-            for ($j = $k; $j < $this->n; ++$j) {
-                if ($this->QR[$k][$k] != 0) {
-                    $s = 0.0;
-                    for ($i = $k; $i < $this->m; ++$i) {
-                        $s += $this->QR[$i][$k] * $Q[$i][$j];
-                    }
-                    $s = -$s / $this->QR[$k][$k];
-                    for ($i = $k; $i < $this->m; ++$i) {
-                        $Q[$i][$j] += $s * $this->QR[$i][$k];
-                    }
-                }
-            }
-        }
-
-        return new Matrix($Q);
-    }
-
-    //    function getQ()
-
-    /**
-     * Least squares solution of A*X = B.
-     *
-     * @param Matrix $B a Matrix with as many rows as A and any number of columns
-     *
-     * @return Matrix matrix that minimizes the two norm of Q*R*X-B
-     */
-    public function solve(Matrix $B)
-    {
-        if ($B->getRowDimension() == $this->m) {
-            if ($this->isFullRank()) {
-                // Copy right hand side
-                $nx = $B->getColumnDimension();
-                $X = $B->getArray();
-                // Compute Y = transpose(Q)*B
-                for ($k = 0; $k < $this->n; ++$k) {
-                    for ($j = 0; $j < $nx; ++$j) {
-                        $s = 0.0;
-                        for ($i = $k; $i < $this->m; ++$i) {
-                            $s += $this->QR[$i][$k] * $X[$i][$j];
-                        }
-                        $s = -$s / $this->QR[$k][$k];
-                        for ($i = $k; $i < $this->m; ++$i) {
-                            $X[$i][$j] += $s * $this->QR[$i][$k];
-                        }
-                    }
-                }
-                // Solve R*X = Y;
-                for ($k = $this->n - 1; $k >= 0; --$k) {
-                    for ($j = 0; $j < $nx; ++$j) {
-                        $X[$k][$j] /= $this->Rdiag[$k];
-                    }
-                    for ($i = 0; $i < $k; ++$i) {
-                        for ($j = 0; $j < $nx; ++$j) {
-                            $X[$i][$j] -= $X[$k][$j] * $this->QR[$i][$k];
-                        }
-                    }
-                }
-                $X = new Matrix($X);
-
-                return $X->getMatrix(0, $this->n - 1, 0, $nx);
-            }
-
-            throw new CalculationException(self::MATRIX_RANK_EXCEPTION);
-        }
-
-        throw new CalculationException(Matrix::MATRIX_DIMENSION_EXCEPTION);
-    }
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php
deleted file mode 100644
index 49877b2..0000000
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/utils/Maths.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- *    Pythagorean Theorem:.
- *
- *    a = 3
- *    b = 4
- *    r = sqrt(square(a) + square(b))
- *    r = 5
- *
- *    r = sqrt(a^2 + b^2) without under/overflow.
- *
- * @param mixed $a
- * @param mixed $b
- *
- * @return float
- */
-function hypo($a, $b)
-{
-    if (abs($a) > abs($b)) {
-        $r = $b / $a;
-        $r = abs($a) * sqrt(1 + $r * $r);
-    } elseif ($b != 0) {
-        $r = $a / $b;
-        $r = abs($b) * sqrt(1 + $r * $r);
-    } else {
-        $r = 0.0;
-    }
-
-    return $r;
-}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
index 815b1c1..dbf6df8 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
@@ -58,65 +58,57 @@ class OLE
 
     /**
      * Array of PPS's found on the OLE container.
-     *
-     * @var array
      */
-    public $_list = [];
+    public array $_list = [];
 
     /**
      * Root directory of OLE container.
-     *
-     * @var Root
      */
-    public $root;
+    public Root $root;
 
     /**
      * Big Block Allocation Table.
      *
      * @var array (blockId => nextBlockId)
      */
-    public $bbat;
+    public array $bbat;
 
     /**
      * Short Block Allocation Table.
      *
      * @var array (blockId => nextBlockId)
      */
-    public $sbat;
+    public array $sbat;
 
     /**
      * Size of big blocks. This is usually 512.
      *
      * @var int number of octets per block
      */
-    public $bigBlockSize;
+    public int $bigBlockSize;
 
     /**
      * Size of small blocks. This is usually 64.
      *
      * @var int number of octets per block
      */
-    public $smallBlockSize;
+    public int $smallBlockSize;
 
     /**
      * Threshold for big blocks.
-     *
-     * @var int
      */
-    public $bigBlockThreshold;
+    public int $bigBlockThreshold;
 
     /**
      * Reads an OLE container from the contents of the file given.
      *
      * @acces public
      *
-     * @param string $filename
-     *
      * @return bool true on success, PEAR_Error on failure
      */
-    public function read($filename)
+    public function read(string $filename): bool
     {
-        $fh = fopen($filename, 'rb');
+        $fh = @fopen($filename, 'rb');
         if ($fh === false) {
             throw new ReaderException("Can't open file $filename");
         }
@@ -200,10 +192,8 @@ class OLE
 
     /**
      * @param int $blockId byte offset from beginning of file
-     *
-     * @return int
      */
-    public function getBlockOffset($blockId)
+    public function getBlockOffset(int $blockId): int
     {
         return 512 + $blockId * $this->bigBlockSize;
     }
@@ -239,20 +229,22 @@ class OLE
             $path .= '&blockId=' . $blockIdOrPps;
         }
 
-        return fopen($path, 'rb');
+        $resource = fopen($path, 'rb');
+        if ($resource === false) {
+            throw new Exception("Unable to open stream $path");
+        }
+
+        return $resource;
     }
 
     /**
      * Reads a signed char.
      *
      * @param resource $fileHandle file handle
-     *
-     * @return int
      */
-    private static function readInt1($fileHandle)
+    private static function readInt1($fileHandle): int
     {
-        // @phpstan-ignore-next-line
-        [, $tmp] = unpack('c', fread($fileHandle, 1));
+        [, $tmp] = unpack('c', fread($fileHandle, 1) ?: '') ?: [0, 0];
 
         return $tmp;
     }
@@ -261,28 +253,29 @@ class OLE
      * Reads an unsigned short (2 octets).
      *
      * @param resource $fileHandle file handle
-     *
-     * @return int
      */
-    private static function readInt2($fileHandle)
+    private static function readInt2($fileHandle): int
     {
-        // @phpstan-ignore-next-line
-        [, $tmp] = unpack('v', fread($fileHandle, 2));
+        [, $tmp] = unpack('v', fread($fileHandle, 2) ?: '') ?: [0, 0];
 
         return $tmp;
     }
 
+    private const SIGNED_4OCTET_LIMIT = 2147483648;
+
+    private const SIGNED_4OCTET_SUBTRACT = 2 * self::SIGNED_4OCTET_LIMIT;
+
     /**
-     * Reads an unsigned long (4 octets).
+     * Reads long (4 octets), interpreted as if signed on 32-bit system.
      *
      * @param resource $fileHandle file handle
-     *
-     * @return int
      */
-    private static function readInt4($fileHandle)
+    private static function readInt4($fileHandle): int
     {
-        // @phpstan-ignore-next-line
-        [, $tmp] = unpack('V', fread($fileHandle, 4));
+        [, $tmp] = unpack('V', fread($fileHandle, 4) ?: '') ?: [0, 0];
+        if ($tmp >= self::SIGNED_4OCTET_LIMIT) {
+            $tmp -= self::SIGNED_4OCTET_SUBTRACT;
+        }
 
         return $tmp;
     }
@@ -295,12 +288,12 @@ class OLE
      *
      * @return bool true on success, PEAR_Error on failure
      */
-    public function readPpsWks($blockId)
+    public function readPpsWks(int $blockId): bool
     {
         $fh = $this->getStream($blockId);
         for ($pos = 0; true; $pos += 128) {
             fseek($fh, $pos, SEEK_SET);
-            $nameUtf16 = fread($fh, 64);
+            $nameUtf16 = (string) fread($fh, 64);
             $nameLength = self::readInt2($fh);
             $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
             // Simple conversion from UTF-16LE to ISO-8859-1
@@ -308,7 +301,7 @@ class OLE
             $type = self::readInt1($fh);
             switch ($type) {
                 case self::OLE_PPS_TYPE_ROOT:
-                    $pps = new OLE\PPS\Root(null, null, []);
+                    $pps = new Root(null, null, []);
                     $this->root = $pps;
 
                     break;
@@ -330,8 +323,8 @@ class OLE
             $pps->NextPps = self::readInt4($fh);
             $pps->DirPps = self::readInt4($fh);
             fseek($fh, 20, SEEK_CUR);
-            $pps->Time1st = self::OLE2LocalDate(fread($fh, 8));
-            $pps->Time2nd = self::OLE2LocalDate(fread($fh, 8));
+            $pps->Time1st = self::OLE2LocalDate((string) fread($fh, 8));
+            $pps->Time2nd = self::OLE2LocalDate((string) fread($fh, 8));
             $pps->startBlock = self::readInt4($fh);
             $pps->Size = self::readInt4($fh);
             $pps->No = count($this->_list);
@@ -372,16 +365,16 @@ class OLE
      *
      * @return bool Whether the PPS tree for the given PPS is complete
      */
-    private function ppsTreeComplete($index)
+    private function ppsTreeComplete(int $index): bool
     {
-        return isset($this->_list[$index]) &&
-            ($pps = $this->_list[$index]) &&
-            ($pps->PrevPps == -1 ||
-                $this->ppsTreeComplete($pps->PrevPps)) &&
-            ($pps->NextPps == -1 ||
-                $this->ppsTreeComplete($pps->NextPps)) &&
-            ($pps->DirPps == -1 ||
-                $this->ppsTreeComplete($pps->DirPps));
+        return isset($this->_list[$index])
+            && ($pps = $this->_list[$index])
+            && ($pps->PrevPps == -1
+                || $this->ppsTreeComplete($pps->PrevPps))
+            && ($pps->NextPps == -1
+                || $this->ppsTreeComplete($pps->NextPps))
+            && ($pps->DirPps == -1
+                || $this->ppsTreeComplete($pps->DirPps));
     }
 
     /**
@@ -392,7 +385,7 @@ class OLE
      *
      * @return bool true if it's a File PPS, false otherwise
      */
-    public function isFile($index)
+    public function isFile(int $index): bool
     {
         if (isset($this->_list[$index])) {
             return $this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE;
@@ -409,7 +402,7 @@ class OLE
      *
      * @return bool true if it's a Root PPS, false otherwise
      */
-    public function isRoot($index)
+    public function isRoot(int $index): bool
     {
         if (isset($this->_list[$index])) {
             return $this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT;
@@ -423,7 +416,7 @@ class OLE
      *
      * @return int The total number of PPS's found in the OLE container
      */
-    public function ppsTotal()
+    public function ppsTotal(): int
     {
         return count($this->_list);
     }
@@ -441,14 +434,14 @@ class OLE
      *
      * @see OLE_PPS_File::getStream()
      */
-    public function getData($index, $position, $length)
+    public function getData(int $index, int $position, int $length): string
     {
         // if position is not valid return empty string
         if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
             return '';
         }
         $fh = $this->getStream($this->_list[$index]);
-        $data = stream_get_contents($fh, $length, $position);
+        $data = (string) stream_get_contents($fh, $length, $position);
         fclose($fh);
 
         return $data;
@@ -462,7 +455,7 @@ class OLE
      *
      * @return int The amount of bytes in data the PPS has
      */
-    public function getDataLength($index)
+    public function getDataLength(int $index): int
     {
         if (isset($this->_list[$index])) {
             return $this->_list[$index]->Size;
@@ -478,7 +471,7 @@ class OLE
      *
      * @return string The string in Unicode
      */
-    public static function ascToUcs($ascii)
+    public static function ascToUcs(string $ascii): string
     {
         $rawname = '';
         $iMax = strlen($ascii);
@@ -498,7 +491,7 @@ class OLE
      *
      * @return string The string for the OLE container
      */
-    public static function localDateToOLE($date)
+    public static function localDateToOLE($date): string
     {
         if (!$date) {
             return "\x00\x00\x00\x00\x00\x00\x00\x00";
@@ -533,14 +526,14 @@ class OLE
      *
      * @return float|int The Unix timestamp corresponding to the string
      */
-    public static function OLE2LocalDate($oleTimestamp)
+    public static function OLE2LocalDate(string $oleTimestamp)
     {
         if (strlen($oleTimestamp) != 8) {
             throw new ReaderException('Expecting 8 byte string');
         }
 
         // convert to units of 100 ns since 1601:
-        $unpackedTimestamp = unpack('v4', $oleTimestamp);
+        $unpackedTimestamp = unpack('v4', $oleTimestamp) ?: [];
         $timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3];
         $timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1];
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
index ee93c05..61bd6ac 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php
@@ -6,33 +6,30 @@ use PhpOffice\PhpSpreadsheet\Shared\OLE;
 
 class ChainedBlockStream
 {
+    /** @var mixed */
+    public $context;
+
     /**
      * The OLE container of the file that is being read.
-     *
-     * @var null|OLE
      */
-    public $ole;
+    public ?OLE $ole = null;
 
     /**
      * Parameters specified by fopen().
-     *
-     * @var array
      */
-    public $params;
+    public array $params = [];
 
     /**
      * The binary data of the file.
-     *
-     * @var string
      */
-    public $data;
+    public string $data;
 
     /**
      * The file pointer.
      *
      * @var int byte offset
      */
-    public $pos;
+    public int $pos = 0;
 
     /**
      * Implements support for fopen().
@@ -42,13 +39,13 @@ class ChainedBlockStream
      *                                    ole-chainedblockstream://oleInstanceId=1
      * @param string $mode only "r" is supported
      * @param int $options mask of STREAM_REPORT_ERRORS and STREAM_USE_PATH
-     * @param string $openedPath absolute path of the opened stream (out parameter)
+     * @param ?string $openedPath absolute path of the opened stream (out parameter)
      *
      * @return bool true on success
      */
-    public function stream_open($path, $mode, $options, &$openedPath) // @codingStandardsIgnoreLine
+    public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool // @codingStandardsIgnoreLine
     {
-        if ($mode != 'r') {
+        if ($mode[0] !== 'r') {
             if ($options & STREAM_REPORT_ERRORS) {
                 trigger_error('Only reading is supported', E_USER_WARNING);
             }
@@ -114,12 +111,12 @@ class ChainedBlockStream
      *
      * @return false|string
      */
-    public function stream_read($count) // @codingStandardsIgnoreLine
+    public function stream_read(int $count): bool|string // @codingStandardsIgnoreLine
     {
         if ($this->stream_eof()) {
             return false;
         }
-        $s = substr($this->data, $this->pos, $count);
+        $s = substr($this->data, (int) $this->pos, $count);
         $this->pos += $count;
 
         return $s;
@@ -130,7 +127,7 @@ class ChainedBlockStream
      *
      * @return bool TRUE if the file pointer is at EOF; otherwise FALSE
      */
-    public function stream_eof() // @codingStandardsIgnoreLine
+    public function stream_eof(): bool // @codingStandardsIgnoreLine
     {
         return $this->pos >= strlen($this->data);
     }
@@ -138,10 +135,8 @@ class ChainedBlockStream
     /**
      * Returns the position of the file pointer, i.e. its offset into the file
      * stream. Implements support for ftell().
-     *
-     * @return int
      */
-    public function stream_tell() // @codingStandardsIgnoreLine
+    public function stream_tell(): int // @codingStandardsIgnoreLine
     {
         return $this->pos;
     }
@@ -151,17 +146,14 @@ class ChainedBlockStream
      *
      * @param int $offset byte offset
      * @param int $whence SEEK_SET, SEEK_CUR or SEEK_END
-     *
-     * @return bool
      */
-    public function stream_seek($offset, $whence) // @codingStandardsIgnoreLine
+    public function stream_seek(int $offset, int $whence): bool // @codingStandardsIgnoreLine
     {
         if ($whence == SEEK_SET && $offset >= 0) {
             $this->pos = $offset;
         } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) {
             $this->pos += $offset;
-        // @phpstan-ignore-next-line
-        } elseif ($whence == SEEK_END && -$offset <= count(/** @scrutinizer ignore-type */ $this->data)) {
+        } elseif ($whence == SEEK_END && -$offset <= count($this->data)) { // @phpstan-ignore-line
             $this->pos = strlen($this->data) + $offset;
         } else {
             return false;
@@ -173,10 +165,8 @@ class ChainedBlockStream
     /**
      * Implements support for fstat(). Currently the only supported field is
      * "size".
-     *
-     * @return array
      */
-    public function stream_stat() // @codingStandardsIgnoreLine
+    public function stream_stat(): array // @codingStandardsIgnoreLine
     {
         return [
             'size' => strlen($this->data),
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
index d3d86f5..3a77c78 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
@@ -29,128 +29,100 @@ use PhpOffice\PhpSpreadsheet\Shared\OLE;
  */
 class PPS
 {
+    private const ALL_ONE_BITS = (PHP_INT_SIZE > 4) ? 0xFFFFFFFF : -1;
+
     /**
      * The PPS index.
-     *
-     * @var int
      */
-    public $No;
+    public int $No;
 
     /**
      * The PPS name (in Unicode).
-     *
-     * @var string
      */
-    public $Name;
+    public string $Name;
 
     /**
      * The PPS type. Dir, Root or File.
-     *
-     * @var int
      */
-    public $Type;
+    public int $Type;
 
     /**
      * The index of the previous PPS.
-     *
-     * @var int
      */
-    public $PrevPps;
+    public int $PrevPps;
 
     /**
      * The index of the next PPS.
-     *
-     * @var int
      */
-    public $NextPps;
+    public int $NextPps;
 
     /**
      * The index of it's first child if this is a Dir or Root PPS.
-     *
-     * @var int
      */
-    public $DirPps;
+    public int $DirPps;
 
     /**
      * A timestamp.
-     *
-     * @var float|int
      */
-    public $Time1st;
+    public float|int $Time1st;
 
     /**
      * A timestamp.
-     *
-     * @var float|int
      */
-    public $Time2nd;
+    public float|int $Time2nd;
 
     /**
      * Starting block (small or big) for this PPS's data  inside the container.
-     *
-     * @var int
      */
-    public $startBlock;
+    public ?int $startBlock = null;
 
     /**
      * The size of the PPS's data (in bytes).
-     *
-     * @var int
      */
-    public $Size;
+    public int $Size;
 
     /**
      * The PPS's data (only used if it's not using a temporary file).
-     *
-     * @var string
      */
-    public $_data;
+    public string $_data = '';
 
     /**
      * Array of child PPS's (only used by Root and Dir PPS's).
-     *
-     * @var array
      */
-    public $children = [];
+    public array $children = [];
 
     /**
      * Pointer to OLE container.
-     *
-     * @var OLE
      */
-    public $ole;
+    public OLE $ole;
 
     /**
      * The constructor.
      *
-     * @param int $No The PPS index
-     * @param string $name The PPS name
-     * @param int $type The PPS type. Dir, Root or File
-     * @param int $prev The index of the previous PPS
-     * @param int $next The index of the next PPS
-     * @param int $dir The index of it's first child if this is a Dir or Root PPS
+     * @param ?int $No The PPS index
+     * @param ?string $name The PPS name
+     * @param ?int $type The PPS type. Dir, Root or File
+     * @param ?int $prev The index of the previous PPS
+     * @param ?int $next The index of the next PPS
+     * @param ?int $dir The index of it's first child if this is a Dir or Root PPS
      * @param null|float|int $time_1st A timestamp
      * @param null|float|int $time_2nd A timestamp
-     * @param string $data The (usually binary) source data of the PPS
+     * @param ?string $data The (usually binary) source data of the PPS
      * @param array $children Array containing children PPS for this PPS
      */
-    public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children)
+    public function __construct(?int $No, ?string $name, ?int $type, ?int $prev, ?int $next, ?int $dir, $time_1st, $time_2nd, ?string $data, array $children)
     {
-        $this->No = $No;
-        $this->Name = $name;
-        $this->Type = $type;
-        $this->PrevPps = $prev;
-        $this->NextPps = $next;
-        $this->DirPps = $dir;
+        $this->No = (int) $No;
+        $this->Name = (string) $name;
+        $this->Type = (int) $type;
+        $this->PrevPps = (int) $prev;
+        $this->NextPps = (int) $next;
+        $this->DirPps = (int) $dir;
         $this->Time1st = $time_1st ?? 0;
         $this->Time2nd = $time_2nd ?? 0;
-        $this->_data = $data;
+        $this->_data = (string) $data;
         $this->children = $children;
-        if ($data != '') {
-            $this->Size = strlen($data);
-        } else {
-            $this->Size = 0;
-        }
+        $this->Size = strlen((string) $data);
     }
 
     /**
@@ -158,11 +130,11 @@ class PPS
      *
      * @return int The amount of data (in bytes)
      */
-    public function getDataLen()
+    public function getDataLen(): int
     {
-        if (!isset($this->_data)) {
-            return 0;
-        }
+        //if (!isset($this->_data)) {
+        //    return 0;
+        //}
 
         return strlen($this->_data);
     }
@@ -172,7 +144,7 @@ class PPS
      *
      * @return string The binary string
      */
-    public function getPpsWk()
+    public function getPpsWk(): string
     {
         $ret = str_pad($this->Name, 64, "\x00");
 
@@ -202,22 +174,20 @@ class PPS
      *
      * @param array $raList Reference to the array of PPS's for the whole OLE
      *                          container
-     * @param mixed $to_save
-     * @param mixed $depth
      *
      * @return int The index for this PPS
      */
-    public static function savePpsSetPnt(&$raList, $to_save, $depth = 0)
+    public static function savePpsSetPnt(array &$raList, mixed $to_save, int $depth = 0): int
     {
         if (!is_array($to_save) || (empty($to_save))) {
-            return 0xFFFFFFFF;
+            return self::ALL_ONE_BITS;
         } elseif (count($to_save) == 1) {
             $cnt = count($raList);
             // If the first entry, it's the root... Don't clone it!
             $raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0];
             $raList[$cnt]->No = $cnt;
-            $raList[$cnt]->PrevPps = 0xFFFFFFFF;
-            $raList[$cnt]->NextPps = 0xFFFFFFFF;
+            $raList[$cnt]->PrevPps = self::ALL_ONE_BITS;
+            $raList[$cnt]->NextPps = self::ALL_ONE_BITS;
             $raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
         } else {
             $iPos = (int) floor(count($to_save) / 2);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php
index dd1cda2..0798e3b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php
@@ -37,17 +37,15 @@ class File extends PPS
      *
      * @see OLE::ascToUcs()
      */
-    public function __construct($name)
+    public function __construct(string $name)
     {
         parent::__construct(null, $name, OLE::OLE_PPS_TYPE_FILE, null, null, null, null, null, '', []);
     }
 
     /**
      * Initialization method. Has to be called right after OLE_PPS_File().
-     *
-     * @return mixed true on success
      */
-    public function init()
+    public function init(): bool
     {
         return true;
     }
@@ -57,7 +55,7 @@ class File extends PPS
      *
      * @param string $data The data to append
      */
-    public function append($data): void
+    public function append(string $data): void
     {
         $this->_data .= $data;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
index 3fe8af2..64de77f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
@@ -35,22 +35,16 @@ class Root extends PPS
      */
     private $fileHandle;
 
-    /**
-     * @var int
-     */
-    private $smallBlockSize;
+    private ?int $smallBlockSize = null;
 
-    /**
-     * @var int
-     */
-    private $bigBlockSize;
+    private ?int $bigBlockSize = null;
 
     /**
      * @param null|float|int $time_1st A timestamp
      * @param null|float|int $time_2nd A timestamp
      * @param File[] $raChild
      */
-    public function __construct($time_1st, $time_2nd, $raChild)
+    public function __construct($time_1st, $time_2nd, array $raChild)
     {
         parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild);
     }
@@ -66,7 +60,7 @@ class Root extends PPS
      *
      * @return bool true on success
      */
-    public function save($fileHandle)
+    public function save($fileHandle): bool
     {
         $this->fileHandle = $fileHandle;
 
@@ -106,7 +100,7 @@ class Root extends PPS
      *
      * @return float[] The array of numbers
      */
-    private function calcSize(&$raList)
+    private function calcSize(array &$raList): array
     {
         // Calculate Basic Setting
         [$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0];
@@ -119,16 +113,16 @@ class Root extends PPS
                     $iSBcnt += floor($raList[$i]->Size / $this->smallBlockSize)
                         + (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
                 } else {
-                    $iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize) +
-                        (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
+                    $iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize)
+                        + (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
                 }
             }
         }
         $iSmallLen = $iSBcnt * $this->smallBlockSize;
         $iSlCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
         $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0);
-        $iBBcnt += (floor($iSmallLen / $this->bigBlockSize) +
-            (($iSmallLen % $this->bigBlockSize) ? 1 : 0));
+        $iBBcnt += (floor($iSmallLen / $this->bigBlockSize)
+            + (($iSmallLen % $this->bigBlockSize) ? 1 : 0));
         $iCnt = count($raList);
         $iBdCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
         $iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0));
@@ -141,11 +135,9 @@ class Root extends PPS
      *
      * @param int $i2 The argument
      *
-     * @return float
-     *
      * @see save()
      */
-    private static function adjust2($i2)
+    private static function adjust2(int $i2): float
     {
         $iWk = log($i2) / log(2);
 
@@ -154,12 +146,8 @@ class Root extends PPS
 
     /**
      * Save OLE header.
-     *
-     * @param int $iSBDcnt
-     * @param int $iBBcnt
-     * @param int $iPPScnt
      */
-    private function saveHeader($iSBDcnt, $iBBcnt, $iPPScnt): void
+    private function saveHeader(int $iSBDcnt, int $iBBcnt, int $iPPScnt): void
     {
         $FILE = $this->fileHandle;
 
@@ -194,7 +182,7 @@ class Root extends PPS
             . "\x00\x00\x00\x00"
             . "\x00\x00\x00\x00"
             . "\x00\x00\x00\x00"
-            . pack('v', 0x3b)
+            . pack('v', 0x3B)
             . pack('v', 0x03)
             . pack('v', -2)
             . pack('v', 9)
@@ -235,10 +223,9 @@ class Root extends PPS
     /**
      * Saving big data (PPS's with data bigger than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
      *
-     * @param int $iStBlk
      * @param array $raList Reference to array of PPS's
      */
-    private function saveBigData($iStBlk, &$raList): void
+    private function saveBigData(int $iStBlk, array &$raList): void
     {
         $FILE = $this->fileHandle;
 
@@ -255,9 +242,9 @@ class Root extends PPS
                     }
                     // Set For PPS
                     $raList[$i]->startBlock = $iStBlk;
-                    $iStBlk +=
-                        (floor($raList[$i]->Size / $this->bigBlockSize) +
-                            (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
+                    $iStBlk
+                        += (floor($raList[$i]->Size / $this->bigBlockSize)
+                            + (($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
                 }
             }
         }
@@ -267,10 +254,8 @@ class Root extends PPS
      * get small data (PPS's with data smaller than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
      *
      * @param array $raList Reference to array of PPS's
-     *
-     * @return string
      */
-    private function makeSmallData(&$raList)
+    private function makeSmallData(array &$raList): string
     {
         $sRes = '';
         $FILE = $this->fileHandle;
@@ -320,7 +305,7 @@ class Root extends PPS
      *
      * @param array $raList Reference to an array with all PPS's
      */
-    private function savePps(&$raList): void
+    private function savePps(array &$raList): void
     {
         // Save each PPS WK
         $iC = count($raList);
@@ -337,12 +322,8 @@ class Root extends PPS
 
     /**
      * Saving Big Block Depot.
-     *
-     * @param int $iSbdSize
-     * @param int $iBsize
-     * @param int $iPpsCnt
      */
-    private function saveBbd($iSbdSize, $iBsize, $iPpsCnt): void
+    private function saveBbd(int $iSbdSize, int $iBsize, int $iPpsCnt): void
     {
         $FILE = $this->fileHandle;
         // Calculate Basic Setting
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php
index b3e35c5..645dbf7 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php
@@ -6,7 +6,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
 
 class OLERead
 {
-    private $data = '';
+    private string $data = '';
 
     // Size of a sector = 512 bytes
     const BIG_BLOCK_SIZE = 0x200;
@@ -21,12 +21,12 @@ class OLERead
     const SMALL_BLOCK_THRESHOLD = 0x1000;
 
     // header offsets
-    const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2c;
+    const NUM_BIG_BLOCK_DEPOT_BLOCKS_POS = 0x2C;
     const ROOT_START_BLOCK_POS = 0x30;
-    const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3c;
+    const SMALL_BLOCK_DEPOT_BLOCK_POS = 0x3C;
     const EXTENSION_BLOCK_POS = 0x44;
     const NUM_EXTENSION_BLOCK_POS = 0x48;
-    const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4c;
+    const BIG_BLOCK_DEPOT_BLOCKS_POS = 0x4C;
 
     // property storage offsets (directory offsets)
     const SIZE_OF_NAME_POS = 0x40;
@@ -34,61 +34,31 @@ class OLERead
     const START_BLOCK_POS = 0x74;
     const SIZE_POS = 0x78;
 
-    public $wrkbook;
+    public ?int $wrkbook = null;
 
-    public $summaryInformation;
+    public ?int $summaryInformation = null;
 
-    public $documentSummaryInformation;
+    public ?int $documentSummaryInformation = null;
 
-    /**
-     * @var int
-     */
-    private $numBigBlockDepotBlocks;
+    private int $numBigBlockDepotBlocks;
 
-    /**
-     * @var int
-     */
-    private $rootStartBlock;
+    private int $rootStartBlock;
 
-    /**
-     * @var int
-     */
-    private $sbdStartBlock;
+    private int $sbdStartBlock;
 
-    /**
-     * @var int
-     */
-    private $extensionBlock;
+    private int $extensionBlock;
 
-    /**
-     * @var int
-     */
-    private $numExtensionBlocks;
+    private int $numExtensionBlocks;
 
-    /**
-     * @var string
-     */
-    private $bigBlockChain;
+    private string $bigBlockChain;
 
-    /**
-     * @var string
-     */
-    private $smallBlockChain;
+    private string $smallBlockChain;
 
-    /**
-     * @var string
-     */
-    private $entry;
+    private string $entry;
 
-    /**
-     * @var int
-     */
-    private $rootentry;
+    private int $rootentry;
 
-    /**
-     * @var array
-     */
-    private $props = [];
+    private array $props = [];
 
     /**
      * Read the file.
@@ -99,16 +69,16 @@ class OLERead
 
         // Get the file identifier
         // Don't bother reading the whole file until we know it's a valid OLE file
-        $this->data = file_get_contents($filename, false, null, 0, 8);
+        $this->data = (string) file_get_contents($filename, false, null, 0, 8);
 
         // Check OLE identifier
-        $identifierOle = pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1);
+        $identifierOle = pack('CCCCCCCC', 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1);
         if ($this->data != $identifierOle) {
             throw new ReaderException('The filename ' . $filename . ' is not recognised as an OLE file');
         }
 
         // Get the file data
-        $this->data = file_get_contents($filename);
+        $this->data = (string) file_get_contents($filename);
 
         // Total number of sectors used for the SAT
         $this->numBigBlockDepotBlocks = self::getInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
@@ -130,7 +100,7 @@ class OLERead
 
         $bbdBlocks = $this->numBigBlockDepotBlocks;
 
-        if ($this->numExtensionBlocks != 0) {
+        if ($this->numExtensionBlocks !== 0) {
             $bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
         }
 
@@ -184,12 +154,8 @@ class OLERead
 
     /**
      * Extract binary stream data.
-     *
-     * @param ?int $stream
-     *
-     * @return null|string
      */
-    public function getStream($stream)
+    public function getStream(?int $stream): ?string
     {
         if ($stream === null) {
             return null;
@@ -238,7 +204,7 @@ class OLERead
      *
      * @return string Data for standard stream
      */
-    private function readData($block)
+    private function readData(int $block): string
     {
         $data = '';
 
@@ -312,13 +278,8 @@ class OLERead
 
     /**
      * Read 4 bytes of data at specified position.
-     *
-     * @param string $data
-     * @param int $pos
-     *
-     * @return int
      */
-    private static function getInt4d($data, $pos)
+    private static function getInt4d(string $data, int $pos): int
     {
         if ($pos < 0) {
             // Invalid position
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php
index e9414f9..fcdbc98 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php
@@ -50,9 +50,6 @@ class PasswordHasher
      * Daniel Rentz of OpenOffice and the PEAR package
      * Spreadsheet_Excel_Writer by Xavier Noguer <xnoguer@rezebra.com>.
      *
-     * Scrutinizer will squawk at the use of bitwise operations here,
-     * but it should ultimately pass.
-     *
      * @param string $password Password to hash
      */
     private static function defaultHashPassword(string $password): string
@@ -63,7 +60,7 @@ class PasswordHasher
         for ($i = $pwlen; $i >= 0; --$i) {
             $intermediate1 = (($verifier & 0x4000) === 0) ? 0 : 1;
             $intermediate2 = 2 * $verifier;
-            $intermediate2 = $intermediate2 & 0x7fff;
+            $intermediate2 = $intermediate2 & 0x7FFF;
             $intermediate3 = $intermediate1 | $intermediate2;
             $verifier = $intermediate3 ^ ord($passwordArray[$i]);
         }
@@ -99,7 +96,7 @@ class PasswordHasher
         $saltValue = base64_decode($salt);
         $encodedPassword = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
 
-        $hashValue = hash($phpAlgorithm, $saltValue . /** @scrutinizer ignore-type */ $encodedPassword, true);
+        $hashValue = hash($phpAlgorithm, $saltValue . $encodedPassword, true);
         for ($i = 0; $i < $spinCount; ++$i) {
             $hashValue = hash($phpAlgorithm, $hashValue . pack('L', $i), true);
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
index 30bd8c5..aac3836 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
@@ -9,49 +9,37 @@ class StringHelper
      *
      * @var string[]
      */
-    private static $controlCharacters = [];
+    private static array $controlCharacters = [];
 
     /**
      * SYLK Characters array.
-     *
-     * @var array
      */
-    private static $SYLKCharacters = [];
+    private static array $SYLKCharacters = [];
 
     /**
      * Decimal separator.
-     *
-     * @var ?string
      */
-    private static $decimalSeparator;
+    private static ?string $decimalSeparator;
 
     /**
      * Thousands separator.
-     *
-     * @var ?string
      */
-    private static $thousandsSeparator;
+    private static ?string $thousandsSeparator;
 
     /**
      * Currency code.
-     *
-     * @var string
      */
-    private static $currencyCode;
+    private static ?string $currencyCode;
 
     /**
      * Is iconv extension avalable?
-     *
-     * @var ?bool
      */
-    private static $isIconvEnabled;
+    private static ?bool $isIconvEnabled;
 
     /**
      * iconv options.
-     *
-     * @var string
      */
-    private static $iconvOptions = '//IGNORE//TRANSLIT';
+    private static string $iconvOptions = '//IGNORE//TRANSLIT';
 
     /**
      * Build control characters array.
@@ -234,10 +222,8 @@ class StringHelper
 
     /**
      * Get whether iconv extension is available.
-     *
-     * @return bool
      */
-    public static function getIsIconvEnabled()
+    public static function getIsIconvEnabled(): bool
     {
         if (isset(self::$isIconvEnabled)) {
             return self::$isIconvEnabled;
@@ -288,10 +274,8 @@ class StringHelper
      * element or in the shared string <t> element.
      *
      * @param string $textValue Value to unescape
-     *
-     * @return string
      */
-    public static function controlCharacterOOXML2PHP($textValue)
+    public static function controlCharacterOOXML2PHP(string $textValue): string
     {
         self::buildCharacterSets();
 
@@ -310,10 +294,8 @@ class StringHelper
      * element or in the shared string <t> element.
      *
      * @param string $textValue Value to escape
-     *
-     * @return string
      */
-    public static function controlCharacterPHP2OOXML($textValue)
+    public static function controlCharacterPHP2OOXML(string $textValue): string
     {
         self::buildCharacterSets();
 
@@ -330,19 +312,9 @@ class StringHelper
         mb_substitute_character(65533); // Unicode substitution character
         // Phpstan does not think this can return false.
         $returnValue = mb_convert_encoding($textValue, 'UTF-8', 'UTF-8');
-        mb_substitute_character(/** @scrutinizer ignore-type */ $subst);
+        mb_substitute_character($subst);
 
-        return self::returnString($returnValue);
-    }
-
-    /**
-     * Strictly to satisfy Scrutinizer.
-     *
-     * @param mixed $value
-     */
-    private static function returnString($value): string
-    {
-        return is_string($value) ? $value : '';
+        return $returnValue;
     }
 
     /**
@@ -356,10 +328,8 @@ class StringHelper
     /**
      * Formats a numeric value as a string for output in various output writers forcing
      * point as decimal separator in case locale is other than English.
-     *
-     * @param float|int|string $numericValue
      */
-    public static function formatNumber($numericValue): string
+    public static function formatNumber(float|int|string|null $numericValue): string
     {
         if (is_float($numericValue)) {
             return str_replace(',', '.', (string) $numericValue);
@@ -376,7 +346,7 @@ class StringHelper
      * see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
      *
      * @param string $textValue UTF-8 encoded string
-     * @param mixed[] $arrcRuns Details of rich text runs in $value
+     * @param array<int, array{strlen: int, fontidx: int}> $arrcRuns Details of rich text runs in $value
      */
     public static function UTF8toBIFF8UnicodeShort(string $textValue, array $arrcRuns = []): string
     {
@@ -412,11 +382,9 @@ class StringHelper
      */
     public static function UTF8toBIFF8UnicodeLong(string $textValue): string
     {
-        // character count
-        $ln = self::countCharacters($textValue, 'UTF-8');
-
         // characters
         $chars = self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
+        $ln = (int) (strlen($chars) / 2);  // N.B. - strlen, not mb_strlen issue #642
 
         return pack('vC', $ln, 0x0001) . $chars;
     }
@@ -436,7 +404,7 @@ class StringHelper
             }
         }
 
-        return self::returnString(mb_convert_encoding($textValue, $to, $from));
+        return mb_convert_encoding($textValue, $to, $from);
     }
 
     /**
@@ -451,6 +419,18 @@ class StringHelper
         return mb_strlen($textValue, $encoding);
     }
 
+    /**
+     * Get character count using mb_strwidth rather than mb_strlen.
+     *
+     * @param string $encoding Encoding
+     *
+     * @return int Character count
+     */
+    public static function countCharactersDbcs(string $textValue, string $encoding = 'UTF-8'): int
+    {
+        return mb_strwidth($textValue, $encoding);
+    }
+
     /**
      * Get a substring of a UTF-8 encoded string.
      *
@@ -555,9 +535,9 @@ class StringHelper
      * Set the decimal separator. Only used by NumberFormat::toFormattedString()
      * to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
      *
-     * @param string $separator Character for decimal separator
+     * @param ?string $separator Character for decimal separator
      */
-    public static function setDecimalSeparator(string $separator): void
+    public static function setDecimalSeparator(?string $separator): void
     {
         self::$decimalSeparator = $separator;
     }
@@ -586,9 +566,9 @@ class StringHelper
      * Set the thousands separator. Only used by NumberFormat::toFormattedString()
      * to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
      *
-     * @param string $separator Character for thousands separator
+     * @param ?string $separator Character for thousands separator
      */
-    public static function setThousandsSeparator(string $separator): void
+    public static function setThousandsSeparator(?string $separator): void
     {
         self::$thousandsSeparator = $separator;
     }
@@ -622,9 +602,9 @@ class StringHelper
      * Set the currency code. Only used by NumberFormat::toFormattedString()
      *        to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
      *
-     * @param string $currencyCode Character for currency code
+     * @param ?string $currencyCode Character for currency code
      */
-    public static function setCurrencyCode(string $currencyCode): void
+    public static function setCurrencyCode(?string $currencyCode): void
     {
         self::$currencyCode = $currencyCode;
     }
@@ -641,7 +621,7 @@ class StringHelper
         self::buildCharacterSets();
 
         // If there is no escape character in the string there is nothing to do
-        if (strpos($textValue, '') === false) {
+        if (!str_contains($textValue, '')) {
             return $textValue;
         }
 
@@ -656,11 +636,9 @@ class StringHelper
      * Retrieve any leading numeric part of a string, or return the full string if no leading numeric
      * (handles basic integer or float, but not exponent or non decimal).
      *
-     * @param string $textValue
-     *
-     * @return mixed string or only the leading numeric part of the string
+     * @return float|string string or only the leading numeric part of the string
      */
-    public static function testStringAsNumeric($textValue)
+    public static function testStringAsNumeric(string $textValue): float|string
     {
         if (is_numeric($textValue)) {
             return $textValue;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
index 324e342..f6e8500 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
@@ -9,10 +9,8 @@ class TimeZone
 {
     /**
      * Default Timezone used for date/time conversions.
-     *
-     * @var string
      */
-    protected static $timezone = 'UTC';
+    protected static string $timezone = 'UTC';
 
     /**
      * Validate a Timezone name.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php
index b2b0d94..f9dacfb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php
@@ -6,87 +6,79 @@ abstract class BestFit
 {
     /**
      * Indicator flag for a calculation error.
-     *
-     * @var bool
      */
-    protected $error = false;
+    protected bool $error = false;
 
     /**
      * Algorithm type to use for best-fit.
-     *
-     * @var string
      */
-    protected $bestFitType = 'undetermined';
+    protected string $bestFitType = 'undetermined';
 
     /**
      * Number of entries in the sets of x- and y-value arrays.
-     *
-     * @var int
      */
-    protected $valueCount = 0;
+    protected int $valueCount;
 
     /**
      * X-value dataseries of values.
      *
      * @var float[]
      */
-    protected $xValues = [];
+    protected array $xValues = [];
 
     /**
      * Y-value dataseries of values.
      *
      * @var float[]
      */
-    protected $yValues = [];
+    protected array $yValues = [];
 
     /**
      * Flag indicating whether values should be adjusted to Y=0.
-     *
-     * @var bool
      */
-    protected $adjustToZero = false;
+    protected bool $adjustToZero = false;
 
     /**
      * Y-value series of best-fit values.
      *
      * @var float[]
      */
-    protected $yBestFitValues = [];
+    protected array $yBestFitValues = [];
 
-    protected $goodnessOfFit = 1;
+    protected float $goodnessOfFit = 1;
 
-    protected $stdevOfResiduals = 0;
+    protected float $stdevOfResiduals = 0;
 
-    protected $covariance = 0;
+    protected float $covariance = 0;
 
-    protected $correlation = 0;
+    protected float $correlation = 0;
 
-    protected $SSRegression = 0;
+    protected float $SSRegression = 0;
 
-    protected $SSResiduals = 0;
+    protected float $SSResiduals = 0;
 
-    protected $DFResiduals = 0;
+    protected float $DFResiduals = 0;
 
-    protected $f = 0;
+    protected float $f = 0;
 
-    protected $slope = 0;
+    protected float $slope = 0;
 
-    protected $slopeSE = 0;
+    protected float $slopeSE = 0;
 
-    protected $intersect = 0;
+    protected float $intersect = 0;
 
-    protected $intersectSE = 0;
+    protected float $intersectSE = 0;
 
-    protected $xOffset = 0;
+    protected float $xOffset = 0;
 
-    protected $yOffset = 0;
+    protected float $yOffset = 0;
 
-    public function getError()
+    public function getError(): bool
     {
         return $this->error;
     }
 
-    public function getBestFitType()
+    public function getBestFitType(): string
     {
         return $this->bestFitType;
     }
@@ -98,7 +90,7 @@ abstract class BestFit
      *
      * @return float Y-Value
      */
-    abstract public function getValueOfYForX($xValue);
+    abstract public function getValueOfYForX(float $xValue): float;
 
     /**
      * Return the X-Value for a specified value of Y.
@@ -107,14 +99,14 @@ abstract class BestFit
      *
      * @return float X-Value
      */
-    abstract public function getValueOfXForY($yValue);
+    abstract public function getValueOfXForY(float $yValue): float;
 
     /**
      * Return the original set of X-Values.
      *
      * @return float[] X-Values
      */
-    public function getXValues()
+    public function getXValues(): array
     {
         return $this->xValues;
     }
@@ -123,19 +115,15 @@ abstract class BestFit
      * Return the Equation of the best-fit line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return string
      */
-    abstract public function getEquation($dp = 0);
+    abstract public function getEquation(int $dp = 0): string;
 
     /**
      * Return the Slope of the line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getSlope($dp = 0)
+    public function getSlope(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->slope, $dp);
@@ -148,10 +136,8 @@ abstract class BestFit
      * Return the standard error of the Slope.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getSlopeSE($dp = 0)
+    public function getSlopeSE(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->slopeSE, $dp);
@@ -164,10 +150,8 @@ abstract class BestFit
      * Return the Value of X where it intersects Y = 0.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getIntersect($dp = 0)
+    public function getIntersect(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->intersect, $dp);
@@ -180,10 +164,8 @@ abstract class BestFit
      * Return the standard error of the Intersect.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getIntersectSE($dp = 0)
+    public function getIntersectSE(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->intersectSE, $dp);
@@ -196,10 +178,8 @@ abstract class BestFit
      * Return the goodness of fit for this regression.
      *
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getGoodnessOfFit($dp = 0)
+    public function getGoodnessOfFit(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->goodnessOfFit, $dp);
@@ -212,10 +192,8 @@ abstract class BestFit
      * Return the goodness of fit for this regression.
      *
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getGoodnessOfFitPercent($dp = 0)
+    public function getGoodnessOfFitPercent(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->goodnessOfFit * 100, $dp);
@@ -228,10 +206,8 @@ abstract class BestFit
      * Return the standard deviation of the residuals for this regression.
      *
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getStdevOfResiduals($dp = 0)
+    public function getStdevOfResiduals(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->stdevOfResiduals, $dp);
@@ -242,10 +218,8 @@ abstract class BestFit
 
     /**
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getSSRegression($dp = 0)
+    public function getSSRegression(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->SSRegression, $dp);
@@ -256,10 +230,8 @@ abstract class BestFit
 
     /**
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getSSResiduals($dp = 0)
+    public function getSSResiduals(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->SSResiduals, $dp);
@@ -270,10 +242,8 @@ abstract class BestFit
 
     /**
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getDFResiduals($dp = 0)
+    public function getDFResiduals(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->DFResiduals, $dp);
@@ -284,10 +254,8 @@ abstract class BestFit
 
     /**
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getF($dp = 0)
+    public function getF(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->f, $dp);
@@ -298,10 +266,8 @@ abstract class BestFit
 
     /**
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getCovariance($dp = 0)
+    public function getCovariance(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->covariance, $dp);
@@ -312,10 +278,8 @@ abstract class BestFit
 
     /**
      * @param int $dp Number of places of decimal precision to return
-     *
-     * @return float
      */
-    public function getCorrelation($dp = 0)
+    public function getCorrelation(int $dp = 0): float
     {
         if ($dp != 0) {
             return round($this->correlation, $dp);
@@ -327,24 +291,12 @@ abstract class BestFit
     /**
      * @return float[]
      */
-    public function getYBestFitValues()
+    public function getYBestFitValues(): array
     {
         return $this->yBestFitValues;
     }
 
-    /** @var mixed */
-    private static $scrutinizerZeroPointZero = 0.0;
-
-    /**
-     * @param mixed $x
-     * @param mixed $y
-     */
-    private static function scrutinizerLooseCompare($x, $y): bool
-    {
-        return $x == $y;
-    }
-
-    protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void
+    protected function calculateGoodnessOfFit(float $sumX, float $sumY, float $sumX2, float $sumY2, float $sumXY, float $meanX, float $meanY, bool|int $const): void
     {
         $SSres = $SScov = $SStot = $SSsex = 0.0;
         foreach ($this->xValues as $xKey => $xValue) {
@@ -372,8 +324,8 @@ abstract class BestFit
         } else {
             $this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals);
         }
-        // Scrutinizer thinks $SSres == $SStot is always true. It is wrong.
-        if ($SStot == self::$scrutinizerZeroPointZero || self::scrutinizerLooseCompare($SSres, $SStot)) {
+
+        if ($SStot == 0.0 || $SSres == $SStot) {
             $this->goodnessOfFit = 1;
         } else {
             $this->goodnessOfFit = 1 - ($SSres / $SStot);
@@ -399,13 +351,12 @@ abstract class BestFit
         }
     }
 
+    /** @return float|int */
     private function sumSquares(array $values)
     {
         return array_sum(
             array_map(
-                function ($value) {
-                    return $value ** 2;
-                },
+                fn ($value): float|int => $value ** 2,
                 $values
             )
         );
@@ -453,7 +404,7 @@ abstract class BestFit
      * @param float[] $yValues The set of Y-values for this regression
      * @param float[] $xValues The set of X-values for this regression
      */
-    public function __construct($yValues, $xValues = [])
+    public function __construct(array $yValues, array $xValues = [])
     {
         //    Calculate number of points
         $yValueCount = count($yValues);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
index eb8cd74..ed2d889 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
@@ -7,10 +7,8 @@ class ExponentialBestFit extends BestFit
     /**
      * Algorithm type to use for best-fit
      * (Name of this Trend class).
-     *
-     * @var string
      */
-    protected $bestFitType = 'exponential';
+    protected string $bestFitType = 'exponential';
 
     /**
      * Return the Y-Value for a specified value of X.
@@ -19,7 +17,7 @@ class ExponentialBestFit extends BestFit
      *
      * @return float Y-Value
      */
-    public function getValueOfYForX($xValue)
+    public function getValueOfYForX(float $xValue): float
     {
         return $this->getIntersect() * $this->getSlope() ** ($xValue - $this->xOffset);
     }
@@ -31,7 +29,7 @@ class ExponentialBestFit extends BestFit
      *
      * @return float X-Value
      */
-    public function getValueOfXForY($yValue)
+    public function getValueOfXForY(float $yValue): float
     {
         return log(($yValue + $this->yOffset) / $this->getIntersect()) / log($this->getSlope());
     }
@@ -40,10 +38,8 @@ class ExponentialBestFit extends BestFit
      * Return the Equation of the best-fit line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return string
      */
-    public function getEquation($dp = 0)
+    public function getEquation(int $dp = 0): string
     {
         $slope = $this->getSlope($dp);
         $intersect = $this->getIntersect($dp);
@@ -55,10 +51,8 @@ class ExponentialBestFit extends BestFit
      * Return the Slope of the line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getSlope($dp = 0)
+    public function getSlope(int $dp = 0): float
     {
         if ($dp != 0) {
             return round(exp($this->slope), $dp);
@@ -71,10 +65,8 @@ class ExponentialBestFit extends BestFit
      * Return the Value of X where it intersects Y = 0.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getIntersect($dp = 0)
+    public function getIntersect(int $dp = 0): float
     {
         if ($dp != 0) {
             return round(exp($this->intersect), $dp);
@@ -92,9 +84,7 @@ class ExponentialBestFit extends BestFit
     private function exponentialRegression(array $yValues, array $xValues, bool $const): void
     {
         $adjustedYValues = array_map(
-            function ($value) {
-                return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
-            },
+            fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
             $yValues
         );
 
@@ -106,9 +96,8 @@ class ExponentialBestFit extends BestFit
      *
      * @param float[] $yValues The set of Y-values for this regression
      * @param float[] $xValues The set of X-values for this regression
-     * @param bool $const
      */
-    public function __construct($yValues, $xValues = [], $const = true)
+    public function __construct(array $yValues, array $xValues = [], bool $const = true)
     {
         parent::__construct($yValues, $xValues);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
index 65d6b4f..8a54080 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
@@ -7,10 +7,8 @@ class LinearBestFit extends BestFit
     /**
      * Algorithm type to use for best-fit
      * (Name of this Trend class).
-     *
-     * @var string
      */
-    protected $bestFitType = 'linear';
+    protected string $bestFitType = 'linear';
 
     /**
      * Return the Y-Value for a specified value of X.
@@ -19,7 +17,7 @@ class LinearBestFit extends BestFit
      *
      * @return float Y-Value
      */
-    public function getValueOfYForX($xValue)
+    public function getValueOfYForX(float $xValue): float
     {
         return $this->getIntersect() + $this->getSlope() * $xValue;
     }
@@ -31,7 +29,7 @@ class LinearBestFit extends BestFit
      *
      * @return float X-Value
      */
-    public function getValueOfXForY($yValue)
+    public function getValueOfXForY(float $yValue): float
     {
         return ($yValue - $this->getIntersect()) / $this->getSlope();
     }
@@ -40,10 +38,8 @@ class LinearBestFit extends BestFit
      * Return the Equation of the best-fit line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return string
      */
-    public function getEquation($dp = 0)
+    public function getEquation(int $dp = 0): string
     {
         $slope = $this->getSlope($dp);
         $intersect = $this->getIntersect($dp);
@@ -67,9 +63,8 @@ class LinearBestFit extends BestFit
      *
      * @param float[] $yValues The set of Y-values for this regression
      * @param float[] $xValues The set of X-values for this regression
-     * @param bool $const
      */
-    public function __construct($yValues, $xValues = [], $const = true)
+    public function __construct(array $yValues, array $xValues = [], bool $const = true)
     {
         parent::__construct($yValues, $xValues);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
index 2366dc6..3dec61b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
@@ -7,10 +7,8 @@ class LogarithmicBestFit extends BestFit
     /**
      * Algorithm type to use for best-fit
      * (Name of this Trend class).
-     *
-     * @var string
      */
-    protected $bestFitType = 'logarithmic';
+    protected string $bestFitType = 'logarithmic';
 
     /**
      * Return the Y-Value for a specified value of X.
@@ -19,7 +17,7 @@ class LogarithmicBestFit extends BestFit
      *
      * @return float Y-Value
      */
-    public function getValueOfYForX($xValue)
+    public function getValueOfYForX(float $xValue): float
     {
         return $this->getIntersect() + $this->getSlope() * log($xValue - $this->xOffset);
     }
@@ -31,7 +29,7 @@ class LogarithmicBestFit extends BestFit
      *
      * @return float X-Value
      */
-    public function getValueOfXForY($yValue)
+    public function getValueOfXForY(float $yValue): float
     {
         return exp(($yValue - $this->getIntersect()) / $this->getSlope());
     }
@@ -40,10 +38,8 @@ class LogarithmicBestFit extends BestFit
      * Return the Equation of the best-fit line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return string
      */
-    public function getEquation($dp = 0)
+    public function getEquation(int $dp = 0): string
     {
         $slope = $this->getSlope($dp);
         $intersect = $this->getIntersect($dp);
@@ -60,9 +56,7 @@ class LogarithmicBestFit extends BestFit
     private function logarithmicRegression(array $yValues, array $xValues, bool $const): void
     {
         $adjustedYValues = array_map(
-            function ($value) {
-                return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
-            },
+            fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
             $yValues
         );
 
@@ -74,9 +68,8 @@ class LogarithmicBestFit extends BestFit
      *
      * @param float[] $yValues The set of Y-values for this regression
      * @param float[] $xValues The set of X-values for this regression
-     * @param bool $const
      */
-    public function __construct($yValues, $xValues = [], $const = true)
+    public function __construct(array $yValues, array $xValues = [], bool $const = true)
     {
         parent::__construct($yValues, $xValues);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
index ea40e29..911a9c3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
@@ -2,7 +2,7 @@
 
 namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
 
-use PhpOffice\PhpSpreadsheet\Shared\JAMA\Matrix;
+use Matrix\Matrix;
 
 // Phpstan and Scrutinizer seem to have legitimate complaints.
 // $this->slope is specified where an array is expected in several places.
@@ -13,24 +13,18 @@ class PolynomialBestFit extends BestFit
     /**
      * Algorithm type to use for best-fit
      * (Name of this Trend class).
-     *
-     * @var string
      */
-    protected $bestFitType = 'polynomial';
+    protected string $bestFitType = 'polynomial';
 
     /**
      * Polynomial order.
-     *
-     * @var int
      */
-    protected $order = 0;
+    protected int $order = 0;
 
     /**
      * Return the order of this polynomial.
-     *
-     * @return int
      */
-    public function getOrder()
+    public function getOrder(): int
     {
         return $this->order;
     }
@@ -42,7 +36,7 @@ class PolynomialBestFit extends BestFit
      *
      * @return float Y-Value
      */
-    public function getValueOfYForX($xValue)
+    public function getValueOfYForX(float $xValue): float
     {
         $retVal = $this->getIntersect();
         $slope = $this->getSlope();
@@ -64,7 +58,7 @@ class PolynomialBestFit extends BestFit
      *
      * @return float X-Value
      */
-    public function getValueOfXForY($yValue)
+    public function getValueOfXForY(float $yValue): float
     {
         return ($yValue - $this->getIntersect()) / $this->getSlope();
     }
@@ -73,10 +67,8 @@ class PolynomialBestFit extends BestFit
      * Return the Equation of the best-fit line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return string
      */
-    public function getEquation($dp = 0)
+    public function getEquation(int $dp = 0): string
     {
         $slope = $this->getSlope($dp);
         $intersect = $this->getIntersect($dp);
@@ -100,14 +92,12 @@ class PolynomialBestFit extends BestFit
      * Return the Slope of the line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getSlope($dp = 0)
+    public function getSlope(int $dp = 0): float
     {
         if ($dp != 0) {
             $coefficients = [];
-            // Scrutinizer is correct - $this->slope is float, not array.
+            //* @phpstan-ignore-next-line
             foreach ($this->slope as $coefficient) {
                 $coefficients[] = round($coefficient, $dp);
             }
@@ -119,7 +109,7 @@ class PolynomialBestFit extends BestFit
         return $this->slope;
     }
 
-    public function getCoefficients($dp = 0)
+    public function getCoefficients(int $dp = 0): array
     {
         // Phpstan and Scrutinizer are both correct - getSlope returns float, not array.
         // @phpstan-ignore-next-line
@@ -133,7 +123,7 @@ class PolynomialBestFit extends BestFit
      * @param float[] $yValues The set of Y-values for this regression
      * @param float[] $xValues The set of X-values for this regression
      */
-    private function polynomialRegression($order, $yValues, $xValues): void
+    private function polynomialRegression(int $order, array $yValues, array $xValues): void
     {
         // calculate sums
         $x_sum = array_sum($xValues);
@@ -167,15 +157,19 @@ class PolynomialBestFit extends BestFit
         $C = $matrixA->solve($matrixB);
 
         $coefficients = [];
-        for ($i = 0; $i < $C->getRowDimension(); ++$i) {
-            $r = $C->get($i, 0);
-            if (abs($r) <= 10 ** (-9)) {
+        for ($i = 0; $i < $C->rows; ++$i) {
+            $r = $C->getValue($i + 1, 1); // row and column are origin-1
+            if (!is_numeric($r) || abs($r) <= 10 ** (-9)) {
                 $r = 0;
+            } else {
+                $r += 0;
             }
             $coefficients[] = $r;
         }
 
-        $this->intersect = array_shift($coefficients);
+        $this->intersect = (float) array_shift($coefficients);
+        // Phpstan is correct
+        //* @phpstan-ignore-next-line
         $this->slope = $coefficients;
 
         $this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0);
@@ -191,7 +185,7 @@ class PolynomialBestFit extends BestFit
      * @param float[] $yValues The set of Y-values for this regression
      * @param float[] $xValues The set of X-values for this regression
      */
-    public function __construct($order, $yValues, $xValues = [])
+    public function __construct(int $order, array $yValues, array $xValues = [])
     {
         parent::__construct($yValues, $xValues);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
index cafd011..56b5a12 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
@@ -7,10 +7,8 @@ class PowerBestFit extends BestFit
     /**
      * Algorithm type to use for best-fit
      * (Name of this Trend class).
-     *
-     * @var string
      */
-    protected $bestFitType = 'power';
+    protected string $bestFitType = 'power';
 
     /**
      * Return the Y-Value for a specified value of X.
@@ -19,7 +17,7 @@ class PowerBestFit extends BestFit
      *
      * @return float Y-Value
      */
-    public function getValueOfYForX($xValue)
+    public function getValueOfYForX(float $xValue): float
     {
         return $this->getIntersect() * ($xValue - $this->xOffset) ** $this->getSlope();
     }
@@ -31,7 +29,7 @@ class PowerBestFit extends BestFit
      *
      * @return float X-Value
      */
-    public function getValueOfXForY($yValue)
+    public function getValueOfXForY(float $yValue): float
     {
         return (($yValue + $this->yOffset) / $this->getIntersect()) ** (1 / $this->getSlope());
     }
@@ -40,10 +38,8 @@ class PowerBestFit extends BestFit
      * Return the Equation of the best-fit line.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return string
      */
-    public function getEquation($dp = 0)
+    public function getEquation(int $dp = 0): string
     {
         $slope = $this->getSlope($dp);
         $intersect = $this->getIntersect($dp);
@@ -55,10 +51,8 @@ class PowerBestFit extends BestFit
      * Return the Value of X where it intersects Y = 0.
      *
      * @param int $dp Number of places of decimal precision to display
-     *
-     * @return float
      */
-    public function getIntersect($dp = 0)
+    public function getIntersect(int $dp = 0): float
     {
         if ($dp != 0) {
             return round(exp($this->intersect), $dp);
@@ -76,15 +70,11 @@ class PowerBestFit extends BestFit
     private function powerRegression(array $yValues, array $xValues, bool $const): void
     {
         $adjustedYValues = array_map(
-            function ($value) {
-                return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
-            },
+            fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
             $yValues
         );
         $adjustedXValues = array_map(
-            function ($value) {
-                return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
-            },
+            fn ($value): float => ($value < 0.0) ? 0 - log(abs($value)) : log($value),
             $xValues
         );
 
@@ -96,9 +86,8 @@ class PowerBestFit extends BestFit
      *
      * @param float[] $yValues The set of Y-values for this regression
      * @param float[] $xValues The set of X-values for this regression
-     * @param bool $const
      */
-    public function __construct($yValues, $xValues = [], $const = true)
+    public function __construct(array $yValues, array $xValues = [], bool $const = true)
     {
         parent::__construct($yValues, $xValues);
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
index 929f59b..dc87943 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
@@ -21,7 +21,7 @@ class Trend
      *
      * @var string[]
      */
-    private static $trendTypes = [
+    private static array $trendTypes = [
         self::TREND_LINEAR,
         self::TREND_LOGARITHMIC,
         self::TREND_EXPONENTIAL,
@@ -33,7 +33,7 @@ class Trend
      *
      * @var string[]
      */
-    private static $trendTypePolynomialOrders = [
+    private static array $trendTypePolynomialOrders = [
         self::TREND_POLYNOMIAL_2,
         self::TREND_POLYNOMIAL_3,
         self::TREND_POLYNOMIAL_4,
@@ -46,9 +46,9 @@ class Trend
      *
      * @var BestFit[]
      */
-    private static $trendCache = [];
+    private static array $trendCache = [];
 
-    public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [], $xValues = [], $const = true)
+    public static function calculate(string $trendType = self::TREND_BEST_FIT, array $yValues = [], array $xValues = [], bool $const = true): mixed
     {
         //    Calculate number of points in each dataset
         $nY = count($yValues);
@@ -72,7 +72,6 @@ class Trend
             case self::TREND_POWER:
                 if (!isset(self::$trendCache[$key])) {
                     $className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
-                    // @phpstan-ignore-next-line
                     self::$trendCache[$key] = new $className($yValues, $xValues, $const);
                 }
 
@@ -96,6 +95,7 @@ class Trend
                 $bestFitValue = [];
                 foreach (self::$trendTypes as $trendMethod) {
                     $className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
+                    //* @phpstan-ignore-next-line
                     $bestFit[$trendMethod] = new $className($yValues, $xValues, $const);
                     $bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
                 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
index 65bd7ec..2703e98 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
@@ -2,10 +2,11 @@
 
 namespace PhpOffice\PhpSpreadsheet\Shared;
 
+use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
+
 class XMLWriter extends \XMLWriter
 {
-    /** @var bool */
-    public static $debugEnabled = false;
+    public static bool $debugEnabled = false;
 
     /** Temporary storage method */
     const STORAGE_MEMORY = 1;
@@ -13,18 +14,16 @@ class XMLWriter extends \XMLWriter
 
     /**
      * Temporary filename.
-     *
-     * @var string
      */
-    private $tempFileName = '';
+    private string $tempFileName = '';
 
     /**
      * Create a new XMLWriter instance.
      *
      * @param int $temporaryStorage Temporary storage location
-     * @param string $temporaryStorageFolder Temporary storage folder
+     * @param ?string $temporaryStorageFolder Temporary storage folder
      */
-    public function __construct($temporaryStorage = self::STORAGE_MEMORY, $temporaryStorageFolder = null)
+    public function __construct(int $temporaryStorage = self::STORAGE_MEMORY, ?string $temporaryStorageFolder = null)
     {
         // Open temporary storage
         if ($temporaryStorage == self::STORAGE_MEMORY) {
@@ -57,17 +56,21 @@ class XMLWriter extends \XMLWriter
         // Unlink temporary files
         // There is nothing reasonable to do if unlink fails.
         if ($this->tempFileName != '') {
-            /** @scrutinizer ignore-unhandled */
             @unlink($this->tempFileName);
         }
     }
 
+    public function __wakeup(): void
+    {
+        $this->tempFileName = '';
+
+        throw new SpreadsheetException('Unserialize not permitted');
+    }
+
     /**
      * Get written data.
-     *
-     * @return string
      */
-    public function getData()
+    public function getData(): string
     {
         if ($this->tempFileName == '') {
             return $this->outputMemory(true);
@@ -81,10 +84,8 @@ class XMLWriter extends \XMLWriter
      * Wrapper method for writeRaw.
      *
      * @param null|string|string[] $rawTextData
-     *
-     * @return bool
      */
-    public function writeRawData($rawTextData)
+    public function writeRawData($rawTextData): bool
     {
         if (is_array($rawTextData)) {
             $rawTextData = implode("\n", $rawTextData);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
index 2c3198b..cdb1bf2 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php
@@ -18,10 +18,10 @@ class Xls
      *
      * @return int The width in pixels
      */
-    public static function sizeCol(Worksheet $worksheet, $col = 'A')
+    public static function sizeCol(Worksheet $worksheet, string $col = 'A'): int
     {
         // default font of the workbook
-        $font = $worksheet->getParent()->getDefaultStyle()->getFont();
+        $font = $worksheet->getParentOrThrow()->getDefaultStyle()->getFont();
 
         $columnDimensions = $worksheet->getColumnDimensions();
 
@@ -61,10 +61,10 @@ class Xls
      *
      * @return int The width in pixels
      */
-    public static function sizeRow(Worksheet $worksheet, $row = 1)
+    public static function sizeRow(Worksheet $worksheet, int $row = 1): int
     {
         // default font of the workbook
-        $font = $worksheet->getParent()->getDefaultStyle()->getFont();
+        $font = $worksheet->getParentOrThrow()->getDefaultStyle()->getFont();
 
         $rowDimensions = $worksheet->getRowDimensions();
 
@@ -98,14 +98,12 @@ class Xls
      * Get the horizontal distance in pixels between two anchors
      * The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets.
      *
-     * @param string $startColumn
-     * @param int $startOffsetX Offset within start cell measured in 1/1024 of the cell width
-     * @param string $endColumn
-     * @param int $endOffsetX Offset within end cell measured in 1/1024 of the cell width
+     * @param float|int $startOffsetX Offset within start cell measured in 1/1024 of the cell width
+     * @param float|int $endOffsetX Offset within end cell measured in 1/1024 of the cell width
      *
      * @return int Horizontal measured in pixels
      */
-    public static function getDistanceX(Worksheet $worksheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0)
+    public static function getDistanceX(Worksheet $worksheet, string $startColumn = 'A', float|int $startOffsetX = 0, string $endColumn = 'A', float|int $endOffsetX = 0): int
     {
         $distanceX = 0;
 
@@ -130,13 +128,13 @@ class Xls
      * The distanceY is found as sum of all the spanning rows minus two offsets.
      *
      * @param int $startRow (1-based)
-     * @param int $startOffsetY Offset within start cell measured in 1/256 of the cell height
+     * @param float|int $startOffsetY Offset within start cell measured in 1/256 of the cell height
      * @param int $endRow (1-based)
-     * @param int $endOffsetY Offset within end cell measured in 1/256 of the cell height
+     * @param float|int $endOffsetY Offset within end cell measured in 1/256 of the cell height
      *
      * @return int Vertical distance measured in pixels
      */
-    public static function getDistanceY(Worksheet $worksheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0)
+    public static function getDistanceY(Worksheet $worksheet, int $startRow = 1, float|int $startOffsetY = 0, int $endRow = 1, float|int $endOffsetY = 0): int
     {
         $distanceY = 0;
 
@@ -203,10 +201,8 @@ class Xls
      * @param int $offsetY Vertical offset in pixels
      * @param int $width Width in pixels
      * @param int $height Height in pixels
-     *
-     * @return null|array
      */
-    public static function oneAnchor2twoAnchor(Worksheet $worksheet, $coordinates, $offsetX, $offsetY, $width, $height)
+    public static function oneAnchor2twoAnchor(Worksheet $worksheet, string $coordinates, int $offsetX, int $offsetY, int $width, int $height): ?array
     {
         [$col_start, $row] = Coordinate::indexesFromString($coordinates);
         $row_start = $row - 1;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
index 1432ba4..bcea8e6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
@@ -4,11 +4,15 @@ namespace PhpOffice\PhpSpreadsheet;
 
 use JsonSerializable;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Document\Properties;
+use PhpOffice\PhpSpreadsheet\Document\Security;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
 use PhpOffice\PhpSpreadsheet\Shared\File;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Style\Style;
 use PhpOffice\PhpSpreadsheet\Worksheet\Iterator;
+use PhpOffice\PhpSpreadsheet\Worksheet\Table;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
 
@@ -28,187 +32,156 @@ class Spreadsheet implements JsonSerializable
         self::VISIBILITY_VERY_HIDDEN,
     ];
 
+    protected int $excelCalendar = Date::CALENDAR_WINDOWS_1900;
+
     /**
      * Unique ID.
-     *
-     * @var string
      */
-    private $uniqueID;
+    private string $uniqueID;
 
     /**
      * Document properties.
-     *
-     * @var Document\Properties
      */
-    private $properties;
+    private Properties $properties;
 
     /**
      * Document security.
-     *
-     * @var Document\Security
      */
-    private $security;
+    private Security $security;
 
     /**
      * Collection of Worksheet objects.
      *
      * @var Worksheet[]
      */
-    private $workSheetCollection = [];
+    private array $workSheetCollection;
 
     /**
      * Calculation Engine.
-     *
-     * @var null|Calculation
      */
-    private $calculationEngine;
+    private ?Calculation $calculationEngine;
 
     /**
      * Active sheet index.
-     *
-     * @var int
      */
-    private $activeSheetIndex = 0;
+    private int $activeSheetIndex;
 
     /**
      * Named ranges.
      *
      * @var DefinedName[]
      */
-    private $definedNames = [];
+    private array $definedNames;
 
     /**
      * CellXf supervisor.
-     *
-     * @var Style
      */
-    private $cellXfSupervisor;
+    private Style $cellXfSupervisor;
 
     /**
      * CellXf collection.
      *
      * @var Style[]
      */
-    private $cellXfCollection = [];
+    private array $cellXfCollection = [];
 
     /**
      * CellStyleXf collection.
      *
      * @var Style[]
      */
-    private $cellStyleXfCollection = [];
+    private array $cellStyleXfCollection = [];
 
     /**
      * hasMacros : this workbook have macros ?
-     *
-     * @var bool
      */
-    private $hasMacros = false;
+    private bool $hasMacros = false;
 
     /**
      * macrosCode : all macros code as binary data (the vbaProject.bin file, this include form, code,  etc.), null if no macro.
-     *
-     * @var null|string
      */
-    private $macrosCode;
+    private ?string $macrosCode = null;
 
     /**
      * macrosCertificate : if macros are signed, contains binary data vbaProjectSignature.bin file, null if not signed.
-     *
-     * @var null|string
      */
-    private $macrosCertificate;
+    private ?string $macrosCertificate = null;
 
     /**
      * ribbonXMLData : null if workbook is'nt Excel 2007 or not contain a customized UI.
      *
      * @var null|array{target: string, data: string}
      */
-    private $ribbonXMLData;
+    private ?array $ribbonXMLData = null;
 
     /**
      * ribbonBinObjects : null if workbook is'nt Excel 2007 or not contain embedded objects (picture(s)) for Ribbon Elements
      * ignored if $ribbonXMLData is null.
-     *
-     * @var null|array
      */
-    private $ribbonBinObjects;
+    private ?array $ribbonBinObjects = null;
 
     /**
      * List of unparsed loaded data for export to same format with better compatibility.
      * It has to be minimized when the library start to support currently unparsed data.
-     *
-     * @var array
      */
-    private $unparsedLoadedData = [];
+    private array $unparsedLoadedData = [];
 
     /**
      * Controls visibility of the horizonal scroll bar in the application.
-     *
-     * @var bool
      */
-    private $showHorizontalScroll = true;
+    private bool $showHorizontalScroll = true;
 
     /**
      * Controls visibility of the horizonal scroll bar in the application.
-     *
-     * @var bool
      */
-    private $showVerticalScroll = true;
+    private bool $showVerticalScroll = true;
 
     /**
      * Controls visibility of the sheet tabs in the application.
-     *
-     * @var bool
      */
-    private $showSheetTabs = true;
+    private bool $showSheetTabs = true;
 
     /**
      * Specifies a boolean value that indicates whether the workbook window
      * is minimized.
-     *
-     * @var bool
      */
-    private $minimized = false;
+    private bool $minimized = false;
 
     /**
      * Specifies a boolean value that indicates whether to group dates
      * when presenting the user with filtering optiomd in the user
      * interface.
-     *
-     * @var bool
      */
-    private $autoFilterDateGrouping = true;
+    private bool $autoFilterDateGrouping = true;
 
     /**
      * Specifies the index to the first sheet in the book view.
-     *
-     * @var int
      */
-    private $firstSheetIndex = 0;
+    private int $firstSheetIndex = 0;
 
     /**
      * Specifies the visible status of the workbook.
-     *
-     * @var string
      */
-    private $visibility = self::VISIBILITY_VISIBLE;
+    private string $visibility = self::VISIBILITY_VISIBLE;
 
     /**
      * Specifies the ratio between the workbook tabs bar and the horizontal
      * scroll bar.  TabRatio is assumed to be out of 1000 of the horizontal
      * window width.
-     *
-     * @var int
      */
-    private $tabRatio = 600;
+    private int $tabRatio = 600;
+
+    private Theme $theme;
+
+    public function getTheme(): Theme
+    {
+        return $this->theme;
+    }
 
     /**
      * The workbook has macros ?
-     *
-     * @return bool
      */
-    public function hasMacros()
+    public function hasMacros(): bool
     {
         return $this->hasMacros;
     }
@@ -218,7 +191,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param bool $hasMacros true|false
      */
-    public function setHasMacros($hasMacros): void
+    public function setHasMacros(bool $hasMacros): void
     {
         $this->hasMacros = (bool) $hasMacros;
     }
@@ -228,7 +201,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param string $macroCode string|null
      */
-    public function setMacrosCode($macroCode): void
+    public function setMacrosCode(string $macroCode): void
     {
         $this->macrosCode = $macroCode;
         $this->setHasMacros($macroCode !== null);
@@ -236,20 +209,16 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * Return the macros code.
-     *
-     * @return null|string
      */
-    public function getMacrosCode()
+    public function getMacrosCode(): ?string
     {
         return $this->macrosCode;
     }
 
     /**
      * Set the macros certificate.
-     *
-     * @param null|string $certificate
      */
-    public function setMacrosCertificate($certificate): void
+    public function setMacrosCertificate(?string $certificate): void
     {
         $this->macrosCertificate = $certificate;
     }
@@ -259,17 +228,15 @@ class Spreadsheet implements JsonSerializable
      *
      * @return bool true|false
      */
-    public function hasMacrosCertificate()
+    public function hasMacrosCertificate(): bool
     {
         return $this->macrosCertificate !== null;
     }
 
     /**
      * Return the macros certificate.
-     *
-     * @return null|string
      */
-    public function getMacrosCertificate()
+    public function getMacrosCertificate(): ?string
     {
         return $this->macrosCertificate;
     }
@@ -286,13 +253,10 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * set ribbon XML data.
-     *
-     * @param null|mixed $target
-     * @param null|mixed $xmlData
      */
-    public function setRibbonXMLData($target, $xmlData): void
+    public function setRibbonXMLData(mixed $target, mixed $xmlData): void
     {
-        if ($target !== null && $xmlData !== null) {
+        if (is_string($target) && is_string($xmlData)) {
             $this->ribbonXMLData = ['target' => $target, 'data' => $xmlData];
         } else {
             $this->ribbonXMLData = null;
@@ -301,12 +265,8 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * retrieve ribbon XML Data.
-     *
-     * @param string $what
-     *
-     * @return null|array|string
      */
-    public function getRibbonXMLData($what = 'all') //we need some constants here...
+    public function getRibbonXMLData(string $what = 'all'): null|array|string //we need some constants here...
     {
         $returnData = null;
         $what = strtolower($what);
@@ -329,14 +289,11 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * store binaries ribbon objects (pictures).
-     *
-     * @param null|mixed $BinObjectsNames
-     * @param null|mixed $BinObjectsData
      */
-    public function setRibbonBinObjects($BinObjectsNames, $BinObjectsData): void
+    public function setRibbonBinObjects(mixed $binObjectsNames, mixed $binObjectsData): void
     {
-        if ($BinObjectsNames !== null && $BinObjectsData !== null) {
-            $this->ribbonBinObjects = ['names' => $BinObjectsNames, 'data' => $BinObjectsData];
+        if ($binObjectsNames !== null && $binObjectsData !== null) {
+            $this->ribbonBinObjects = ['names' => $binObjectsNames, 'data' => $binObjectsData];
         } else {
             $this->ribbonBinObjects = null;
         }
@@ -347,10 +304,8 @@ class Spreadsheet implements JsonSerializable
      * It has to be minimized when the library start to support currently unparsed data.
      *
      * @internal
-     *
-     * @return array
      */
-    public function getUnparsedLoadedData()
+    public function getUnparsedLoadedData(): array
     {
         return $this->unparsedLoadedData;
     }
@@ -366,28 +321,10 @@ class Spreadsheet implements JsonSerializable
         $this->unparsedLoadedData = $unparsedLoadedData;
     }
 
-    /**
-     * return the extension of a filename. Internal use for a array_map callback (php<5.3 don't like lambda function).
-     *
-     * @param mixed $path
-     *
-     * @return string
-     */
-    private function getExtensionOnly($path)
-    {
-        $extension = pathinfo($path, PATHINFO_EXTENSION);
-
-        return substr(/** @scrutinizer ignore-type */$extension, 0);
-    }
-
     /**
      * retrieve Binaries Ribbon Objects.
-     *
-     * @param string $what
-     *
-     * @return null|array
      */
-    public function getRibbonBinObjects($what = 'all')
+    public function getRibbonBinObjects(string $what = 'all'): ?array
     {
         $ReturnData = null;
         $what = strtolower($what);
@@ -403,11 +340,11 @@ class Spreadsheet implements JsonSerializable
                 break;
             case 'types':
                 if (
-                    is_array($this->ribbonBinObjects) &&
-                    isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])
+                    is_array($this->ribbonBinObjects)
+                    && isset($this->ribbonBinObjects['data']) && is_array($this->ribbonBinObjects['data'])
                 ) {
                     $tmpTypes = array_keys($this->ribbonBinObjects['data']);
-                    $ReturnData = array_unique(array_map([$this, 'getExtensionOnly'], $tmpTypes));
+                    $ReturnData = array_unique(array_map(fn (string $path): string => pathinfo($path, PATHINFO_EXTENSION), $tmpTypes));
                 } else {
                     $ReturnData = []; // the caller want an array... not null if empty
                 }
@@ -420,20 +357,16 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * This workbook have a custom UI ?
-     *
-     * @return bool
      */
-    public function hasRibbon()
+    public function hasRibbon(): bool
     {
         return $this->ribbonXMLData !== null;
     }
 
     /**
      * This workbook have additionnal object for the ribbon ?
-     *
-     * @return bool
      */
-    public function hasRibbonBinObjects()
+    public function hasRibbonBinObjects(): bool
     {
         return $this->ribbonBinObjects !== null;
     }
@@ -442,10 +375,8 @@ class Spreadsheet implements JsonSerializable
      * Check if a sheet with a specified code name already exists.
      *
      * @param string $codeName Name of the worksheet to check
-     *
-     * @return bool
      */
-    public function sheetCodeNameExists($codeName)
+    public function sheetCodeNameExists(string $codeName): bool
     {
         return $this->getSheetByCodeName($codeName) !== null;
     }
@@ -454,10 +385,8 @@ class Spreadsheet implements JsonSerializable
      * Get sheet by code name. Warning : sheet don't have always a code name !
      *
      * @param string $codeName Sheet name
-     *
-     * @return null|Worksheet
      */
-    public function getSheetByCodeName($codeName)
+    public function getSheetByCodeName(string $codeName): ?Worksheet
     {
         $worksheetCount = count($this->workSheetCollection);
         for ($i = 0; $i < $worksheetCount; ++$i) {
@@ -476,6 +405,7 @@ class Spreadsheet implements JsonSerializable
     {
         $this->uniqueID = uniqid('', true);
         $this->calculationEngine = new Calculation($this);
+        $this->theme = new Theme();
 
         // Initialise worksheet collection and add one worksheet
         $this->workSheetCollection = [];
@@ -483,10 +413,10 @@ class Spreadsheet implements JsonSerializable
         $this->activeSheetIndex = 0;
 
         // Create document properties
-        $this->properties = new Document\Properties();
+        $this->properties = new Properties();
 
         // Create document security
-        $this->security = new Document\Security();
+        $this->security = new Security();
 
         // Set defined names
         $this->definedNames = [];
@@ -509,6 +439,7 @@ class Spreadsheet implements JsonSerializable
         $this->calculationEngine = null;
         $this->cellXfCollection = [];
         $this->cellStyleXfCollection = [];
+        $this->definedNames = [];
     }
 
     /**
@@ -526,20 +457,16 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * Return the calculation engine for this worksheet.
-     *
-     * @return null|Calculation
      */
-    public function getCalculationEngine()
+    public function getCalculationEngine(): ?Calculation
     {
         return $this->calculationEngine;
     }
 
     /**
      * Get properties.
-     *
-     * @return Document\Properties
      */
-    public function getProperties()
+    public function getProperties(): Properties
     {
         return $this->properties;
     }
@@ -547,17 +474,15 @@ class Spreadsheet implements JsonSerializable
     /**
      * Set properties.
      */
-    public function setProperties(Document\Properties $documentProperties): void
+    public function setProperties(Properties $documentProperties): void
     {
         $this->properties = $documentProperties;
     }
 
     /**
      * Get security.
-     *
-     * @return Document\Security
      */
-    public function getSecurity()
+    public function getSecurity(): Security
     {
         return $this->security;
     }
@@ -565,17 +490,15 @@ class Spreadsheet implements JsonSerializable
     /**
      * Set security.
      */
-    public function setSecurity(Document\Security $documentSecurity): void
+    public function setSecurity(Security $documentSecurity): void
     {
         $this->security = $documentSecurity;
     }
 
     /**
      * Get active sheet.
-     *
-     * @return Worksheet
      */
-    public function getActiveSheet()
+    public function getActiveSheet(): Worksheet
     {
         return $this->getSheet($this->activeSheetIndex);
     }
@@ -584,10 +507,8 @@ class Spreadsheet implements JsonSerializable
      * Create sheet and add it to this workbook.
      *
      * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
-     *
-     * @return Worksheet
      */
-    public function createSheet($sheetIndex = null)
+    public function createSheet(?int $sheetIndex = null): Worksheet
     {
         $newSheet = new Worksheet($this);
         $this->addSheet($newSheet, $sheetIndex);
@@ -599,10 +520,8 @@ class Spreadsheet implements JsonSerializable
      * Check if a sheet with a specified name already exists.
      *
      * @param string $worksheetName Name of the worksheet to check
-     *
-     * @return bool
      */
-    public function sheetNameExists($worksheetName)
+    public function sheetNameExists(string $worksheetName): bool
     {
         return $this->getSheetByName($worksheetName) !== null;
     }
@@ -612,10 +531,8 @@ class Spreadsheet implements JsonSerializable
      *
      * @param Worksheet $worksheet The worksheet to add
      * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
-     *
-     * @return Worksheet
      */
-    public function addSheet(Worksheet $worksheet, $sheetIndex = null)
+    public function addSheet(Worksheet $worksheet, ?int $sheetIndex = null): Worksheet
     {
         if ($this->sheetNameExists($worksheet->getTitle())) {
             throw new Exception(
@@ -641,9 +558,12 @@ class Spreadsheet implements JsonSerializable
             if ($this->activeSheetIndex >= $sheetIndex) {
                 ++$this->activeSheetIndex;
             }
+            if ($this->activeSheetIndex < 0) {
+                $this->activeSheetIndex = 0;
+            }
         }
 
-        if ($worksheet->getParent() === null) { // @phpstan-ignore-line
+        if ($worksheet->getParent() === null) {
             $worksheet->rebindParent($this);
         }
 
@@ -655,7 +575,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param int $sheetIndex Index position of the worksheet to remove
      */
-    public function removeSheetByIndex($sheetIndex): void
+    public function removeSheetByIndex(int $sheetIndex): void
     {
         $numSheets = count($this->workSheetCollection);
         if ($sheetIndex > $numSheets - 1) {
@@ -667,8 +587,8 @@ class Spreadsheet implements JsonSerializable
 
         // Adjust active sheet index if necessary
         if (
-            ($this->activeSheetIndex >= $sheetIndex) &&
-            ($this->activeSheetIndex > 0 || $numSheets <= 1)
+            ($this->activeSheetIndex >= $sheetIndex)
+            && ($this->activeSheetIndex > 0 || $numSheets <= 1)
         ) {
             --$this->activeSheetIndex;
         }
@@ -678,10 +598,8 @@ class Spreadsheet implements JsonSerializable
      * Get sheet by index.
      *
      * @param int $sheetIndex Sheet index
-     *
-     * @return Worksheet
      */
-    public function getSheet($sheetIndex)
+    public function getSheet(int $sheetIndex): Worksheet
     {
         if (!isset($this->workSheetCollection[$sheetIndex])) {
             $numSheets = $this->getSheetCount();
@@ -699,7 +617,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return Worksheet[]
      */
-    public function getAllSheets()
+    public function getAllSheets(): array
     {
         return $this->workSheetCollection;
     }
@@ -708,14 +626,12 @@ class Spreadsheet implements JsonSerializable
      * Get sheet by name.
      *
      * @param string $worksheetName Sheet name
-     *
-     * @return null|Worksheet
      */
-    public function getSheetByName($worksheetName)
+    public function getSheetByName(string $worksheetName): ?Worksheet
     {
         $worksheetCount = count($this->workSheetCollection);
         for ($i = 0; $i < $worksheetCount; ++$i) {
-            if ($this->workSheetCollection[$i]->getTitle() === trim($worksheetName, "'")) {
+            if (strcasecmp($this->workSheetCollection[$i]->getTitle(), trim($worksheetName, "'")) === 0) {
                 return $this->workSheetCollection[$i];
             }
         }
@@ -741,7 +657,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return int index
      */
-    public function getIndex(Worksheet $worksheet)
+    public function getIndex(Worksheet $worksheet): int
     {
         foreach ($this->workSheetCollection as $key => $value) {
             if ($value->getHashCode() === $worksheet->getHashCode()) {
@@ -760,7 +676,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return int New sheet index
      */
-    public function setIndexByName($worksheetName, $newIndexPosition)
+    public function setIndexByName(string $worksheetName, int $newIndexPosition): int
     {
         $oldIndex = $this->getIndex($this->getSheetByNameOrThrow($worksheetName));
         $worksheet = array_splice(
@@ -780,10 +696,8 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * Get sheet count.
-     *
-     * @return int
      */
-    public function getSheetCount()
+    public function getSheetCount(): int
     {
         return count($this->workSheetCollection);
     }
@@ -793,7 +707,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return int Active sheet index
      */
-    public function getActiveSheetIndex()
+    public function getActiveSheetIndex(): int
     {
         return $this->activeSheetIndex;
     }
@@ -802,10 +716,8 @@ class Spreadsheet implements JsonSerializable
      * Set active sheet index.
      *
      * @param int $worksheetIndex Active sheet index
-     *
-     * @return Worksheet
      */
-    public function setActiveSheetIndex($worksheetIndex)
+    public function setActiveSheetIndex(int $worksheetIndex): Worksheet
     {
         $numSheets = count($this->workSheetCollection);
 
@@ -823,10 +735,8 @@ class Spreadsheet implements JsonSerializable
      * Set active sheet index by name.
      *
      * @param string $worksheetName Sheet title
-     *
-     * @return Worksheet
      */
-    public function setActiveSheetIndexByName($worksheetName)
+    public function setActiveSheetIndexByName(string $worksheetName): Worksheet
     {
         if (($worksheet = $this->getSheetByName($worksheetName)) instanceof Worksheet) {
             $this->setActiveSheetIndex($this->getIndex($worksheet));
@@ -842,7 +752,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return string[]
      */
-    public function getSheetNames()
+    public function getSheetNames(): array
     {
         $returnValue = [];
         $worksheetCount = $this->getSheetCount();
@@ -858,10 +768,8 @@ class Spreadsheet implements JsonSerializable
      *
      * @param Worksheet $worksheet External sheet to add
      * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
-     *
-     * @return Worksheet
      */
-    public function addExternalSheet(Worksheet $worksheet, $sheetIndex = null)
+    public function addExternalSheet(Worksheet $worksheet, ?int $sheetIndex = null): Worksheet
     {
         if ($this->sheetNameExists($worksheet->getTitle())) {
             throw new Exception("Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename the external sheet first.");
@@ -871,7 +779,7 @@ class Spreadsheet implements JsonSerializable
         $countCellXfs = count($this->cellXfCollection);
 
         // copy all the shared cellXfs from the external workbook and append them to the current
-        foreach ($worksheet->getParent()->getCellXfCollection() as $cellXf) {
+        foreach ($worksheet->getParentOrThrow()->getCellXfCollection() as $cellXf) {
             $this->addCellXf(clone $cellXf);
         }
 
@@ -909,9 +817,7 @@ class Spreadsheet implements JsonSerializable
     {
         return array_filter(
             $this->definedNames,
-            function (DefinedName $definedName) {
-                return $definedName->isFormula() === self::DEFINED_NAME_IS_RANGE;
-            }
+            fn (DefinedName $definedName): bool => $definedName->isFormula() === self::DEFINED_NAME_IS_RANGE
         );
     }
 
@@ -924,9 +830,7 @@ class Spreadsheet implements JsonSerializable
     {
         return array_filter(
             $this->definedNames,
-            function (DefinedName $definedName) {
-                return $definedName->isFormula() === self::DEFINED_NAME_IS_FORMULA;
-            }
+            fn (DefinedName $definedName): bool => $definedName->isFormula() === self::DEFINED_NAME_IS_FORMULA
         );
     }
 
@@ -1120,20 +1024,16 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * Get worksheet iterator.
-     *
-     * @return Iterator
      */
-    public function getWorksheetIterator()
+    public function getWorksheetIterator(): Iterator
     {
         return new Iterator($this);
     }
 
     /**
      * Copy workbook (!= clone!).
-     *
-     * @return Spreadsheet
      */
-    public function copy()
+    public function copy(): self
     {
         $filename = File::temporaryFilename();
         $writer = new XlsxWriter($this);
@@ -1160,19 +1060,15 @@ class Spreadsheet implements JsonSerializable
      *
      * @return Style[]
      */
-    public function getCellXfCollection()
+    public function getCellXfCollection(): array
     {
         return $this->cellXfCollection;
     }
 
     /**
      * Get cellXf by index.
-     *
-     * @param int $cellStyleIndex
-     *
-     * @return Style
      */
-    public function getCellXfByIndex($cellStyleIndex)
+    public function getCellXfByIndex(int $cellStyleIndex): Style
     {
         return $this->cellXfCollection[$cellStyleIndex];
     }
@@ -1180,11 +1076,9 @@ class Spreadsheet implements JsonSerializable
     /**
      * Get cellXf by hash code.
      *
-     * @param string $hashcode
-     *
      * @return false|Style
      */
-    public function getCellXfByHashCode($hashcode)
+    public function getCellXfByHashCode(string $hashcode): bool|Style
     {
         foreach ($this->cellXfCollection as $cellXf) {
             if ($cellXf->getHashCode() === $hashcode) {
@@ -1197,20 +1091,16 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * Check if style exists in style collection.
-     *
-     * @return bool
      */
-    public function cellXfExists(Style $cellStyleIndex)
+    public function cellXfExists(Style $cellStyleIndex): bool
     {
         return in_array($cellStyleIndex, $this->cellXfCollection, true);
     }
 
     /**
      * Get default style.
-     *
-     * @return Style
      */
-    public function getDefaultStyle()
+    public function getDefaultStyle(): Style
     {
         if (isset($this->cellXfCollection[0])) {
             return $this->cellXfCollection[0];
@@ -1233,7 +1123,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param int $cellStyleIndex Index to cellXf
      */
-    public function removeCellXfByIndex($cellStyleIndex): void
+    public function removeCellXfByIndex(int $cellStyleIndex): void
     {
         if ($cellStyleIndex > count($this->cellXfCollection) - 1) {
             throw new Exception('CellXf index is out of bounds.');
@@ -1260,10 +1150,8 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * Get the cellXf supervisor.
-     *
-     * @return Style
      */
-    public function getCellXfSupervisor()
+    public function getCellXfSupervisor(): Style
     {
         return $this->cellXfSupervisor;
     }
@@ -1273,7 +1161,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return Style[]
      */
-    public function getCellStyleXfCollection()
+    public function getCellStyleXfCollection(): array
     {
         return $this->cellStyleXfCollection;
     }
@@ -1282,10 +1170,8 @@ class Spreadsheet implements JsonSerializable
      * Get cellStyleXf by index.
      *
      * @param int $cellStyleIndex Index to cellXf
-     *
-     * @return Style
      */
-    public function getCellStyleXfByIndex($cellStyleIndex)
+    public function getCellStyleXfByIndex(int $cellStyleIndex): Style
     {
         return $this->cellStyleXfCollection[$cellStyleIndex];
     }
@@ -1293,11 +1179,9 @@ class Spreadsheet implements JsonSerializable
     /**
      * Get cellStyleXf by hash code.
      *
-     * @param string $hashcode
-     *
      * @return false|Style
      */
-    public function getCellStyleXfByHashCode($hashcode)
+    public function getCellStyleXfByHashCode(string $hashcode): bool|Style
     {
         foreach ($this->cellStyleXfCollection as $cellStyleXf) {
             if ($cellStyleXf->getHashCode() === $hashcode) {
@@ -1322,7 +1206,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param int $cellStyleIndex Index to cellXf
      */
-    public function removeCellStyleXfByIndex($cellStyleIndex): void
+    public function removeCellStyleXfByIndex(int $cellStyleIndex): void
     {
         if ($cellStyleIndex > count($this->cellStyleXfCollection) - 1) {
             throw new Exception('CellStyleXf index is out of bounds.');
@@ -1413,10 +1297,8 @@ class Spreadsheet implements JsonSerializable
 
     /**
      * Return the unique ID value assigned to this spreadsheet workbook.
-     *
-     * @return string
      */
-    public function getID()
+    public function getID(): string
     {
         return $this->uniqueID;
     }
@@ -1426,7 +1308,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return bool True if horizonal scroll bar is visible
      */
-    public function getShowHorizontalScroll()
+    public function getShowHorizontalScroll(): bool
     {
         return $this->showHorizontalScroll;
     }
@@ -1436,7 +1318,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param bool $showHorizontalScroll True if horizonal scroll bar is visible
      */
-    public function setShowHorizontalScroll($showHorizontalScroll): void
+    public function setShowHorizontalScroll(bool $showHorizontalScroll): void
     {
         $this->showHorizontalScroll = (bool) $showHorizontalScroll;
     }
@@ -1446,7 +1328,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return bool True if vertical scroll bar is visible
      */
-    public function getShowVerticalScroll()
+    public function getShowVerticalScroll(): bool
     {
         return $this->showVerticalScroll;
     }
@@ -1456,7 +1338,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param bool $showVerticalScroll True if vertical scroll bar is visible
      */
-    public function setShowVerticalScroll($showVerticalScroll): void
+    public function setShowVerticalScroll(bool $showVerticalScroll): void
     {
         $this->showVerticalScroll = (bool) $showVerticalScroll;
     }
@@ -1466,7 +1348,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return bool True if the sheet tabs are visible
      */
-    public function getShowSheetTabs()
+    public function getShowSheetTabs(): bool
     {
         return $this->showSheetTabs;
     }
@@ -1476,7 +1358,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param bool $showSheetTabs True if sheet tabs are visible
      */
-    public function setShowSheetTabs($showSheetTabs): void
+    public function setShowSheetTabs(bool $showSheetTabs): void
     {
         $this->showSheetTabs = (bool) $showSheetTabs;
     }
@@ -1486,7 +1368,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return bool true if workbook window is minimized
      */
-    public function getMinimized()
+    public function getMinimized(): bool
     {
         return $this->minimized;
     }
@@ -1496,7 +1378,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param bool $minimized true if workbook window is minimized
      */
-    public function setMinimized($minimized): void
+    public function setMinimized(bool $minimized): void
     {
         $this->minimized = (bool) $minimized;
     }
@@ -1507,7 +1389,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return bool true if workbook window is minimized
      */
-    public function getAutoFilterDateGrouping()
+    public function getAutoFilterDateGrouping(): bool
     {
         return $this->autoFilterDateGrouping;
     }
@@ -1518,7 +1400,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param bool $autoFilterDateGrouping true if workbook window is minimized
      */
-    public function setAutoFilterDateGrouping($autoFilterDateGrouping): void
+    public function setAutoFilterDateGrouping(bool $autoFilterDateGrouping): void
     {
         $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping;
     }
@@ -1528,7 +1410,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return int First sheet in book view
      */
-    public function getFirstSheetIndex()
+    public function getFirstSheetIndex(): int
     {
         return $this->firstSheetIndex;
     }
@@ -1538,7 +1420,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param int $firstSheetIndex First sheet in book view
      */
-    public function setFirstSheetIndex($firstSheetIndex): void
+    public function setFirstSheetIndex(int $firstSheetIndex): void
     {
         if ($firstSheetIndex >= 0) {
             $this->firstSheetIndex = (int) $firstSheetIndex;
@@ -1555,7 +1437,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return string Visible status
      */
-    public function getVisibility()
+    public function getVisibility(): string
     {
         return $this->visibility;
     }
@@ -1575,7 +1457,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param null|string $visibility visibility status of the workbook
      */
-    public function setVisibility($visibility): void
+    public function setVisibility(?string $visibility): void
     {
         if ($visibility === null) {
             $visibility = self::VISIBILITY_VISIBLE;
@@ -1594,7 +1476,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @return int Ratio between the workbook tabs bar and the horizontal scroll bar
      */
-    public function getTabRatio()
+    public function getTabRatio(): int
     {
         return $this->tabRatio;
     }
@@ -1605,7 +1487,7 @@ class Spreadsheet implements JsonSerializable
      *
      * @param int $tabRatio Ratio between the tabs bar and the horizontal scroll bar
      */
-    public function setTabRatio($tabRatio): void
+    public function setTabRatio(int $tabRatio): void
     {
         if ($tabRatio >= 0 && $tabRatio <= 1000) {
             $this->tabRatio = (int) $tabRatio;
@@ -1627,22 +1509,10 @@ class Spreadsheet implements JsonSerializable
         }
     }
 
-    /**
-     * Silliness to mollify Scrutinizer.
-     *
-     * @codeCoverageIgnore
-     */
-    public function getSharedComponent(): Style
-    {
-        return new Style();
-    }
-
     /**
      * @throws Exception
-     *
-     * @return mixed
      */
-    public function __serialize()
+    public function __serialize(): array
     {
         throw new Exception('Spreadsheet objects cannot be serialized');
     }
@@ -1654,4 +1524,61 @@ class Spreadsheet implements JsonSerializable
     {
         throw new Exception('Spreadsheet objects cannot be json encoded');
     }
+
+    public function resetThemeFonts(): void
+    {
+        $majorFontLatin = $this->theme->getMajorFontLatin();
+        $minorFontLatin = $this->theme->getMinorFontLatin();
+        foreach ($this->cellXfCollection as $cellStyleXf) {
+            $scheme = $cellStyleXf->getFont()->getScheme();
+            if ($scheme === 'major') {
+                $cellStyleXf->getFont()->setName($majorFontLatin)->setScheme($scheme);
+            } elseif ($scheme === 'minor') {
+                $cellStyleXf->getFont()->setName($minorFontLatin)->setScheme($scheme);
+            }
+        }
+        foreach ($this->cellStyleXfCollection as $cellStyleXf) {
+            $scheme = $cellStyleXf->getFont()->getScheme();
+            if ($scheme === 'major') {
+                $cellStyleXf->getFont()->setName($majorFontLatin)->setScheme($scheme);
+            } elseif ($scheme === 'minor') {
+                $cellStyleXf->getFont()->setName($minorFontLatin)->setScheme($scheme);
+            }
+        }
+    }
+
+    public function getTableByName(string $tableName): ?Table
+    {
+        $table = null;
+        foreach ($this->workSheetCollection as $sheet) {
+            $table = $sheet->getTableByName($tableName);
+            if ($table !== null) {
+                break;
+            }
+        }
+
+        return $table;
+    }
+
+    /**
+     * @return bool Success or failure
+     */
+    public function setExcelCalendar(int $baseYear): bool
+    {
+        if (($baseYear === Date::CALENDAR_WINDOWS_1900) || ($baseYear === Date::CALENDAR_MAC_1904)) {
+            $this->excelCalendar = $baseYear;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @return int Excel base date (1900 or 1904)
+     */
+    public function getExcelCalendar(): int
+    {
+        return $this->excelCalendar;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
index 68edfac..3aed77c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Alignment.php
@@ -94,52 +94,38 @@ class Alignment extends Supervisor
 
     /**
      * Horizontal alignment.
-     *
-     * @var null|string
      */
-    protected $horizontal = self::HORIZONTAL_GENERAL;
+    protected ?string $horizontal = self::HORIZONTAL_GENERAL;
 
     /**
      * Vertical alignment.
-     *
-     * @var null|string
      */
-    protected $vertical = self::VERTICAL_BOTTOM;
+    protected ?string $vertical = self::VERTICAL_BOTTOM;
 
     /**
      * Text rotation.
-     *
-     * @var null|int
      */
-    protected $textRotation = 0;
+    protected ?int $textRotation = 0;
 
     /**
      * Wrap text.
-     *
-     * @var bool
      */
-    protected $wrapText = false;
+    protected bool $wrapText = false;
 
     /**
      * Shrink to fit.
-     *
-     * @var bool
      */
-    protected $shrinkToFit = false;
+    protected bool $shrinkToFit = false;
 
     /**
      * Indent - only possible with horizontal alignment left and right.
-     *
-     * @var int
      */
-    protected $indent = 0;
+    protected int $indent = 0;
 
     /**
      * Read order.
-     *
-     * @var int
      */
-    protected $readOrder = 0;
+    protected int $readOrder = 0;
 
     /**
      * Create a new Alignment.
@@ -151,7 +137,7 @@ class Alignment extends Supervisor
      *                                       Leave this value at default unless you understand exactly what
      *                                          its ramifications are
      */
-    public function __construct($isSupervisor = false, $isConditional = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         // Supervisor?
         parent::__construct($isSupervisor);
@@ -166,12 +152,10 @@ class Alignment extends Supervisor
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return Alignment
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
         return $parent->getSharedComponent()->getAlignment();
@@ -179,12 +163,8 @@ class Alignment extends Supervisor
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
         return ['alignment' => $array];
     }
@@ -207,7 +187,7 @@ class Alignment extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())
@@ -241,10 +221,8 @@ class Alignment extends Supervisor
 
     /**
      * Get Horizontal.
-     *
-     * @return null|string
      */
-    public function getHorizontal()
+    public function getHorizontal(): null|string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHorizontal();
@@ -260,7 +238,7 @@ class Alignment extends Supervisor
      *
      * @return $this
      */
-    public function setHorizontal(string $horizontalAlignment)
+    public function setHorizontal(string $horizontalAlignment): static
     {
         $horizontalAlignment = strtolower($horizontalAlignment);
         if ($horizontalAlignment === self::HORIZONTAL_CENTER_CONTINUOUS_LC) {
@@ -279,10 +257,8 @@ class Alignment extends Supervisor
 
     /**
      * Get Vertical.
-     *
-     * @return null|string
      */
-    public function getVertical()
+    public function getVertical(): null|string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getVertical();
@@ -298,7 +274,7 @@ class Alignment extends Supervisor
      *
      * @return $this
      */
-    public function setVertical($verticalAlignment)
+    public function setVertical(string $verticalAlignment): static
     {
         $verticalAlignment = strtolower($verticalAlignment);
 
@@ -314,10 +290,8 @@ class Alignment extends Supervisor
 
     /**
      * Get TextRotation.
-     *
-     * @return null|int
      */
-    public function getTextRotation()
+    public function getTextRotation(): null|int
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getTextRotation();
@@ -329,11 +303,9 @@ class Alignment extends Supervisor
     /**
      * Set TextRotation.
      *
-     * @param int $angleInDegrees
-     *
      * @return $this
      */
-    public function setTextRotation($angleInDegrees)
+    public function setTextRotation(int $angleInDegrees): static
     {
         // Excel2007 value 255 => PhpSpreadsheet value -165
         if ($angleInDegrees == self::TEXTROTATION_STACK_EXCEL) {
@@ -357,10 +329,8 @@ class Alignment extends Supervisor
 
     /**
      * Get Wrap Text.
-     *
-     * @return bool
      */
-    public function getWrapText()
+    public function getWrapText(): bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getWrapText();
@@ -372,11 +342,9 @@ class Alignment extends Supervisor
     /**
      * Set Wrap Text.
      *
-     * @param bool $wrapped
-     *
      * @return $this
      */
-    public function setWrapText($wrapped)
+    public function setWrapText(bool $wrapped): static
     {
         if ($wrapped == '') {
             $wrapped = false;
@@ -393,10 +361,8 @@ class Alignment extends Supervisor
 
     /**
      * Get Shrink to fit.
-     *
-     * @return bool
      */
-    public function getShrinkToFit()
+    public function getShrinkToFit(): bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getShrinkToFit();
@@ -408,11 +374,9 @@ class Alignment extends Supervisor
     /**
      * Set Shrink to fit.
      *
-     * @param bool $shrink
-     *
      * @return $this
      */
-    public function setShrinkToFit($shrink)
+    public function setShrinkToFit(bool $shrink): static
     {
         if ($shrink == '') {
             $shrink = false;
@@ -429,10 +393,8 @@ class Alignment extends Supervisor
 
     /**
      * Get indent.
-     *
-     * @return int
      */
-    public function getIndent()
+    public function getIndent(): int
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getIndent();
@@ -444,18 +406,16 @@ class Alignment extends Supervisor
     /**
      * Set indent.
      *
-     * @param int $indent
-     *
      * @return $this
      */
-    public function setIndent($indent)
+    public function setIndent(int $indent): static
     {
         if ($indent > 0) {
             if (
-                $this->getHorizontal() != self::HORIZONTAL_GENERAL &&
-                $this->getHorizontal() != self::HORIZONTAL_LEFT &&
-                $this->getHorizontal() != self::HORIZONTAL_RIGHT &&
-                $this->getHorizontal() != self::HORIZONTAL_DISTRIBUTED
+                $this->getHorizontal() != self::HORIZONTAL_GENERAL
+                && $this->getHorizontal() != self::HORIZONTAL_LEFT
+                && $this->getHorizontal() != self::HORIZONTAL_RIGHT
+                && $this->getHorizontal() != self::HORIZONTAL_DISTRIBUTED
             ) {
                 $indent = 0; // indent not supported
             }
@@ -472,10 +432,8 @@ class Alignment extends Supervisor
 
     /**
      * Get read order.
-     *
-     * @return int
      */
-    public function getReadOrder()
+    public function getReadOrder(): int
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getReadOrder();
@@ -487,11 +445,9 @@ class Alignment extends Supervisor
     /**
      * Set read order.
      *
-     * @param int $readOrder
-     *
      * @return $this
      */
-    public function setReadOrder($readOrder)
+    public function setReadOrder(int $readOrder): static
     {
         if ($readOrder < 0 || $readOrder > 2) {
             $readOrder = 0;
@@ -511,21 +467,21 @@ class Alignment extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHashCode();
         }
 
         return md5(
-            $this->horizontal .
-            $this->vertical .
-            $this->textRotation .
-            ($this->wrapText ? 't' : 'f') .
-            ($this->shrinkToFit ? 't' : 'f') .
-            $this->indent .
-            $this->readOrder .
-            __CLASS__
+            $this->horizontal
+            . $this->vertical
+            . $this->textRotation
+            . ($this->wrapText ? 't' : 'f')
+            . ($this->shrinkToFit ? 't' : 'f')
+            . $this->indent
+            . $this->readOrder
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
index c6fb51f..0716256 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Border.php
@@ -21,25 +21,19 @@ class Border extends Supervisor
     const BORDER_SLANTDASHDOT = 'slantDashDot';
     const BORDER_THICK = 'thick';
     const BORDER_THIN = 'thin';
+    const BORDER_OMIT = 'omit'; // should be used only for Conditional
 
     /**
      * Border style.
-     *
-     * @var string
      */
-    protected $borderStyle = self::BORDER_NONE;
+    protected string $borderStyle = self::BORDER_NONE;
 
     /**
      * Border color.
-     *
-     * @var Color
      */
-    protected $color;
+    protected Color $color;
 
-    /**
-     * @var null|int
-     */
-    public $colorIndex;
+    public ?int $colorIndex = null;
 
     /**
      * Create a new Border.
@@ -48,7 +42,7 @@ class Border extends Supervisor
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($isSupervisor = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         // Supervisor?
         parent::__construct($isSupervisor);
@@ -60,50 +54,42 @@ class Border extends Supervisor
         if ($isSupervisor) {
             $this->color->bindParent($this, 'color');
         }
+        if ($isConditional) {
+            $this->borderStyle = self::BORDER_OMIT;
+        }
     }
 
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return Border
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
         /** @var Borders $sharedComponent */
         $sharedComponent = $parent->getSharedComponent();
-        switch ($this->parentPropertyName) {
-            case 'bottom':
-                return $sharedComponent->getBottom();
-            case 'diagonal':
-                return $sharedComponent->getDiagonal();
-            case 'left':
-                return $sharedComponent->getLeft();
-            case 'right':
-                return $sharedComponent->getRight();
-            case 'top':
-                return $sharedComponent->getTop();
-        }
 
-        throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.');
+        return match ($this->parentPropertyName) {
+            'bottom' => $sharedComponent->getBottom(),
+            'diagonal' => $sharedComponent->getDiagonal(),
+            'left' => $sharedComponent->getLeft(),
+            'right' => $sharedComponent->getRight(),
+            'top' => $sharedComponent->getTop(),
+            default => throw new PhpSpreadsheetException('Cannot get shared component for a pseudo-border.'),
+        };
     }
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
-        return $parent->/** @scrutinizer ignore-call */ getStyleArray([$this->parentPropertyName => $array]);
+        return $parent->getStyleArray([$this->parentPropertyName => $array]);
     }
 
     /**
@@ -124,7 +110,7 @@ class Border extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
@@ -142,10 +128,8 @@ class Border extends Supervisor
 
     /**
      * Get Border style.
-     *
-     * @return string
      */
-    public function getBorderStyle()
+    public function getBorderStyle(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getBorderStyle();
@@ -157,13 +141,12 @@ class Border extends Supervisor
     /**
      * Set Border style.
      *
-     * @param bool|string $style
-     *                            When passing a boolean, FALSE equates Border::BORDER_NONE
+     * @param bool|string $style When passing a boolean, FALSE equates Border::BORDER_NONE
      *                                and TRUE to Border::BORDER_MEDIUM
      *
      * @return $this
      */
-    public function setBorderStyle($style)
+    public function setBorderStyle(bool|string $style): static
     {
         if (empty($style)) {
             $style = self::BORDER_NONE;
@@ -183,10 +166,8 @@ class Border extends Supervisor
 
     /**
      * Get Border Color.
-     *
-     * @return Color
      */
-    public function getColor()
+    public function getColor(): Color
     {
         return $this->color;
     }
@@ -196,7 +177,7 @@ class Border extends Supervisor
      *
      * @return $this
      */
-    public function setColor(Color $color)
+    public function setColor(Color $color): static
     {
         // make sure parameter is a real color and not a supervisor
         $color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
@@ -216,16 +197,16 @@ class Border extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHashCode();
         }
 
         return md5(
-            $this->borderStyle .
-            $this->color->getHashCode() .
-            __CLASS__
+            $this->borderStyle
+            . $this->color->getHashCode()
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
index 56a5270..93a95e6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Borders.php
@@ -14,80 +14,58 @@ class Borders extends Supervisor
 
     /**
      * Left.
-     *
-     * @var Border
      */
-    protected $left;
+    protected Border $left;
 
     /**
      * Right.
-     *
-     * @var Border
      */
-    protected $right;
+    protected Border $right;
 
     /**
      * Top.
-     *
-     * @var Border
      */
-    protected $top;
+    protected Border $top;
 
     /**
      * Bottom.
-     *
-     * @var Border
      */
-    protected $bottom;
+    protected Border $bottom;
 
     /**
      * Diagonal.
-     *
-     * @var Border
      */
-    protected $diagonal;
+    protected Border $diagonal;
 
     /**
      * DiagonalDirection.
-     *
-     * @var int
      */
-    protected $diagonalDirection;
+    protected int $diagonalDirection;
 
     /**
      * All borders pseudo-border. Only applies to supervisor.
-     *
-     * @var Border
      */
-    protected $allBorders;
+    protected Border $allBorders;
 
     /**
      * Outline pseudo-border. Only applies to supervisor.
-     *
-     * @var Border
      */
-    protected $outline;
+    protected Border $outline;
 
     /**
      * Inside pseudo-border. Only applies to supervisor.
-     *
-     * @var Border
      */
-    protected $inside;
+    protected Border $inside;
 
     /**
      * Vertical pseudo-border. Only applies to supervisor.
-     *
-     * @var Border
      */
-    protected $vertical;
+    protected Border $vertical;
 
     /**
      * Horizontal pseudo-border. Only applies to supervisor.
-     *
-     * @var Border
      */
-    protected $horizontal;
+    protected Border $horizontal;
 
     /**
      * Create a new Borders.
@@ -96,27 +74,27 @@ class Borders extends Supervisor
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($isSupervisor = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         // Supervisor?
         parent::__construct($isSupervisor);
 
         // Initialise values
-        $this->left = new Border($isSupervisor);
-        $this->right = new Border($isSupervisor);
-        $this->top = new Border($isSupervisor);
-        $this->bottom = new Border($isSupervisor);
-        $this->diagonal = new Border($isSupervisor);
+        $this->left = new Border($isSupervisor, $isConditional);
+        $this->right = new Border($isSupervisor, $isConditional);
+        $this->top = new Border($isSupervisor, $isConditional);
+        $this->bottom = new Border($isSupervisor, $isConditional);
+        $this->diagonal = new Border($isSupervisor, $isConditional);
         $this->diagonalDirection = self::DIAGONAL_NONE;
 
         // Specially for supervisor
         if ($isSupervisor) {
             // Initialize pseudo-borders
-            $this->allBorders = new Border(true);
-            $this->outline = new Border(true);
-            $this->inside = new Border(true);
-            $this->vertical = new Border(true);
-            $this->horizontal = new Border(true);
+            $this->allBorders = new Border(true, $isConditional);
+            $this->outline = new Border(true, $isConditional);
+            $this->inside = new Border(true, $isConditional);
+            $this->vertical = new Border(true, $isConditional);
+            $this->horizontal = new Border(true, $isConditional);
 
             // bind parent if we are a supervisor
             $this->left->bindParent($this, 'left');
@@ -135,12 +113,10 @@ class Borders extends Supervisor
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return Borders
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
         return $parent->getSharedComponent()->getBorders();
@@ -148,12 +124,8 @@ class Borders extends Supervisor
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
         return ['borders' => $array];
     }
@@ -197,7 +169,7 @@ class Borders extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
@@ -233,60 +205,48 @@ class Borders extends Supervisor
 
     /**
      * Get Left.
-     *
-     * @return Border
      */
-    public function getLeft()
+    public function getLeft(): Border
     {
         return $this->left;
     }
 
     /**
      * Get Right.
-     *
-     * @return Border
      */
-    public function getRight()
+    public function getRight(): Border
     {
         return $this->right;
     }
 
     /**
      * Get Top.
-     *
-     * @return Border
      */
-    public function getTop()
+    public function getTop(): Border
     {
         return $this->top;
     }
 
     /**
      * Get Bottom.
-     *
-     * @return Border
      */
-    public function getBottom()
+    public function getBottom(): Border
     {
         return $this->bottom;
     }
 
     /**
      * Get Diagonal.
-     *
-     * @return Border
      */
-    public function getDiagonal()
+    public function getDiagonal(): Border
     {
         return $this->diagonal;
     }
 
     /**
      * Get AllBorders (pseudo-border). Only applies to supervisor.
-     *
-     * @return Border
      */
-    public function getAllBorders()
+    public function getAllBorders(): Border
     {
         if (!$this->isSupervisor) {
             throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
@@ -297,10 +257,8 @@ class Borders extends Supervisor
 
     /**
      * Get Outline (pseudo-border). Only applies to supervisor.
-     *
-     * @return Border
      */
-    public function getOutline()
+    public function getOutline(): Border
     {
         if (!$this->isSupervisor) {
             throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
@@ -311,10 +269,8 @@ class Borders extends Supervisor
 
     /**
      * Get Inside (pseudo-border). Only applies to supervisor.
-     *
-     * @return Border
      */
-    public function getInside()
+    public function getInside(): Border
     {
         if (!$this->isSupervisor) {
             throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
@@ -325,10 +281,8 @@ class Borders extends Supervisor
 
     /**
      * Get Vertical (pseudo-border). Only applies to supervisor.
-     *
-     * @return Border
      */
-    public function getVertical()
+    public function getVertical(): Border
     {
         if (!$this->isSupervisor) {
             throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
@@ -339,10 +293,8 @@ class Borders extends Supervisor
 
     /**
      * Get Horizontal (pseudo-border). Only applies to supervisor.
-     *
-     * @return Border
      */
-    public function getHorizontal()
+    public function getHorizontal(): Border
     {
         if (!$this->isSupervisor) {
             throw new PhpSpreadsheetException('Can only get pseudo-border for supervisor.');
@@ -353,10 +305,8 @@ class Borders extends Supervisor
 
     /**
      * Get DiagonalDirection.
-     *
-     * @return int
      */
-    public function getDiagonalDirection()
+    public function getDiagonalDirection(): int
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getDiagonalDirection();
@@ -372,7 +322,7 @@ class Borders extends Supervisor
      *
      * @return $this
      */
-    public function setDiagonalDirection($direction)
+    public function setDiagonalDirection(int $direction): static
     {
         if ($direction == '') {
             $direction = self::DIAGONAL_NONE;
@@ -392,20 +342,20 @@ class Borders extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHashcode();
         }
 
         return md5(
-            $this->getLeft()->getHashCode() .
-            $this->getRight()->getHashCode() .
-            $this->getTop()->getHashCode() .
-            $this->getBottom()->getHashCode() .
-            $this->getDiagonal()->getHashCode() .
-            $this->getDiagonalDirection() .
-            __CLASS__
+            $this->getLeft()->getHashCode()
+            . $this->getRight()->getHashCode()
+            . $this->getTop()->getHashCode()
+            . $this->getBottom()->getHashCode()
+            . $this->getDiagonal()->getHashCode()
+            . $this->getDiagonalDirection()
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
index 3c002b2..6f9db8a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Color.php
@@ -106,13 +106,10 @@ class Color extends Supervisor
 
     /**
      * ARGB - Alpha RGB.
-     *
-     * @var null|string
      */
-    protected $argb;
+    protected ?string $argb = null;
 
-    /** @var bool */
-    private $hasChanged = false;
+    private bool $hasChanged = false;
 
     /**
      * Create a new Color.
@@ -125,7 +122,7 @@ class Color extends Supervisor
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($colorValue = self::COLOR_BLACK, $isSupervisor = false, $isConditional = false)
+    public function __construct(string $colorValue = self::COLOR_BLACK, bool $isSupervisor = false, bool $isConditional = false)
     {
         //    Supervisor?
         parent::__construct($isSupervisor);
@@ -139,12 +136,10 @@ class Color extends Supervisor
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return Color
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
         /** @var Border|Fill $sharedComponent */
         $sharedComponent = $parent->getSharedComponent();
@@ -161,17 +156,13 @@ class Color extends Supervisor
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
-        return $parent->/** @scrutinizer ignore-call */ getStyleArray([$this->parentPropertyName => $array]);
+        return $parent->getStyleArray([$this->parentPropertyName => $array]);
     }
 
     /**
@@ -185,7 +176,7 @@ class Color extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
@@ -235,11 +226,11 @@ class Color extends Supervisor
     /**
      * Set ARGB.
      *
-     * @param string $colorValue  ARGB value, or a named color
+     * @param ?string $colorValue  ARGB value, or a named color
      *
      * @return $this
      */
-    public function setARGB(?string $colorValue = self::COLOR_BLACK)
+    public function setARGB(?string $colorValue = self::COLOR_BLACK): static
     {
         $this->hasChanged = true;
         $colorValue = $this->validateColor($colorValue);
@@ -272,11 +263,11 @@ class Color extends Supervisor
     /**
      * Set RGB.
      *
-     * @param string $colorValue RGB value, or a named color
+     * @param ?string $colorValue RGB value, or a named color
      *
      * @return $this
      */
-    public function setRGB(?string $colorValue = self::COLOR_BLACK)
+    public function setRGB(?string $colorValue = self::COLOR_BLACK): static
     {
         return $this->setARGB($colorValue);
     }
@@ -291,7 +282,7 @@ class Color extends Supervisor
      *
      * @return int|string The extracted colour component
      */
-    private static function getColourComponent($rgbValue, $offset, $hex = true)
+    private static function getColourComponent(string $rgbValue, int $offset, bool $hex = true): string|int
     {
         $colour = substr($rgbValue, $offset, 2) ?: '';
         if (preg_match('/^[0-9a-f]{2}$/i', $colour) !== 1) {
@@ -310,7 +301,7 @@ class Color extends Supervisor
      *
      * @return int|string The red colour component
      */
-    public static function getRed($rgbValue, $hex = true)
+    public static function getRed(string $rgbValue, bool $hex = true)
     {
         return self::getColourComponent($rgbValue, strlen($rgbValue) - 6, $hex);
     }
@@ -324,7 +315,7 @@ class Color extends Supervisor
      *
      * @return int|string The green colour component
      */
-    public static function getGreen($rgbValue, $hex = true)
+    public static function getGreen(string $rgbValue, bool $hex = true)
     {
         return self::getColourComponent($rgbValue, strlen($rgbValue) - 4, $hex);
     }
@@ -338,7 +329,7 @@ class Color extends Supervisor
      *
      * @return int|string The blue colour component
      */
-    public static function getBlue($rgbValue, $hex = true)
+    public static function getBlue(string $rgbValue, bool $hex = true)
     {
         return self::getColourComponent($rgbValue, strlen($rgbValue) - 2, $hex);
     }
@@ -351,7 +342,7 @@ class Color extends Supervisor
      *
      * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
      */
-    public static function changeBrightness($hexColourValue, $adjustPercentage)
+    public static function changeBrightness(string $hexColourValue, float $adjustPercentage): string
     {
         $rgba = (strlen($hexColourValue) === 8);
         $adjustPercentage = max(-1.0, min(1.0, $adjustPercentage));
@@ -362,23 +353,8 @@ class Color extends Supervisor
         $green = self::getGreen($hexColourValue, false);
         /** @var int $blue */
         $blue = self::getBlue($hexColourValue, false);
-        if ($adjustPercentage > 0) {
-            $red += (255 - $red) * $adjustPercentage;
-            $green += (255 - $green) * $adjustPercentage;
-            $blue += (255 - $blue) * $adjustPercentage;
-        } else {
-            $red += $red * $adjustPercentage;
-            $green += $green * $adjustPercentage;
-            $blue += $blue * $adjustPercentage;
-        }
 
-        $rgb = strtoupper(
-            str_pad(dechex((int) $red), 2, '0', 0) .
-            str_pad(dechex((int) $green), 2, '0', 0) .
-            str_pad(dechex((int) $blue), 2, '0', 0)
-        );
-
-        return (($rgba) ? 'FF' : '') . $rgb;
+        return (($rgba) ? 'FF' : '') . RgbTint::rgbAndTintToRgb($red, $green, $blue, $adjustPercentage);
     }
 
     /**
@@ -388,7 +364,7 @@ class Color extends Supervisor
      * @param bool $background Flag to indicate whether default background or foreground colour
      *                                            should be returned if the indexed colour doesn't exist
      */
-    public static function indexedColor($colorIndex, $background = false, ?array $palette = null): self
+    public static function indexedColor(int $colorIndex, bool $background = false, ?array $palette = null): self
     {
         // Clean parameter
         $colorIndex = (int) $colorIndex;
@@ -418,8 +394,8 @@ class Color extends Supervisor
         }
 
         return md5(
-            $this->argb .
-            __CLASS__
+            $this->argb
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
index 019c064..01a4d8a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Conditional.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Style;
 
 use PhpOffice\PhpSpreadsheet\IComparable;
+use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalColorScale;
 use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;
 
 class Conditional implements IComparable
@@ -11,6 +12,7 @@ class Conditional implements IComparable
     const CONDITION_NONE = 'none';
     const CONDITION_BEGINSWITH = 'beginsWith';
     const CONDITION_CELLIS = 'cellIs';
+    const CONDITION_COLORSCALE = 'colorScale';
     const CONDITION_CONTAINSBLANKS = 'containsBlanks';
     const CONDITION_CONTAINSERRORS = 'containsErrors';
     const CONDITION_CONTAINSTEXT = 'containsText';
@@ -27,6 +29,7 @@ class Conditional implements IComparable
     private const CONDITION_TYPES = [
         self::CONDITION_BEGINSWITH,
         self::CONDITION_CELLIS,
+        self::CONDITION_COLORSCALE,
         self::CONDITION_CONTAINSBLANKS,
         self::CONDITION_CONTAINSERRORS,
         self::CONDITION_CONTAINSTEXT,
@@ -70,50 +73,38 @@ class Conditional implements IComparable
 
     /**
      * Condition type.
-     *
-     * @var string
      */
-    private $conditionType = self::CONDITION_NONE;
+    private string $conditionType = self::CONDITION_NONE;
 
     /**
      * Operator type.
-     *
-     * @var string
      */
-    private $operatorType = self::OPERATOR_NONE;
+    private string $operatorType = self::OPERATOR_NONE;
 
     /**
      * Text.
-     *
-     * @var string
      */
-    private $text;
+    private string $text = '';
 
     /**
      * Stop on this condition, if it matches.
-     *
-     * @var bool
      */
-    private $stopIfTrue = false;
+    private bool $stopIfTrue = false;
 
     /**
      * Condition.
      *
      * @var (bool|float|int|string)[]
      */
-    private $condition = [];
+    private array $condition = [];
 
-    /**
-     * @var ConditionalDataBar
-     */
-    private $dataBar;
+    private ?ConditionalDataBar $dataBar = null;
 
-    /**
-     * Style.
-     *
-     * @var Style
-     */
-    private $style;
+    private ?ConditionalColorScale $colorScale = null;
+
+    private Style $style;
+
+    private bool $noFormatSet = false;
 
     /**
      * Create a new Conditional.
@@ -124,12 +115,22 @@ class Conditional implements IComparable
         $this->style = new Style(false, true);
     }
 
+    public function getNoFormatSet(): bool
+    {
+        return $this->noFormatSet;
+    }
+
+    public function setNoFormatSet(bool $noFormatSet): self
+    {
+        $this->noFormatSet = $noFormatSet;
+
+        return $this;
+    }
+
     /**
      * Get Condition type.
-     *
-     * @return string
      */
-    public function getConditionType()
+    public function getConditionType(): string
     {
         return $this->conditionType;
     }
@@ -141,7 +142,7 @@ class Conditional implements IComparable
      *
      * @return $this
      */
-    public function setConditionType($type)
+    public function setConditionType(string $type): static
     {
         $this->conditionType = $type;
 
@@ -150,10 +151,8 @@ class Conditional implements IComparable
 
     /**
      * Get Operator type.
-     *
-     * @return string
      */
-    public function getOperatorType()
+    public function getOperatorType(): string
     {
         return $this->operatorType;
     }
@@ -165,7 +164,7 @@ class Conditional implements IComparable
      *
      * @return $this
      */
-    public function setOperatorType($type)
+    public function setOperatorType(string $type): static
     {
         $this->operatorType = $type;
 
@@ -174,10 +173,8 @@ class Conditional implements IComparable
 
     /**
      * Get text.
-     *
-     * @return string
      */
-    public function getText()
+    public function getText(): string
     {
         return $this->text;
     }
@@ -185,11 +182,9 @@ class Conditional implements IComparable
     /**
      * Set text.
      *
-     * @param string $text
-     *
      * @return $this
      */
-    public function setText($text)
+    public function setText(string $text): static
     {
         $this->text = $text;
 
@@ -198,10 +193,8 @@ class Conditional implements IComparable
 
     /**
      * Get StopIfTrue.
-     *
-     * @return bool
      */
-    public function getStopIfTrue()
+    public function getStopIfTrue(): bool
     {
         return $this->stopIfTrue;
     }
@@ -209,11 +202,9 @@ class Conditional implements IComparable
     /**
      * Set StopIfTrue.
      *
-     * @param bool $stopIfTrue
-     *
      * @return $this
      */
-    public function setStopIfTrue($stopIfTrue)
+    public function setStopIfTrue(bool $stopIfTrue): static
     {
         $this->stopIfTrue = $stopIfTrue;
 
@@ -225,7 +216,7 @@ class Conditional implements IComparable
      *
      * @return (bool|float|int|string)[]
      */
-    public function getConditions()
+    public function getConditions(): array
     {
         return $this->condition;
     }
@@ -233,11 +224,11 @@ class Conditional implements IComparable
     /**
      * Set Conditions.
      *
-     * @param bool|float|int|string|(bool|float|int|string)[] $conditions Condition
+     * @param bool|(bool|float|int|string)[]|float|int|string $conditions Condition
      *
      * @return $this
      */
-    public function setConditions($conditions)
+    public function setConditions($conditions): static
     {
         if (!is_array($conditions)) {
             $conditions = [$conditions];
@@ -254,7 +245,7 @@ class Conditional implements IComparable
      *
      * @return $this
      */
-    public function addCondition($condition)
+    public function addCondition($condition): static
     {
         $this->condition[] = $condition;
 
@@ -263,10 +254,8 @@ class Conditional implements IComparable
 
     /**
      * Get Style.
-     *
-     * @return Style
      */
-    public function getStyle()
+    public function getStyle(): Style
     {
         return $this->style;
     }
@@ -276,48 +265,50 @@ class Conditional implements IComparable
      *
      * @return $this
      */
-    public function setStyle(Style $style)
+    public function setStyle(Style $style): static
     {
         $this->style = $style;
 
         return $this;
     }
 
-    /**
-     * get DataBar.
-     *
-     * @return null|ConditionalDataBar
-     */
-    public function getDataBar()
+    public function getDataBar(): ?ConditionalDataBar
     {
         return $this->dataBar;
     }
 
-    /**
-     * set DataBar.
-     *
-     * @return $this
-     */
-    public function setDataBar(ConditionalDataBar $dataBar)
+    public function setDataBar(ConditionalDataBar $dataBar): static
     {
         $this->dataBar = $dataBar;
 
         return $this;
     }
 
+    public function getColorScale(): ?ConditionalColorScale
+    {
+        return $this->colorScale;
+    }
+
+    public function setColorScale(ConditionalColorScale $colorScale): static
+    {
+        $this->colorScale = $colorScale;
+
+        return $this;
+    }
+
     /**
      * Get hash code.
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->conditionType .
-            $this->operatorType .
-            implode(';', $this->condition) .
-            $this->style->getHashCode() .
-            __CLASS__
+            $this->conditionType
+            . $this->operatorType
+            . implode(';', $this->condition)
+            . $this->style->getHashCode()
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
index 0f6f523..2a27907 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellMatcher.php
@@ -30,50 +30,23 @@ class CellMatcher
         Conditional::CONDITION_UNIQUE => "COUNTIF('%s'!%s,%s)=1",
     ];
 
-    /**
-     * @var Cell
-     */
-    protected $cell;
+    protected Cell $cell;
 
-    /**
-     * @var int
-     */
-    protected $cellRow;
+    protected int $cellRow;
 
-    /**
-     * @var Worksheet
-     */
-    protected $worksheet;
+    protected Worksheet $worksheet;
 
-    /**
-     * @var int
-     */
-    protected $cellColumn;
+    protected int $cellColumn;
 
-    /**
-     * @var string
-     */
-    protected $conditionalRange;
+    protected string $conditionalRange;
 
-    /**
-     * @var string
-     */
-    protected $referenceCell;
+    protected string $referenceCell;
 
-    /**
-     * @var int
-     */
-    protected $referenceRow;
+    protected int $referenceRow;
 
-    /**
-     * @var int
-     */
-    protected $referenceColumn;
+    protected int $referenceColumn;
 
-    /**
-     * @var Calculation
-     */
-    protected $engine;
+    protected Calculation $engine;
 
     public function __construct(Cell $cell, string $conditionalRange)
     {
@@ -111,47 +84,37 @@ class CellMatcher
         $cellAddress = "{$cellColumn}{$this->cellRow}";
         $this->cell = $this->worksheet->getCell($cellAddress);
 
-        switch ($conditional->getConditionType()) {
-            case Conditional::CONDITION_CELLIS:
-                return $this->processOperatorComparison($conditional);
-            case Conditional::CONDITION_DUPLICATES:
-            case Conditional::CONDITION_UNIQUE:
-                return $this->processDuplicatesComparison($conditional);
-            case Conditional::CONDITION_CONTAINSTEXT:
-                // Expression is NOT(ISERROR(SEARCH("<TEXT>",<Cell Reference>)))
-            case Conditional::CONDITION_NOTCONTAINSTEXT:
-                // Expression is ISERROR(SEARCH("<TEXT>",<Cell Reference>))
-            case Conditional::CONDITION_BEGINSWITH:
-                // Expression is LEFT(<Cell Reference>,LEN("<TEXT>"))="<TEXT>"
-            case Conditional::CONDITION_ENDSWITH:
-                // Expression is RIGHT(<Cell Reference>,LEN("<TEXT>"))="<TEXT>"
-            case Conditional::CONDITION_CONTAINSBLANKS:
-                // Expression is LEN(TRIM(<Cell Reference>))=0
-            case Conditional::CONDITION_NOTCONTAINSBLANKS:
-                // Expression is LEN(TRIM(<Cell Reference>))>0
-            case Conditional::CONDITION_CONTAINSERRORS:
-                // Expression is ISERROR(<Cell Reference>)
-            case Conditional::CONDITION_NOTCONTAINSERRORS:
-                // Expression is NOT(ISERROR(<Cell Reference>))
-            case Conditional::CONDITION_TIMEPERIOD:
-                // Expression varies, depending on specified timePeriod value, e.g.
-                // Yesterday FLOOR(<Cell Reference>,1)=TODAY()-1
-                // Today FLOOR(<Cell Reference>,1)=TODAY()
-                // Tomorrow FLOOR(<Cell Reference>,1)=TODAY()+1
-                // Last 7 Days AND(TODAY()-FLOOR(<Cell Reference>,1)<=6,FLOOR(<Cell Reference>,1)<=TODAY())
-            case Conditional::CONDITION_EXPRESSION:
-                return $this->processExpression($conditional);
-        }
-
-        return false;
+        return match ($conditional->getConditionType()) {
+            Conditional::CONDITION_CELLIS => $this->processOperatorComparison($conditional),
+            Conditional::CONDITION_DUPLICATES, Conditional::CONDITION_UNIQUE => $this->processDuplicatesComparison($conditional),
+            // Expression is NOT(ISERROR(SEARCH("<TEXT>",<Cell Reference>)))
+            Conditional::CONDITION_CONTAINSTEXT,
+            // Expression is ISERROR(SEARCH("<TEXT>",<Cell Reference>))
+            Conditional::CONDITION_NOTCONTAINSTEXT,
+            // Expression is LEFT(<Cell Reference>,LEN("<TEXT>"))="<TEXT>"
+            Conditional::CONDITION_BEGINSWITH,
+            // Expression is RIGHT(<Cell Reference>,LEN("<TEXT>"))="<TEXT>"
+            Conditional::CONDITION_ENDSWITH,
+            // Expression is LEN(TRIM(<Cell Reference>))=0
+            Conditional::CONDITION_CONTAINSBLANKS,
+            // Expression is LEN(TRIM(<Cell Reference>))>0
+            Conditional::CONDITION_NOTCONTAINSBLANKS,
+            // Expression is ISERROR(<Cell Reference>)
+            Conditional::CONDITION_CONTAINSERRORS,
+            // Expression is NOT(ISERROR(<Cell Reference>))
+            Conditional::CONDITION_NOTCONTAINSERRORS,
+            // Expression varies, depending on specified timePeriod value, e.g.
+            // Yesterday FLOOR(<Cell Reference>,1)=TODAY()-1
+            // Today FLOOR(<Cell Reference>,1)=TODAY()
+            // Tomorrow FLOOR(<Cell Reference>,1)=TODAY()+1
+            // Last 7 Days AND(TODAY()-FLOOR(<Cell Reference>,1)<=6,FLOOR(<Cell Reference>,1)<=TODAY())
+            Conditional::CONDITION_TIMEPERIOD,
+            Conditional::CONDITION_EXPRESSION => $this->processExpression($conditional),
+            default => false,
+        };
     }
 
-    /**
-     * @param mixed $value
-     *
-     * @return float|int|string
-     */
-    protected function wrapValue($value)
+    protected function wrapValue(mixed $value): float|int|string
     {
         if (!is_numeric($value)) {
             if (is_bool($value)) {
@@ -166,34 +129,28 @@ class CellMatcher
         return $value;
     }
 
-    /**
-     * @return float|int|string
-     */
-    protected function wrapCellValue()
+    protected function wrapCellValue(): float|int|string
     {
         return $this->wrapValue($this->cell->getCalculatedValue());
     }
 
-    /**
-     * @return float|int|string
-     */
-    protected function conditionCellAdjustment(array $matches)
+    protected function conditionCellAdjustment(array $matches): float|int|string
     {
         $column = $matches[6];
         $row = $matches[7];
 
-        if (strpos($column, '$') === false) {
+        if (!str_contains($column, '$')) {
             $column = Coordinate::columnIndexFromString($column);
             $column += $this->cellColumn - $this->referenceColumn;
             $column = Coordinate::stringFromColumnIndex($column);
         }
 
-        if (strpos($row, '$') === false) {
+        if (!str_contains($row, '$')) {
             $row += $this->cellRow - $this->referenceRow;
         }
 
         if (!empty($matches[4])) {
-            $worksheet = $this->worksheet->getParent()->getSheetByName(trim($matches[4], "'"));
+            $worksheet = $this->worksheet->getParentOrThrow()->getSheetByName(trim($matches[4], "'"));
             if ($worksheet === null) {
                 return $this->wrapValue(null);
             }
@@ -228,6 +185,7 @@ class CellMatcher
             }
         }
         unset($value);
+
         //    Then rebuild the condition string to return it
         return implode(Calculation::FORMULA_STRING_QUOTE, $splitCondition);
     }
@@ -277,7 +235,7 @@ class CellMatcher
             self::COMPARISON_DUPLICATES_OPERATORS[$conditional->getConditionType()],
             $worksheetName,
             $this->conditionalRange,
-            $this->cellConditionCheck($this->cell->getCalculatedValue())
+            $this->cellConditionCheck($this->cell->getCalculatedValueString())
         );
 
         return $this->evaluateExpression($expression);
@@ -304,7 +262,7 @@ class CellMatcher
         try {
             $this->engine->flushInstance();
             $result = (bool) $this->engine->calculateFormula($expression);
-        } catch (Exception $e) {
+        } catch (Exception) {
             return false;
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php
index 4c000db..bcf59de 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/CellStyleAssessor.php
@@ -8,15 +8,9 @@ use PhpOffice\PhpSpreadsheet\Style\Style;
 
 class CellStyleAssessor
 {
-    /**
-     * @var CellMatcher
-     */
-    protected $cellMatcher;
+    protected CellMatcher $cellMatcher;
 
-    /**
-     * @var StyleMerger
-     */
-    protected $styleMerger;
+    protected StyleMerger $styleMerger;
 
     public function __construct(Cell $cell, string $conditionalRange)
     {
@@ -30,7 +24,6 @@ class CellStyleAssessor
     public function matchConditions(array $conditionalStyles = []): Style
     {
         foreach ($conditionalStyles as $conditional) {
-            /** @var Conditional $conditional */
             if ($this->cellMatcher->evaluateConditional($conditional) === true) {
                 // Merging the conditional style into the base style goes in here
                 $this->styleMerger->mergeStyle($conditional->getStyle());
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php
new file mode 100644
index 0000000..7fcc080
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
+
+use PhpOffice\PhpSpreadsheet\Style\Color;
+
+class ConditionalColorScale
+{
+    private ?ConditionalFormatValueObject $minimumConditionalFormatValueObject = null;
+
+    private ?ConditionalFormatValueObject $midpointConditionalFormatValueObject = null;
+
+    private ?ConditionalFormatValueObject $maximumConditionalFormatValueObject = null;
+
+    private ?Color $minimumColor = null;
+
+    private ?Color $midpointColor = null;
+
+    private ?Color $maximumColor = null;
+
+    public function getMinimumConditionalFormatValueObject(): ?ConditionalFormatValueObject
+    {
+        return $this->minimumConditionalFormatValueObject;
+    }
+
+    public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject): self
+    {
+        $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
+
+        return $this;
+    }
+
+    public function getMidpointConditionalFormatValueObject(): ?ConditionalFormatValueObject
+    {
+        return $this->midpointConditionalFormatValueObject;
+    }
+
+    public function setMidpointConditionalFormatValueObject(ConditionalFormatValueObject $midpointConditionalFormatValueObject): self
+    {
+        $this->midpointConditionalFormatValueObject = $midpointConditionalFormatValueObject;
+
+        return $this;
+    }
+
+    public function getMaximumConditionalFormatValueObject(): ?ConditionalFormatValueObject
+    {
+        return $this->maximumConditionalFormatValueObject;
+    }
+
+    public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject): self
+    {
+        $this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
+
+        return $this;
+    }
+
+    public function getMinimumColor(): ?Color
+    {
+        return $this->minimumColor;
+    }
+
+    public function setMinimumColor(Color $minimumColor): self
+    {
+        $this->minimumColor = $minimumColor;
+
+        return $this;
+    }
+
+    public function getMidpointColor(): ?Color
+    {
+        return $this->midpointColor;
+    }
+
+    public function setMidpointColor(Color $midpointColor): self
+    {
+        $this->midpointColor = $midpointColor;
+
+        return $this;
+    }
+
+    public function getMaximumColor(): ?Color
+    {
+        return $this->maximumColor;
+    }
+
+    public function setMaximumColor(Color $maximumColor): self
+    {
+        $this->maximumColor = $maximumColor;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
index f7a2eee..370f102 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
@@ -4,39 +4,22 @@ namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
 
 class ConditionalDataBar
 {
-    /** <dataBar> attribute  */
+    private ?bool $showValue = null;
 
-    /** @var null|bool */
-    private $showValue;
+    private ?ConditionalFormatValueObject $minimumConditionalFormatValueObject = null;
 
-    /** <dataBar> children */
+    private ?ConditionalFormatValueObject $maximumConditionalFormatValueObject = null;
 
-    /** @var ?ConditionalFormatValueObject */
-    private $minimumConditionalFormatValueObject;
+    private string $color = '';
 
-    /** @var ?ConditionalFormatValueObject */
-    private $maximumConditionalFormatValueObject;
+    private ?ConditionalFormattingRuleExtension $conditionalFormattingRuleExt = null;
 
-    /** @var string */
-    private $color;
-
-    /** <extLst> */
-
-    /** @var ?ConditionalFormattingRuleExtension */
-    private $conditionalFormattingRuleExt;
-
-    /**
-     * @return null|bool
-     */
-    public function getShowValue()
+    public function getShowValue(): ?bool
     {
         return $this->showValue;
     }
 
-    /**
-     * @param bool $showValue
-     */
-    public function setShowValue($showValue)
+    public function setShowValue(bool $showValue): self
     {
         $this->showValue = $showValue;
 
@@ -48,7 +31,7 @@ class ConditionalDataBar
         return $this->minimumConditionalFormatValueObject;
     }
 
-    public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject)
+    public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject): self
     {
         $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
 
@@ -60,7 +43,7 @@ class ConditionalDataBar
         return $this->maximumConditionalFormatValueObject;
     }
 
-    public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject)
+    public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject): self
     {
         $this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
 
@@ -84,7 +67,7 @@ class ConditionalDataBar
         return $this->conditionalFormattingRuleExt;
     }
 
-    public function setConditionalFormattingRuleExt(ConditionalFormattingRuleExtension $conditionalFormattingRuleExt)
+    public function setConditionalFormattingRuleExt(ConditionalFormattingRuleExtension $conditionalFormattingRuleExt): self
     {
         $this->conditionalFormattingRuleExt = $conditionalFormattingRuleExt;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
index c709cf3..28cd94b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBarExtension.php
@@ -5,53 +5,39 @@ namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
 class ConditionalDataBarExtension
 {
     /** <dataBar> attributes */
+    private int $minLength;
 
-    /** @var int */
-    private $minLength;
+    private int $maxLength;
 
-    /** @var int */
-    private $maxLength;
+    private ?bool $border = null;
 
-    /** @var null|bool */
-    private $border;
+    private ?bool $gradient = null;
 
-    /** @var null|bool */
-    private $gradient;
+    private ?string $direction = null;
 
-    /** @var string */
-    private $direction;
+    private ?bool $negativeBarBorderColorSameAsPositive = null;
 
-    /** @var null|bool */
-    private $negativeBarBorderColorSameAsPositive;
-
-    /** @var string */
-    private $axisPosition;
+    private ?string $axisPosition = null;
 
     // <dataBar> children
 
-    /** @var ConditionalFormatValueObject */
-    private $maximumConditionalFormatValueObject;
+    private ConditionalFormatValueObject $maximumConditionalFormatValueObject;
 
-    /** @var ConditionalFormatValueObject */
-    private $minimumConditionalFormatValueObject;
+    private ConditionalFormatValueObject $minimumConditionalFormatValueObject;
 
-    /** @var string */
-    private $borderColor;
+    private ?string $borderColor = null;
 
-    /** @var string */
-    private $negativeFillColor;
+    private ?string $negativeFillColor = null;
 
-    /** @var string */
-    private $negativeBorderColor;
+    private ?string $negativeBorderColor = null;
 
-    /** @var array */
-    private $axisColor = [
+    private array $axisColor = [
         'rgb' => null,
         'theme' => null,
         'tint' => null,
     ];
 
-    public function getXmlAttributes()
+    public function getXmlAttributes(): array
     {
         $ret = [];
         foreach (['minLength', 'maxLength', 'direction', 'axisPosition'] as $attrKey) {
@@ -68,7 +54,7 @@ class ConditionalDataBarExtension
         return $ret;
     }
 
-    public function getXmlElements()
+    public function getXmlElements(): array
     {
         $ret = [];
         $elms = ['borderColor', 'negativeFillColor', 'negativeBorderColor'];
@@ -87,10 +73,7 @@ class ConditionalDataBarExtension
         return $ret;
     }
 
-    /**
-     * @return int
-     */
-    public function getMinLength()
+    public function getMinLength(): int
     {
         return $this->minLength;
     }
@@ -102,10 +85,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return int
-     */
-    public function getMaxLength()
+    public function getMaxLength(): int
     {
         return $this->maxLength;
     }
@@ -117,10 +97,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return null|bool
-     */
-    public function getBorder()
+    public function getBorder(): ?bool
     {
         return $this->border;
     }
@@ -132,10 +109,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return null|bool
-     */
-    public function getGradient()
+    public function getGradient(): ?bool
     {
         return $this->gradient;
     }
@@ -147,10 +121,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return string
-     */
-    public function getDirection()
+    public function getDirection(): ?string
     {
         return $this->direction;
     }
@@ -162,10 +133,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return null|bool
-     */
-    public function getNegativeBarBorderColorSameAsPositive()
+    public function getNegativeBarBorderColorSameAsPositive(): ?bool
     {
         return $this->negativeBarBorderColorSameAsPositive;
     }
@@ -177,10 +145,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return string
-     */
-    public function getAxisPosition()
+    public function getAxisPosition(): ?string
     {
         return $this->axisPosition;
     }
@@ -192,40 +157,31 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return ConditionalFormatValueObject
-     */
-    public function getMaximumConditionalFormatValueObject()
+    public function getMaximumConditionalFormatValueObject(): ConditionalFormatValueObject
     {
         return $this->maximumConditionalFormatValueObject;
     }
 
-    public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject)
+    public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject): self
     {
         $this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
 
         return $this;
     }
 
-    /**
-     * @return ConditionalFormatValueObject
-     */
-    public function getMinimumConditionalFormatValueObject()
+    public function getMinimumConditionalFormatValueObject(): ConditionalFormatValueObject
     {
         return $this->minimumConditionalFormatValueObject;
     }
 
-    public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject)
+    public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject): self
     {
         $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
 
         return $this;
     }
 
-    /**
-     * @return string
-     */
-    public function getBorderColor()
+    public function getBorderColor(): ?string
     {
         return $this->borderColor;
     }
@@ -237,10 +193,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return string
-     */
-    public function getNegativeFillColor()
+    public function getNegativeFillColor(): ?string
     {
         return $this->negativeFillColor;
     }
@@ -252,10 +205,7 @@ class ConditionalDataBarExtension
         return $this;
     }
 
-    /**
-     * @return string
-     */
-    public function getNegativeBorderColor()
+    public function getNegativeBorderColor(): ?string
     {
         return $this->negativeBorderColor;
     }
@@ -272,12 +222,7 @@ class ConditionalDataBarExtension
         return $this->axisColor;
     }
 
-    /**
-     * @param mixed $rgb
-     * @param null|mixed $theme
-     * @param null|mixed $tint
-     */
-    public function setAxisColor($rgb, $theme = null, $tint = null): self
+    public function setAxisColor(mixed $rgb, mixed $theme = null, mixed $tint = null): self
     {
         $this->axisColor = [
             'rgb' => $rgb,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
index 107969b..e6d1035 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormatValueObject.php
@@ -4,72 +4,49 @@ namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
 
 class ConditionalFormatValueObject
 {
-    private $type;
+    private string $type;
 
-    private $value;
+    private null|float|int|string $value;
 
-    private $cellFormula;
+    private ?string $cellFormula;
 
-    /**
-     * ConditionalFormatValueObject constructor.
-     *
-     * @param null|mixed $cellFormula
-     */
-    public function __construct($type, $value = null, $cellFormula = null)
+    public function __construct(string $type, null|float|int|string $value = null, ?string $cellFormula = null)
     {
         $this->type = $type;
         $this->value = $value;
         $this->cellFormula = $cellFormula;
     }
 
-    /**
-     * @return mixed
-     */
-    public function getType()
+    public function getType(): string
     {
         return $this->type;
     }
 
-    /**
-     * @param mixed $type
-     */
-    public function setType($type)
+    public function setType(string $type): self
     {
         $this->type = $type;
 
         return $this;
     }
 
-    /**
-     * @return mixed
-     */
-    public function getValue()
+    public function getValue(): null|float|int|string
     {
         return $this->value;
     }
 
-    /**
-     * @param mixed $value
-     */
-    public function setValue($value)
+    public function setValue(null|float|int|string $value): self
     {
         $this->value = $value;
 
         return $this;
     }
 
-    /**
-     * @return mixed
-     */
-    public function getCellFormula()
+    public function getCellFormula(): ?string
     {
         return $this->cellFormula;
     }
 
-    /**
-     * @param mixed $cellFormula
-     */
-    public function setCellFormula($cellFormula)
+    public function setCellFormula(?string $cellFormula): self
     {
         $this->cellFormula = $cellFormula;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
index 34c9cbc..c6c648b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
@@ -9,24 +9,20 @@ class ConditionalFormattingRuleExtension
 {
     const CONDITION_EXTENSION_DATABAR = 'dataBar';
 
-    /** <conditionalFormatting> attributes */
-    private $id;
+    private string $id;
 
     /** @var string Conditional Formatting Rule */
-    private $cfRule;
+    private string $cfRule;
 
-    /** <conditionalFormatting> children */
-
-    /** @var ConditionalDataBarExtension */
-    private $dataBar;
+    private ConditionalDataBarExtension $dataBar;
 
     /** @var string Sequence of References */
-    private $sqref;
+    private string $sqref = '';
 
     /**
      * ConditionalFormattingRuleExtension constructor.
      */
-    public function __construct($id = null, string $cfRule = self::CONDITION_EXTENSION_DATABAR)
+    public function __construct(?string $id = null, string $cfRule = self::CONDITION_EXTENSION_DATABAR)
     {
         if (null === $id) {
             $this->id = '{' . $this->generateUuid() . '}';
@@ -36,7 +32,7 @@ class ConditionalFormattingRuleExtension
         $this->cfRule = $cfRule;
     }
 
-    private function generateUuid()
+    private function generateUuid(): string
     {
         $chars = str_split('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx');
 
@@ -48,10 +44,10 @@ class ConditionalFormattingRuleExtension
             }
         }
 
-        return implode('', /** @scrutinizer ignore-type */ $chars);
+        return implode('', $chars);
     }
 
-    public static function parseExtLstXml($extLstXml)
+    public static function parseExtLstXml(?SimpleXMLElement $extLstXml): array
     {
         $conditionalFormattingRuleExtensions = [];
         $conditionalFormattingRuleExtensionXml = null;
@@ -96,6 +92,9 @@ class ConditionalFormattingRuleExtension
         SimpleXMLElement $dataBarXml
     ): void {
         $dataBarAttribute = $dataBarXml->attributes();
+        if ($dataBarAttribute === null) {
+            return;
+        }
         if ($dataBarAttribute->minLength) {
             $extDataBarObj->setMinLength((int) $dataBarAttribute->minLength);
         }
@@ -119,25 +118,35 @@ class ConditionalFormattingRuleExtension
         }
     }
 
-    private static function parseExtDataBarElementChildrenFromXml(ConditionalDataBarExtension $extDataBarObj, SimpleXMLElement $dataBarXml, $ns): void
+    private static function parseExtDataBarElementChildrenFromXml(ConditionalDataBarExtension $extDataBarObj, SimpleXMLElement $dataBarXml, array $ns): void
     {
         if ($dataBarXml->borderColor) {
-            $extDataBarObj->setBorderColor((string) $dataBarXml->borderColor->attributes()['rgb']);
+            $attributes = $dataBarXml->borderColor->attributes();
+            if ($attributes !== null) {
+                $extDataBarObj->setBorderColor((string) $attributes['rgb']);
+            }
         }
         if ($dataBarXml->negativeFillColor) {
-            $extDataBarObj->setNegativeFillColor((string) $dataBarXml->negativeFillColor->attributes()['rgb']);
+            $attributes = $dataBarXml->negativeFillColor->attributes();
+            if ($attributes !== null) {
+                $extDataBarObj->setNegativeFillColor((string) $attributes['rgb']);
+            }
         }
         if ($dataBarXml->negativeBorderColor) {
-            $extDataBarObj->setNegativeBorderColor((string) $dataBarXml->negativeBorderColor->attributes()['rgb']);
+            $attributes = $dataBarXml->negativeBorderColor->attributes();
+            if ($attributes !== null) {
+                $extDataBarObj->setNegativeBorderColor((string) $attributes['rgb']);
+            }
         }
         if ($dataBarXml->axisColor) {
             $axisColorAttr = $dataBarXml->axisColor->attributes();
-            $extDataBarObj->setAxisColor((string) $axisColorAttr['rgb'], (string) $axisColorAttr['theme'], (string) $axisColorAttr['tint']);
+            if ($axisColorAttr !== null) {
+                $extDataBarObj->setAxisColor((string) $axisColorAttr['rgb'], (string) $axisColorAttr['theme'], (string) $axisColorAttr['tint']);
+            }
         }
         $cfvoIndex = 0;
         foreach ($dataBarXml->cfvo as $cfvo) {
-            $f = (string) $cfvo->/** @scrutinizer ignore-call */ children($ns['xm'])->f;
-            /** @scrutinizer ignore-call */
+            $f = (string) $cfvo->children($ns['xm'])->f;
             $attributes = $cfvo->attributes();
             if (!($attributes)) {
                 continue;
@@ -153,18 +162,12 @@ class ConditionalFormattingRuleExtension
         }
     }
 
-    /**
-     * @return mixed
-     */
-    public function getId()
+    public function getId(): string
     {
         return $this->id;
     }
 
-    /**
-     * @param mixed $id
-     */
-    public function setId($id): self
+    public function setId(string $id): self
     {
         $this->id = $id;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
index 0ddc07f..9388848 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/StyleMerger.php
@@ -10,10 +10,7 @@ use PhpOffice\PhpSpreadsheet\Style\Style;
 
 class StyleMerger
 {
-    /**
-     * @var Style
-     */
-    protected $baseStyle;
+    protected Style $baseStyle;
 
     public function __construct(Style $baseStyle)
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
index d5d56a9..2791a2b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard.php
@@ -24,10 +24,7 @@ class Wizard
     public const VALUE_TYPE_CELL = 'cell';
     public const VALUE_TYPE_FORMULA = 'formula';
 
-    /**
-     * @var string
-     */
-    protected $cellRange;
+    protected string $cellRange;
 
     public function __construct(string $cellRange)
     {
@@ -36,60 +33,34 @@ class Wizard
 
     public function newRule(string $ruleType): WizardInterface
     {
-        switch ($ruleType) {
-            case self::CELL_VALUE:
-                return new Wizard\CellValue($this->cellRange);
-            case self::TEXT_VALUE:
-                return new Wizard\TextValue($this->cellRange);
-            case self::BLANKS:
-                return new Wizard\Blanks($this->cellRange, true);
-            case self::NOT_BLANKS:
-                return new Wizard\Blanks($this->cellRange, false);
-            case self::ERRORS:
-                return new Wizard\Errors($this->cellRange, true);
-            case self::NOT_ERRORS:
-                return new Wizard\Errors($this->cellRange, false);
-            case self::EXPRESSION:
-            case self::FORMULA:
-                return new Wizard\Expression($this->cellRange);
-            case self::DATES_OCCURRING:
-                return new Wizard\DateValue($this->cellRange);
-            case self::DUPLICATES:
-                return new Wizard\Duplicates($this->cellRange, false);
-            case self::UNIQUE:
-                return new Wizard\Duplicates($this->cellRange, true);
-            default:
-                throw new Exception('No wizard exists for this CF rule type');
-        }
+        return match ($ruleType) {
+            self::CELL_VALUE => new Wizard\CellValue($this->cellRange),
+            self::TEXT_VALUE => new Wizard\TextValue($this->cellRange),
+            self::BLANKS => new Wizard\Blanks($this->cellRange, true),
+            self::NOT_BLANKS => new Wizard\Blanks($this->cellRange, false),
+            self::ERRORS => new Wizard\Errors($this->cellRange, true),
+            self::NOT_ERRORS => new Wizard\Errors($this->cellRange, false),
+            self::EXPRESSION, self::FORMULA => new Wizard\Expression($this->cellRange),
+            self::DATES_OCCURRING => new Wizard\DateValue($this->cellRange),
+            self::DUPLICATES => new Wizard\Duplicates($this->cellRange, false),
+            self::UNIQUE => new Wizard\Duplicates($this->cellRange, true),
+            default => throw new Exception('No wizard exists for this CF rule type'),
+        };
     }
 
     public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
     {
         $conditionalType = $conditional->getConditionType();
 
-        switch ($conditionalType) {
-            case Conditional::CONDITION_CELLIS:
-                return Wizard\CellValue::fromConditional($conditional, $cellRange);
-            case Conditional::CONDITION_CONTAINSTEXT:
-            case Conditional::CONDITION_NOTCONTAINSTEXT:
-            case Conditional::CONDITION_BEGINSWITH:
-            case Conditional::CONDITION_ENDSWITH:
-                return Wizard\TextValue::fromConditional($conditional, $cellRange);
-            case Conditional::CONDITION_CONTAINSBLANKS:
-            case Conditional::CONDITION_NOTCONTAINSBLANKS:
-                return Wizard\Blanks::fromConditional($conditional, $cellRange);
-            case Conditional::CONDITION_CONTAINSERRORS:
-            case Conditional::CONDITION_NOTCONTAINSERRORS:
-                return Wizard\Errors::fromConditional($conditional, $cellRange);
-            case Conditional::CONDITION_TIMEPERIOD:
-                return Wizard\DateValue::fromConditional($conditional, $cellRange);
-            case Conditional::CONDITION_EXPRESSION:
-                return Wizard\Expression::fromConditional($conditional, $cellRange);
-            case Conditional::CONDITION_DUPLICATES:
-            case Conditional::CONDITION_UNIQUE:
-                return Wizard\Duplicates::fromConditional($conditional, $cellRange);
-            default:
-                throw new Exception('No wizard exists for this CF rule type');
-        }
+        return match ($conditionalType) {
+            Conditional::CONDITION_CELLIS => Wizard\CellValue::fromConditional($conditional, $cellRange),
+            Conditional::CONDITION_CONTAINSTEXT, Conditional::CONDITION_NOTCONTAINSTEXT, Conditional::CONDITION_BEGINSWITH, Conditional::CONDITION_ENDSWITH => Wizard\TextValue::fromConditional($conditional, $cellRange),
+            Conditional::CONDITION_CONTAINSBLANKS, Conditional::CONDITION_NOTCONTAINSBLANKS => Wizard\Blanks::fromConditional($conditional, $cellRange),
+            Conditional::CONDITION_CONTAINSERRORS, Conditional::CONDITION_NOTCONTAINSERRORS => Wizard\Errors::fromConditional($conditional, $cellRange),
+            Conditional::CONDITION_TIMEPERIOD => Wizard\DateValue::fromConditional($conditional, $cellRange),
+            Conditional::CONDITION_EXPRESSION => Wizard\Expression::fromConditional($conditional, $cellRange),
+            Conditional::CONDITION_DUPLICATES, Conditional::CONDITION_UNIQUE => Wizard\Duplicates::fromConditional($conditional, $cellRange),
+            default => throw new Exception('No wizard exists for this CF rule type'),
+        };
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
index 14f30d3..5a8fe9b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Blanks.php
@@ -26,10 +26,7 @@ class Blanks extends WizardAbstract implements WizardInterface
         Wizard::BLANKS => 'LEN(TRIM(%s))=0',
     ];
 
-    /**
-     * @var bool
-     */
-    protected $inverse;
+    protected bool $inverse;
 
     public function __construct(string $cellRange, bool $inverse = false)
     {
@@ -68,8 +65,8 @@ class Blanks extends WizardAbstract implements WizardInterface
     public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
     {
         if (
-            $conditional->getConditionType() !== Conditional::CONDITION_CONTAINSBLANKS &&
-            $conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSBLANKS
+            $conditional->getConditionType() !== Conditional::CONDITION_CONTAINSBLANKS
+            && $conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSBLANKS
         ) {
             throw new Exception('Conditional is not a Blanks CF Rule conditional');
         }
@@ -83,10 +80,9 @@ class Blanks extends WizardAbstract implements WizardInterface
     }
 
     /**
-     * @param string $methodName
      * @param mixed[] $arguments
      */
-    public function __call($methodName, $arguments): self
+    public function __call(string $methodName, array $arguments): self
     {
         if (!array_key_exists($methodName, self::OPERATORS)) {
             throw new Exception('Invalid Operation for Blanks CF Rule Wizard');
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
index 265b241..63f360b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/CellValue.php
@@ -36,16 +36,14 @@ class CellValue extends WizardAbstract implements WizardInterface
 
     protected const RANGE_OPERATORS = CellMatcher::COMPARISON_RANGE_OPERATORS;
 
-    /** @var string */
-    protected $operator = Conditional::OPERATOR_EQUAL;
+    protected string $operator = Conditional::OPERATOR_EQUAL;
 
-    /** @var array */
-    protected $operand = [0];
+    protected array $operand = [0];
 
     /**
      * @var string[]
      */
-    protected $operandValueType = [];
+    protected array $operandValueType = [];
 
     public function __construct(string $cellRange)
     {
@@ -61,10 +59,7 @@ class CellValue extends WizardAbstract implements WizardInterface
         $this->operator = $operator;
     }
 
-    /**
-     * @param mixed $operand
-     */
-    protected function operand(int $index, $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): void
+    protected function operand(int $index, mixed $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): void
     {
         if (is_string($operand)) {
             $operand = $this->validateOperand($operand, $operandValueType);
@@ -74,12 +69,8 @@ class CellValue extends WizardAbstract implements WizardInterface
         $this->operandValueType[$index] = $operandValueType;
     }
 
-    /**
-     * @param mixed $value
-     *
-     * @return float|int|string
-     */
-    protected function wrapValue($value, string $operandValueType)
+    /** @param null|bool|float|int|string $value value to be wrapped */
+    protected function wrapValue(mixed $value, string $operandValueType): float|int|string
     {
         if (!is_numeric($value) && !is_bool($value) && null !== $value) {
             if ($operandValueType === Wizard::VALUE_TYPE_LITERAL) {
@@ -117,7 +108,7 @@ class CellValue extends WizardAbstract implements WizardInterface
 
     protected static function unwrapString(string $condition): string
     {
-        if ((strpos($condition, '"') === 0) && (strpos(strrev($condition), '"') === 0)) {
+        if ((str_starts_with($condition, '"')) && (str_starts_with(strrev($condition), '"'))) {
             $condition = substr($condition, 1, -1);
         }
 
@@ -146,8 +137,8 @@ class CellValue extends WizardAbstract implements WizardInterface
                     $operandValueType = Wizard::VALUE_TYPE_CELL;
                     $condition = self::reverseAdjustCellRef($condition, $cellRange);
                 } elseif (
-                    preg_match('/\(\)/', $condition) ||
-                    preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
+                    preg_match('/\(\)/', $condition)
+                    || preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
                 ) {
                     $operandValueType = Wizard::VALUE_TYPE_FORMULA;
                     $condition = self::reverseAdjustCellRef($condition, $cellRange);
@@ -162,10 +153,9 @@ class CellValue extends WizardAbstract implements WizardInterface
     }
 
     /**
-     * @param string $methodName
      * @param mixed[] $arguments
      */
-    public function __call($methodName, $arguments): self
+    public function __call(string $methodName, array $arguments): self
     {
         if (!isset(self::MAGIC_OPERATIONS[$methodName]) && $methodName !== 'and') {
             throw new Exception('Invalid Operator for Cell Value CF Rule Wizard');
@@ -176,13 +166,7 @@ class CellValue extends WizardAbstract implements WizardInterface
                 throw new Exception('AND Value is only appropriate for range operators');
             }
 
-            // Scrutinizer ignores its own suggested workaround.
-            //$this->operand(1, /** @scrutinizer ignore-type */ ...$arguments);
-            if (count($arguments) < 2) {
-                $this->operand(1, $arguments[0]);
-            } else {
-                $this->operand(1, $arguments[0], $arguments[1]);
-            }
+            $this->operand(1, ...$arguments);
 
             return $this;
         }
@@ -192,7 +176,9 @@ class CellValue extends WizardAbstract implements WizardInterface
         if (count($arguments) < 2) {
             $this->operand(0, $arguments[0]);
         } else {
-            $this->operand(0, $arguments[0], $arguments[1]);
+            /** @var string */
+            $arg1 = $arguments[1];
+            $this->operand(0, $arguments[0], $arg1);
         }
 
         return $this;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
index 453dcdf..23477e9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/DateValue.php
@@ -46,8 +46,7 @@ class DateValue extends WizardAbstract implements WizardInterface
         Conditional::TIMEPERIOD_NEXT_MONTH => 'AND(MONTH(%s)=MONTH(EDATE(TODAY(),0+1)),YEAR(%s)=YEAR(EDATE(TODAY(),0+1)))',
     ];
 
-    /** @var string */
-    protected $operator;
+    protected string $operator;
 
     public function __construct(string $cellRange)
     {
@@ -95,10 +94,9 @@ class DateValue extends WizardAbstract implements WizardInterface
     }
 
     /**
-     * @param string $methodName
      * @param mixed[] $arguments
      */
-    public function __call($methodName, $arguments): self
+    public function __call(string $methodName, array $arguments): self
     {
         if (!isset(self::MAGIC_OPERATIONS[$methodName])) {
             throw new Exception('Invalid Operation for Date Value CF Rule Wizard');
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php
index 3f063fe..0fbeddb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Duplicates.php
@@ -16,10 +16,7 @@ class Duplicates extends WizardAbstract implements WizardInterface
         'unique' => true,
     ];
 
-    /**
-     * @var bool
-     */
-    protected $inverse;
+    protected bool $inverse;
 
     public function __construct(string $cellRange, bool $inverse = false)
     {
@@ -47,8 +44,8 @@ class Duplicates extends WizardAbstract implements WizardInterface
     public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
     {
         if (
-            $conditional->getConditionType() !== Conditional::CONDITION_DUPLICATES &&
-            $conditional->getConditionType() !== Conditional::CONDITION_UNIQUE
+            $conditional->getConditionType() !== Conditional::CONDITION_DUPLICATES
+            && $conditional->getConditionType() !== Conditional::CONDITION_UNIQUE
         ) {
             throw new Exception('Conditional is not a Duplicates CF Rule conditional');
         }
@@ -62,10 +59,9 @@ class Duplicates extends WizardAbstract implements WizardInterface
     }
 
     /**
-     * @param string $methodName
      * @param mixed[] $arguments
      */
-    public function __call($methodName, $arguments): self
+    public function __call(string $methodName, array $arguments): self
     {
         if (!array_key_exists($methodName, self::OPERATORS)) {
             throw new Exception('Invalid Operation for Errors CF Rule Wizard');
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
index 56b9086..03a2381 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Errors.php
@@ -22,10 +22,7 @@ class Errors extends WizardAbstract implements WizardInterface
         Wizard::ERRORS => 'ISERROR(%s)',
     ];
 
-    /**
-     * @var bool
-     */
-    protected $inverse;
+    protected bool $inverse;
 
     public function __construct(string $cellRange, bool $inverse = false)
     {
@@ -64,8 +61,8 @@ class Errors extends WizardAbstract implements WizardInterface
     public static function fromConditional(Conditional $conditional, string $cellRange = 'A1'): WizardInterface
     {
         if (
-            $conditional->getConditionType() !== Conditional::CONDITION_CONTAINSERRORS &&
-            $conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSERRORS
+            $conditional->getConditionType() !== Conditional::CONDITION_CONTAINSERRORS
+            && $conditional->getConditionType() !== Conditional::CONDITION_NOTCONTAINSERRORS
         ) {
             throw new Exception('Conditional is not an Errors CF Rule conditional');
         }
@@ -79,10 +76,9 @@ class Errors extends WizardAbstract implements WizardInterface
     }
 
     /**
-     * @param string $methodName
      * @param mixed[] $arguments
      */
-    public function __call($methodName, $arguments): self
+    public function __call(string $methodName, array $arguments): self
     {
         if (!array_key_exists($methodName, self::OPERATORS)) {
             throw new Exception('Invalid Operation for Errors CF Rule Wizard');
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php
index e6d29f7..3f95441 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/Expression.php
@@ -11,10 +11,7 @@ use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
  */
 class Expression extends WizardAbstract implements WizardInterface
 {
-    /**
-     * @var string
-     */
-    protected $expression;
+    protected string $expression;
 
     public function __construct(string $cellRange)
     {
@@ -57,18 +54,15 @@ class Expression extends WizardAbstract implements WizardInterface
     }
 
     /**
-     * @param string $methodName
-     * @param mixed[] $arguments
+     * @param string[] $arguments
      */
-    public function __call($methodName, $arguments): self
+    public function __call(string $methodName, array $arguments): self
     {
         if ($methodName !== 'formula') {
             throw new Exception('Invalid Operation for Expression CF Rule Wizard');
         }
 
-        // Scrutinizer ignores its own recommendation
-        //$this->expression(/** @scrutinizer ignore-type */ ...$arguments);
-        $this->expression($arguments[0]);
+        $this->expression(...$arguments);
 
         return $this;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
index 987cfbf..b25c4f4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/TextValue.php
@@ -40,16 +40,11 @@ class TextValue extends WizardAbstract implements WizardInterface
         Conditional::OPERATOR_ENDSWITH => 'RIGHT(%s,LEN(%s))=%s',
     ];
 
-    /** @var string */
-    protected $operator;
+    protected string $operator;
 
-    /** @var string */
-    protected $operand;
+    protected string $operand;
 
-    /**
-     * @var string
-     */
-    protected $operandValueType;
+    protected string $operandValueType;
 
     public function __construct(string $cellRange)
     {
@@ -85,8 +80,8 @@ class TextValue extends WizardAbstract implements WizardInterface
             : $this->cellConditionCheck($this->operand);
 
         if (
-            $this->operator === Conditional::OPERATOR_CONTAINSTEXT ||
-            $this->operator === Conditional::OPERATOR_NOTCONTAINS
+            $this->operator === Conditional::OPERATOR_CONTAINSTEXT
+            || $this->operator === Conditional::OPERATOR_NOTCONTAINS
         ) {
             $this->expression = sprintf(self::EXPRESSIONS[$this->operator], $operand, $this->referenceCell);
         } else {
@@ -131,8 +126,8 @@ class TextValue extends WizardAbstract implements WizardInterface
             $wizard->operandValueType = Wizard::VALUE_TYPE_CELL;
             $condition = self::reverseAdjustCellRef($condition, $cellRange);
         } elseif (
-            preg_match('/\(\)/', $condition) ||
-            preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
+            preg_match('/\(\)/', $condition)
+            || preg_match('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', $condition)
         ) {
             $wizard->operandValueType = Wizard::VALUE_TYPE_FORMULA;
         }
@@ -142,10 +137,9 @@ class TextValue extends WizardAbstract implements WizardInterface
     }
 
     /**
-     * @param string $methodName
      * @param mixed[] $arguments
      */
-    public function __call($methodName, $arguments): self
+    public function __call(string $methodName, array $arguments): self
     {
         if (!isset(self::MAGIC_OPERATIONS[$methodName])) {
             throw new Exception('Invalid Operation for Text Value CF Rule Wizard');
@@ -154,9 +148,15 @@ class TextValue extends WizardAbstract implements WizardInterface
         $this->operator(self::MAGIC_OPERATIONS[$methodName]);
         //$this->operand(...$arguments);
         if (count($arguments) < 2) {
-            $this->operand($arguments[0]);
+            /** @var string */
+            $arg0 = $arguments[0];
+            $this->operand($arg0);
         } else {
-            $this->operand($arguments[0], $arguments[1]);
+            /** @var string */
+            $arg0 = $arguments[0];
+            /** @var string */
+            $arg1 = $arguments[1];
+            $this->operand($arg0, $arg1);
         }
 
         return $this;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
index 5644aab..953a559 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php
@@ -9,40 +9,19 @@ use PhpOffice\PhpSpreadsheet\Style\Style;
 
 abstract class WizardAbstract
 {
-    /**
-     * @var ?Style
-     */
-    protected $style;
+    protected ?Style $style = null;
 
-    /**
-     * @var string
-     */
-    protected $expression;
+    protected string $expression;
 
-    /**
-     * @var string
-     */
-    protected $cellRange;
+    protected string $cellRange;
 
-    /**
-     * @var string
-     */
-    protected $referenceCell;
+    protected string $referenceCell;
 
-    /**
-     * @var int
-     */
-    protected $referenceRow;
+    protected int $referenceRow;
 
-    /**
-     * @var bool
-     */
-    protected $stopIfTrue = false;
+    protected bool $stopIfTrue = false;
 
-    /**
-     * @var int
-     */
-    protected $referenceColumn;
+    protected int $referenceColumn;
 
     public function __construct(string $cellRange)
     {
@@ -91,12 +70,12 @@ abstract class WizardAbstract
     protected function validateOperand(string $operand, string $operandValueType = Wizard::VALUE_TYPE_LITERAL): string
     {
         if (
-            $operandValueType === Wizard::VALUE_TYPE_LITERAL &&
-            substr($operand, 0, 1) === '"' &&
-            substr($operand, -1) === '"'
+            $operandValueType === Wizard::VALUE_TYPE_LITERAL
+            && str_starts_with($operand, '"')
+            && str_ends_with($operand, '"')
         ) {
             $operand = str_replace('""', '"', substr($operand, 1, -1));
-        } elseif ($operandValueType === Wizard::VALUE_TYPE_FORMULA && substr($operand, 0, 1) === '=') {
+        } elseif ($operandValueType === Wizard::VALUE_TYPE_FORMULA && str_starts_with($operand, '=')) {
             $operand = substr($operand, 1);
         }
 
@@ -109,13 +88,13 @@ abstract class WizardAbstract
         $column = $matches[6];
         $row = $matches[7];
 
-        if (strpos($column, '$') === false) {
+        if (!str_contains($column, '$')) {
             $column = Coordinate::columnIndexFromString($column);
             $column -= $referenceColumn - 1;
             $column = Coordinate::stringFromColumnIndex($column);
         }
 
-        if (strpos($row, '$') === false) {
+        if (!str_contains($row, '$')) {
             $row -= $referenceRow - 1;
         }
 
@@ -136,9 +115,7 @@ abstract class WizardAbstract
             if ($i) {
                 $value = (string) preg_replace_callback(
                     '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
-                    function ($matches) use ($referenceColumnIndex, $referenceRow) {
-                        return self::reverseCellAdjustment($matches, $referenceColumnIndex, $referenceRow);
-                    },
+                    fn ($matches): string => self::reverseCellAdjustment($matches, $referenceColumnIndex, $referenceRow),
                     $value
                 );
             }
@@ -155,13 +132,13 @@ abstract class WizardAbstract
         $column = $matches[6];
         $row = $matches[7];
 
-        if (strpos($column, '$') === false) {
+        if (!str_contains($column, '$')) {
             $column = Coordinate::columnIndexFromString($column);
             $column += $this->referenceColumn - 1;
             $column = Coordinate::stringFromColumnIndex($column);
         }
 
-        if (strpos($row, '$') === false) {
+        if (!str_contains($row, '$')) {
             $row += $this->referenceRow - 1;
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
index bd87a79..9ec5d73 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Fill.php
@@ -27,46 +27,31 @@ class Fill extends Supervisor
     const FILL_PATTERN_LIGHTVERTICAL = 'lightVertical';
     const FILL_PATTERN_MEDIUMGRAY = 'mediumGray';
 
-    /**
-     * @var null|int
-     */
-    public $startcolorIndex;
+    public ?int $startcolorIndex = null;
 
-    /**
-     * @var null|int
-     */
-    public $endcolorIndex;
+    public ?int $endcolorIndex = null;
 
     /**
      * Fill type.
-     *
-     * @var null|string
      */
-    protected $fillType = self::FILL_NONE;
+    protected ?string $fillType = self::FILL_NONE;
 
     /**
      * Rotation.
-     *
-     * @var float
      */
-    protected $rotation = 0.0;
+    protected float $rotation = 0.0;
 
     /**
      * Start color.
-     *
-     * @var Color
      */
-    protected $startColor;
+    protected Color $startColor;
 
     /**
      * End color.
-     *
-     * @var Color
      */
-    protected $endColor;
+    protected Color $endColor;
 
-    /** @var bool */
-    private $colorChanged = false;
+    private bool $colorChanged = false;
 
     /**
      * Create a new Fill.
@@ -78,7 +63,7 @@ class Fill extends Supervisor
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($isSupervisor = false, $isConditional = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         // Supervisor?
         parent::__construct($isSupervisor);
@@ -100,12 +85,10 @@ class Fill extends Supervisor
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return Fill
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
         return $parent->getSharedComponent()->getFill();
@@ -113,12 +96,8 @@ class Fill extends Supervisor
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
         return ['fill' => $array];
     }
@@ -145,7 +124,7 @@ class Fill extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
@@ -173,10 +152,8 @@ class Fill extends Supervisor
 
     /**
      * Get Fill Type.
-     *
-     * @return null|string
      */
-    public function getFillType()
+    public function getFillType(): ?string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getFillType();
@@ -192,7 +169,7 @@ class Fill extends Supervisor
      *
      * @return $this
      */
-    public function setFillType($fillType)
+    public function setFillType(string $fillType): static
     {
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['fillType' => $fillType]);
@@ -206,10 +183,8 @@ class Fill extends Supervisor
 
     /**
      * Get Rotation.
-     *
-     * @return float
      */
-    public function getRotation()
+    public function getRotation(): float
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getRotation();
@@ -221,11 +196,9 @@ class Fill extends Supervisor
     /**
      * Set Rotation.
      *
-     * @param float $angleInDegrees
-     *
      * @return $this
      */
-    public function setRotation($angleInDegrees)
+    public function setRotation(float $angleInDegrees): static
     {
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['rotation' => $angleInDegrees]);
@@ -239,10 +212,8 @@ class Fill extends Supervisor
 
     /**
      * Get Start Color.
-     *
-     * @return Color
      */
-    public function getStartColor()
+    public function getStartColor(): Color
     {
         return $this->startColor;
     }
@@ -252,7 +223,7 @@ class Fill extends Supervisor
      *
      * @return $this
      */
-    public function setStartColor(Color $color)
+    public function setStartColor(Color $color): static
     {
         $this->colorChanged = true;
         // make sure parameter is a real color and not a supervisor
@@ -270,10 +241,8 @@ class Fill extends Supervisor
 
     /**
      * Get End Color.
-     *
-     * @return Color
      */
-    public function getEndColor()
+    public function getEndColor(): Color
     {
         return $this->endColor;
     }
@@ -283,7 +252,7 @@ class Fill extends Supervisor
      *
      * @return $this
      */
-    public function setEndColor(Color $color)
+    public function setEndColor(Color $color): static
     {
         $this->colorChanged = true;
         // make sure parameter is a real color and not a supervisor
@@ -315,20 +284,21 @@ class Fill extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHashCode();
         }
+
         // Note that we don't care about colours for fill type NONE, but could have duplicate NONEs with
         //  different hashes if we don't explicitly prevent this
         return md5(
-            $this->getFillType() .
-            $this->getRotation() .
-            ($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '') .
-            ($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '') .
-            ((string) $this->getColorsChanged()) .
-            __CLASS__
+            $this->getFillType()
+            . $this->getRotation()
+            . ($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '')
+            . ($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '')
+            . ((string) $this->getColorsChanged())
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
index 3d7bc1b..b96cfca 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Font.php
@@ -13,99 +13,79 @@ class Font extends Supervisor
     const UNDERLINE_SINGLE = 'single';
     const UNDERLINE_SINGLEACCOUNTING = 'singleAccounting';
 
+    const CAP_ALL = 'all';
+    const CAP_SMALL = 'small';
+    const CAP_NONE = 'none';
+    private const VALID_CAPS = [self::CAP_ALL, self::CAP_SMALL, self::CAP_NONE];
+
+    protected ?string $cap = null;
+
     /**
      * Font Name.
-     *
-     * @var null|string
      */
-    protected $name = 'Calibri';
+    protected ?string $name = 'Calibri';
 
     /**
      * The following 7 are used only for chart titles, I think.
-     *
-     *@var string
      */
-    private $latin = '';
+    private string $latin = '';
 
-    /** @var string */
-    private $eastAsian = '';
+    private string $eastAsian = '';
 
-    /** @var string */
-    private $complexScript = '';
+    private string $complexScript = '';
 
-    /** @var int */
-    private $baseLine = 0;
+    private int $baseLine = 0;
 
-    /** @var string */
-    private $strikeType = '';
+    private string $strikeType = '';
 
-    /** @var ?ChartColor */
-    private $underlineColor;
+    private ?ChartColor $underlineColor = null;
 
-    /** @var ?ChartColor */
-    private $chartColor;
+    private ?ChartColor $chartColor = null;
     // end of chart title items
 
     /**
      * Font Size.
-     *
-     * @var null|float
      */
-    protected $size = 11;
+    protected ?float $size = 11;
 
     /**
      * Bold.
-     *
-     * @var null|bool
      */
-    protected $bold = false;
+    protected ?bool $bold = false;
 
     /**
      * Italic.
-     *
-     * @var null|bool
      */
-    protected $italic = false;
+    protected ?bool $italic = false;
 
     /**
      * Superscript.
-     *
-     * @var null|bool
      */
-    protected $superscript = false;
+    protected ?bool $superscript = false;
 
     /**
      * Subscript.
-     *
-     * @var null|bool
      */
-    protected $subscript = false;
+    protected ?bool $subscript = false;
 
     /**
      * Underline.
-     *
-     * @var null|string
      */
-    protected $underline = self::UNDERLINE_NONE;
+    protected ?string $underline = self::UNDERLINE_NONE;
 
     /**
      * Strikethrough.
-     *
-     * @var null|bool
      */
-    protected $strikethrough = false;
+    protected ?bool $strikethrough = false;
 
     /**
      * Foreground color.
-     *
-     * @var Color
      */
-    protected $color;
+    protected Color $color;
 
-    /**
-     * @var null|int
-     */
-    public $colorIndex;
+    public ?int $colorIndex = null;
+
+    protected string $scheme = '';
 
     /**
      * Create a new Font.
@@ -117,7 +97,7 @@ class Font extends Supervisor
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($isSupervisor = false, $isConditional = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         // Supervisor?
         parent::__construct($isSupervisor);
@@ -145,12 +125,10 @@ class Font extends Supervisor
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return Font
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
         return $parent->getSharedComponent()->getFont();
@@ -158,12 +136,8 @@ class Font extends Supervisor
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
         return ['font' => $array];
     }
@@ -190,7 +164,7 @@ class Font extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
@@ -231,6 +205,15 @@ class Font extends Supervisor
             if (isset($styleArray['size'])) {
                 $this->setSize($styleArray['size']);
             }
+            if (isset($styleArray['chartColor'])) {
+                $this->chartColor = $styleArray['chartColor'];
+            }
+            if (isset($styleArray['scheme'])) {
+                $this->setScheme($styleArray['scheme']);
+            }
+            if (isset($styleArray['cap'])) {
+                $this->setCap($styleArray['cap']);
+            }
         }
 
         return $this;
@@ -238,10 +221,8 @@ class Font extends Supervisor
 
     /**
      * Get Name.
-     *
-     * @return null|string
      */
-    public function getName()
+    public function getName(): ?string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getName();
@@ -278,13 +259,9 @@ class Font extends Supervisor
     }
 
     /**
-     * Set Name.
-     *
-     * @param string $fontname
-     *
-     * @return $this
+     * Set Name and turn off Scheme.
      */
-    public function setName($fontname)
+    public function setName(string $fontname): self
     {
         if ($fontname == '') {
             $fontname = 'Calibri';
@@ -296,7 +273,7 @@ class Font extends Supervisor
             $this->name = $fontname;
         }
 
-        return $this;
+        return $this->setScheme('');
     }
 
     public function setLatin(string $fontname): self
@@ -355,10 +332,8 @@ class Font extends Supervisor
 
     /**
      * Get Size.
-     *
-     * @return null|float
      */
-    public function getSize()
+    public function getSize(): ?float
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getSize();
@@ -374,7 +349,7 @@ class Font extends Supervisor
      *
      * @return $this
      */
-    public function setSize($sizeInPoints, bool $nullOk = false)
+    public function setSize(mixed $sizeInPoints, bool $nullOk = false): static
     {
         if (is_string($sizeInPoints) || is_int($sizeInPoints)) {
             $sizeInPoints = (float) $sizeInPoints; // $pValue = 0 if given string is not numeric
@@ -400,10 +375,8 @@ class Font extends Supervisor
 
     /**
      * Get Bold.
-     *
-     * @return null|bool
      */
-    public function getBold()
+    public function getBold(): ?bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getBold();
@@ -415,11 +388,9 @@ class Font extends Supervisor
     /**
      * Set Bold.
      *
-     * @param bool $bold
-     *
      * @return $this
      */
-    public function setBold($bold)
+    public function setBold(bool $bold): static
     {
         if ($bold == '') {
             $bold = false;
@@ -436,10 +407,8 @@ class Font extends Supervisor
 
     /**
      * Get Italic.
-     *
-     * @return null|bool
      */
-    public function getItalic()
+    public function getItalic(): ?bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getItalic();
@@ -451,11 +420,9 @@ class Font extends Supervisor
     /**
      * Set Italic.
      *
-     * @param bool $italic
-     *
      * @return $this
      */
-    public function setItalic($italic)
+    public function setItalic(bool $italic): static
     {
         if ($italic == '') {
             $italic = false;
@@ -472,10 +439,8 @@ class Font extends Supervisor
 
     /**
      * Get Superscript.
-     *
-     * @return null|bool
      */
-    public function getSuperscript()
+    public function getSuperscript(): ?bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getSuperscript();
@@ -489,7 +454,7 @@ class Font extends Supervisor
      *
      * @return $this
      */
-    public function setSuperscript(bool $superscript)
+    public function setSuperscript(bool $superscript): static
     {
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['superscript' => $superscript]);
@@ -506,10 +471,8 @@ class Font extends Supervisor
 
     /**
      * Get Subscript.
-     *
-     * @return null|bool
      */
-    public function getSubscript()
+    public function getSubscript(): ?bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getSubscript();
@@ -523,7 +486,7 @@ class Font extends Supervisor
      *
      * @return $this
      */
-    public function setSubscript(bool $subscript)
+    public function setSubscript(bool $subscript): static
     {
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['subscript' => $subscript]);
@@ -634,12 +597,17 @@ class Font extends Supervisor
         return $this;
     }
 
+    public function setChartColorFromObject(?ChartColor $chartColor): self
+    {
+        $this->chartColor = $chartColor;
+
+        return $this;
+    }
+
     /**
      * Get Underline.
-     *
-     * @return null|string
      */
-    public function getUnderline()
+    public function getUnderline(): ?string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getUnderline();
@@ -657,7 +625,7 @@ class Font extends Supervisor
      *
      * @return $this
      */
-    public function setUnderline($underlineStyle)
+    public function setUnderline($underlineStyle): static
     {
         if (is_bool($underlineStyle)) {
             $underlineStyle = ($underlineStyle) ? self::UNDERLINE_SINGLE : self::UNDERLINE_NONE;
@@ -676,10 +644,8 @@ class Font extends Supervisor
 
     /**
      * Get Strikethrough.
-     *
-     * @return null|bool
      */
-    public function getStrikethrough()
+    public function getStrikethrough(): ?bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getStrikethrough();
@@ -691,11 +657,9 @@ class Font extends Supervisor
     /**
      * Set Strikethrough.
      *
-     * @param bool $strikethru
-     *
      * @return $this
      */
-    public function setStrikethrough($strikethru)
+    public function setStrikethrough(bool $strikethru): static
     {
         if ($strikethru == '') {
             $strikethru = false;
@@ -713,10 +677,8 @@ class Font extends Supervisor
 
     /**
      * Get Color.
-     *
-     * @return Color
      */
-    public function getColor()
+    public function getColor(): Color
     {
         return $this->color;
     }
@@ -726,7 +688,7 @@ class Font extends Supervisor
      *
      * @return $this
      */
-    public function setColor(Color $color)
+    public function setColor(Color $color): static
     {
         // make sure parameter is a real color and not a supervisor
         $color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
@@ -758,23 +720,24 @@ class Font extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHashCode();
         }
 
         return md5(
-            $this->name .
-            $this->size .
-            ($this->bold ? 't' : 'f') .
-            ($this->italic ? 't' : 'f') .
-            ($this->superscript ? 't' : 'f') .
-            ($this->subscript ? 't' : 'f') .
-            $this->underline .
-            ($this->strikethrough ? 't' : 'f') .
-            $this->color->getHashCode() .
-            implode(
+            $this->name
+            . $this->size
+            . ($this->bold ? 't' : 'f')
+            . ($this->italic ? 't' : 'f')
+            . ($this->superscript ? 't' : 'f')
+            . ($this->subscript ? 't' : 'f')
+            . $this->underline
+            . ($this->strikethrough ? 't' : 'f')
+            . $this->color->getHashCode()
+            . $this->scheme
+            . implode(
                 '*',
                 [
                     $this->latin,
@@ -784,9 +747,10 @@ class Font extends Supervisor
                     $this->hashChartColor($this->chartColor),
                     $this->hashChartColor($this->underlineColor),
                     (string) $this->baseLine,
+                    (string) $this->cap,
                 ]
-            ) .
-            __CLASS__
+            )
+            . __CLASS__
         );
     }
 
@@ -795,6 +759,7 @@ class Font extends Supervisor
         $exportedArray = [];
         $this->exportArray2($exportedArray, 'baseLine', $this->getBaseLine());
         $this->exportArray2($exportedArray, 'bold', $this->getBold());
+        $this->exportArray2($exportedArray, 'cap', $this->getCap());
         $this->exportArray2($exportedArray, 'chartColor', $this->getChartColor());
         $this->exportArray2($exportedArray, 'color', $this->getColor());
         $this->exportArray2($exportedArray, 'complexScript', $this->getComplexScript());
@@ -802,6 +767,7 @@ class Font extends Supervisor
         $this->exportArray2($exportedArray, 'italic', $this->getItalic());
         $this->exportArray2($exportedArray, 'latin', $this->getLatin());
         $this->exportArray2($exportedArray, 'name', $this->getName());
+        $this->exportArray2($exportedArray, 'scheme', $this->getScheme());
         $this->exportArray2($exportedArray, 'size', $this->getSize());
         $this->exportArray2($exportedArray, 'strikethrough', $this->getStrikethrough());
         $this->exportArray2($exportedArray, 'strikeType', $this->getStrikeType());
@@ -812,4 +778,56 @@ class Font extends Supervisor
 
         return $exportedArray;
     }
+
+    public function getScheme(): string
+    {
+        if ($this->isSupervisor) {
+            return $this->getSharedComponent()->getScheme();
+        }
+
+        return $this->scheme;
+    }
+
+    public function setScheme(string $scheme): self
+    {
+        if ($scheme === '' || $scheme === 'major' || $scheme === 'minor') {
+            if ($this->isSupervisor) {
+                $styleArray = $this->getStyleArray(['scheme' => $scheme]);
+                $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+            } else {
+                $this->scheme = $scheme;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set capitalization attribute. If not one of the permitted
+     * values (all, small, or none), set it to null.
+     * This will be honored only for the font for chart titles.
+     * None is distinguished from null because null will inherit
+     * the current value, whereas 'none' will override it.
+     */
+    public function setCap(string $cap): self
+    {
+        $this->cap = in_array($cap, self::VALID_CAPS, true) ? $cap : null;
+
+        return $this;
+    }
+
+    public function getCap(): ?string
+    {
+        return $this->cap;
+    }
+
+    /**
+     * Implement PHP __clone to create a deep clone, not just a shallow copy.
+     */
+    public function __clone()
+    {
+        $this->color = clone $this->color;
+        $this->chartColor = ($this->chartColor === null) ? null : clone $this->chartColor;
+        $this->underlineColor = ($this->underlineColor === null) ? null : clone $this->underlineColor;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
index c2b11a6..9c9a545 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet\Style;
 
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
+
 class NumberFormat extends Supervisor
 {
     // Pre-defined formats
@@ -19,8 +21,6 @@ class NumberFormat extends Supervisor
     const FORMAT_PERCENTAGE_0 = '0.0%';
     const FORMAT_PERCENTAGE_00 = '0.00%';
 
-    /** @deprecated 1.26 use FORMAT_DATE_YYYYMMDD instead */
-    const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
     const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
     const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
     const FORMAT_DATE_DMYSLASH = 'd/m/yy';
@@ -28,10 +28,12 @@ class NumberFormat extends Supervisor
     const FORMAT_DATE_DMMINUS = 'd-m';
     const FORMAT_DATE_MYMINUS = 'm-yy';
     const FORMAT_DATE_XLSX14 = 'mm-dd-yy';
+    const FORMAT_DATE_XLSX14_ACTUAL = 'm/d/yyyy';
     const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
     const FORMAT_DATE_XLSX16 = 'd-mmm';
     const FORMAT_DATE_XLSX17 = 'mmm-yy';
     const FORMAT_DATE_XLSX22 = 'm/d/yy h:mm';
+    const FORMAT_DATE_XLSX22_ACTUAL = 'm/d/yyyy h:mm';
     const FORMAT_DATE_DATETIME = 'd/m/yy h:mm';
     const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
     const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
@@ -42,6 +44,7 @@ class NumberFormat extends Supervisor
     const FORMAT_DATE_TIME7 = 'i:s.S';
     const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
     const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';
+    const FORMAT_DATE_LONG_DATE = 'dddd, mmmm d, yyyy';
 
     const DATE_TIME_OR_DATETIME_ARRAY = [
         self::FORMAT_DATE_YYYYMMDD,
@@ -51,10 +54,12 @@ class NumberFormat extends Supervisor
         self::FORMAT_DATE_DMMINUS,
         self::FORMAT_DATE_MYMINUS,
         self::FORMAT_DATE_XLSX14,
+        self::FORMAT_DATE_XLSX14_ACTUAL,
         self::FORMAT_DATE_XLSX15,
         self::FORMAT_DATE_XLSX16,
         self::FORMAT_DATE_XLSX17,
         self::FORMAT_DATE_XLSX22,
+        self::FORMAT_DATE_XLSX22_ACTUAL,
         self::FORMAT_DATE_DATETIME,
         self::FORMAT_DATE_TIME1,
         self::FORMAT_DATE_TIME2,
@@ -65,6 +70,7 @@ class NumberFormat extends Supervisor
         self::FORMAT_DATE_TIME7,
         self::FORMAT_DATE_TIME8,
         self::FORMAT_DATE_YYYYMMDDSLASH,
+        self::FORMAT_DATE_LONG_DATE,
     ];
     const TIME_OR_DATETIME_ARRAY = [
         self::FORMAT_DATE_XLSX22,
@@ -79,33 +85,42 @@ class NumberFormat extends Supervisor
         self::FORMAT_DATE_TIME8,
     ];
 
-    const FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-';
-    const FORMAT_CURRENCY_USD = '$#,##0_-';
-    const FORMAT_CURRENCY_EUR_SIMPLE = '#,##0.00_-"€"';
-    const FORMAT_CURRENCY_EUR = '#,##0_-"€"';
+    const FORMAT_CURRENCY_USD_INTEGER = '$#,##0_-';
+    const FORMAT_CURRENCY_USD = '$#,##0.00_-';
+    const FORMAT_CURRENCY_EUR_INTEGER = '#,##0_-[$€]';
+    const FORMAT_CURRENCY_EUR = '#,##0.00_-[$€]';
     const FORMAT_ACCOUNTING_USD = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
     const FORMAT_ACCOUNTING_EUR = '_("€"* #,##0.00_);_("€"* \(#,##0.00\);_("€"* "-"??_);_(@_)';
 
+    const SHORT_DATE_INDEX = 14;
+    const DATE_TIME_INDEX = 22;
+    const FORMAT_SYSDATE_X = '[$-x-sysdate]';
+    const FORMAT_SYSDATE_F800 = '[$-F800]';
+    const FORMAT_SYSTIME_X = '[$-x-systime]';
+    const FORMAT_SYSTIME_F400 = '[$-F400]';
+
+    protected static string $shortDateFormat = self::FORMAT_DATE_XLSX14_ACTUAL;
+
+    protected static string $longDateFormat = self::FORMAT_DATE_LONG_DATE;
+
+    protected static string $dateTimeFormat = self::FORMAT_DATE_XLSX22_ACTUAL;
+
+    protected static string $timeFormat = self::FORMAT_DATE_TIME2;
+
     /**
      * Excel built-in number formats.
-     *
-     * @var array
      */
-    protected static $builtInFormats;
+    protected static array $builtInFormats;
 
     /**
      * Excel built-in number formats (flipped, for faster lookups).
-     *
-     * @var array
      */
-    protected static $flippedBuiltInFormats;
+    protected static array $flippedBuiltInFormats;
 
     /**
      * Format Code.
-     *
-     * @var null|string
      */
-    protected $formatCode = self::FORMAT_GENERAL;
+    protected ?string $formatCode = self::FORMAT_GENERAL;
 
     /**
      * Built-in format Code.
@@ -124,7 +139,7 @@ class NumberFormat extends Supervisor
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($isSupervisor = false, $isConditional = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         // Supervisor?
         parent::__construct($isSupervisor);
@@ -138,12 +153,10 @@ class NumberFormat extends Supervisor
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return NumberFormat
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
         return $parent->getSharedComponent()->getNumberFormat();
@@ -151,12 +164,8 @@ class NumberFormat extends Supervisor
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
         return ['numberFormat' => $array];
     }
@@ -176,7 +185,7 @@ class NumberFormat extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
@@ -191,19 +200,41 @@ class NumberFormat extends Supervisor
 
     /**
      * Get Format Code.
-     *
-     * @return null|string
      */
-    public function getFormatCode()
+    public function getFormatCode(bool $extended = false): ?string
     {
         if ($this->isSupervisor) {
-            return $this->getSharedComponent()->getFormatCode();
+            return $this->getSharedComponent()->getFormatCode($extended);
         }
-        if (is_int($this->builtInFormatCode)) {
-            return self::builtInFormatCode($this->builtInFormatCode);
+        $builtin = $this->getBuiltInFormatCode();
+        if (is_int($builtin)) {
+            if ($extended) {
+                if ($builtin === self::SHORT_DATE_INDEX) {
+                    return self::$shortDateFormat;
+                }
+                if ($builtin === self::DATE_TIME_INDEX) {
+                    return self::$dateTimeFormat;
+                }
+            }
+
+            return self::builtInFormatCode($builtin);
         }
 
-        return $this->formatCode;
+        return $extended ? self::convertSystemFormats($this->formatCode) : $this->formatCode;
+    }
+
+    public static function convertSystemFormats(?string $formatCode): ?string
+    {
+        if (is_string($formatCode)) {
+            if (stripos($formatCode, self::FORMAT_SYSDATE_F800) !== false || stripos($formatCode, self::FORMAT_SYSDATE_X) !== false) {
+                return self::$longDateFormat;
+            }
+            if (stripos($formatCode, self::FORMAT_SYSTIME_F400) !== false || stripos($formatCode, self::FORMAT_SYSTIME_X) !== false) {
+                return self::$timeFormat;
+            }
+        }
+
+        return $formatCode;
     }
 
     /**
@@ -213,7 +244,7 @@ class NumberFormat extends Supervisor
      *
      * @return $this
      */
-    public function setFormatCode($formatCode)
+    public function setFormatCode(string $formatCode): static
     {
         if ($formatCode == '') {
             $formatCode = self::FORMAT_GENERAL;
@@ -240,18 +271,17 @@ class NumberFormat extends Supervisor
             return $this->getSharedComponent()->getBuiltInFormatCode();
         }
 
-        // Scrutinizer says this could return true. It is wrong.
         return $this->builtInFormatCode;
     }
 
     /**
      * Set Built-In Format Code.
      *
-     * @param int $formatCodeIndex
+     * @param int $formatCodeIndex Id of the built-in format code to use
      *
      * @return $this
      */
-    public function setBuiltInFormatCode($formatCodeIndex)
+    public function setBuiltInFormatCode(int $formatCodeIndex): static
     {
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['formatCode' => self::builtInFormatCode($formatCodeIndex)]);
@@ -307,15 +337,15 @@ class NumberFormat extends Supervisor
             self::$builtInFormats[11] = '0.00E+00';
             self::$builtInFormats[12] = '# ?/?';
             self::$builtInFormats[13] = '# ??/??';
-            self::$builtInFormats[14] = 'm/d/yyyy'; // Despite ECMA 'mm-dd-yy';
-            self::$builtInFormats[15] = 'd-mmm-yy';
+            self::$builtInFormats[14] = self::FORMAT_DATE_XLSX14_ACTUAL; // Despite ECMA 'mm-dd-yy';
+            self::$builtInFormats[15] = self::FORMAT_DATE_XLSX15;
             self::$builtInFormats[16] = 'd-mmm';
             self::$builtInFormats[17] = 'mmm-yy';
             self::$builtInFormats[18] = 'h:mm AM/PM';
             self::$builtInFormats[19] = 'h:mm:ss AM/PM';
             self::$builtInFormats[20] = 'h:mm';
             self::$builtInFormats[21] = 'h:mm:ss';
-            self::$builtInFormats[22] = 'm/d/yyyy h:mm'; // Despite ECMA 'm/d/yy h:mm';
+            self::$builtInFormats[22] = self::FORMAT_DATE_XLSX22_ACTUAL; // Despite ECMA 'm/d/yy h:mm';
 
             self::$builtInFormats[37] = '#,##0_);(#,##0)'; //  Despite ECMA '#,##0 ;(#,##0)';
             self::$builtInFormats[38] = '#,##0_);[Red](#,##0)'; //  Despite ECMA '#,##0 ;[Red](#,##0)';
@@ -369,12 +399,8 @@ class NumberFormat extends Supervisor
 
     /**
      * Get built-in format code.
-     *
-     * @param int $index
-     *
-     * @return string
      */
-    public static function builtInFormatCode($index)
+    public static function builtInFormatCode(int $index): string
     {
         // Clean parameter
         $index = (int) $index;
@@ -393,11 +419,9 @@ class NumberFormat extends Supervisor
     /**
      * Get built-in format code index.
      *
-     * @param string $formatCodeIndex
-     *
      * @return false|int
      */
-    public static function builtInFormatCodeIndex($formatCodeIndex)
+    public static function builtInFormatCodeIndex(string $formatCodeIndex)
     {
         // Ensure built-in format codes are available
         self::fillBuiltInFormatCodes();
@@ -415,29 +439,30 @@ class NumberFormat extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHashCode();
         }
 
         return md5(
-            $this->formatCode .
-            $this->builtInFormatCode .
-            __CLASS__
+            $this->formatCode
+            . $this->builtInFormatCode
+            . __CLASS__
         );
     }
 
     /**
      * Convert a value in a pre-defined format to a PHP string.
      *
-     * @param mixed $value Value to format
-     * @param string $format Format code, see = self::FORMAT_*
-     * @param array $callBack Callback function for additional formatting of string
+     * @param null|bool|float|int|RichText|string $value Value to format
+     * @param string $format Format code: see = self::FORMAT_* for predefined values;
+     *                          or can be any valid MS Excel custom format string
+     * @param ?array $callBack Callback function for additional formatting of string
      *
      * @return string Formatted string
      */
-    public static function toFormattedString($value, $format, $callBack = null)
+    public static function toFormattedString(mixed $value, string $format, ?array $callBack = null): string
     {
         return NumberFormat\Formatter::toFormattedString($value, $format, $callBack);
     }
@@ -449,4 +474,44 @@ class NumberFormat extends Supervisor
 
         return $exportedArray;
     }
+
+    public static function getShortDateFormat(): string
+    {
+        return self::$shortDateFormat;
+    }
+
+    public static function setShortDateFormat(string $shortDateFormat): void
+    {
+        self::$shortDateFormat = $shortDateFormat;
+    }
+
+    public static function getLongDateFormat(): string
+    {
+        return self::$longDateFormat;
+    }
+
+    public static function setLongDateFormat(string $longDateFormat): void
+    {
+        self::$longDateFormat = $longDateFormat;
+    }
+
+    public static function getDateTimeFormat(): string
+    {
+        return self::$dateTimeFormat;
+    }
+
+    public static function setDateTimeFormat(string $dateTimeFormat): void
+    {
+        self::$dateTimeFormat = $dateTimeFormat;
+    }
+
+    public static function getTimeFormat(): string
+    {
+        return self::$timeFormat;
+    }
+
+    public static function setTimeFormat(string $timeFormat): void
+    {
+        self::$timeFormat = $timeFormat;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php
index 7988143..165779b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/BaseFormatter.php
@@ -2,6 +2,8 @@
 
 namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
 abstract class BaseFormatter
 {
     protected static function stripQuotes(string $format): string
@@ -9,4 +11,15 @@ abstract class BaseFormatter
         // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
         return str_replace(['"', '*'], '', $format);
     }
+
+    protected static function adjustSeparators(string $value): string
+    {
+        $thousandsSeparator = StringHelper::getThousandsSeparator();
+        $decimalSeparator = StringHelper::getDecimalSeparator();
+        if ($thousandsSeparator !== ',' || $decimalSeparator !== '.') {
+            $value = str_replace(['.', ',', "\u{fffd}"], ["\u{fffd}", $thousandsSeparator, $decimalSeparator], $value);
+        }
+
+        return $value;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php
index ba54b53..2aabd7f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php
@@ -98,11 +98,10 @@ class DateFormatter
         '[ss]' => self::SECONDS_IN_DAY,
     ];
 
-    /** @param mixed $value */
-    private static function tryInterval(bool &$seekingBracket, string &$block, $value, string $format): void
+    private static function tryInterval(bool &$seekingBracket, string &$block, mixed $value, string $format): void
     {
         if ($seekingBracket) {
-            if (false !== strpos($block, $format)) {
+            if (str_contains($block, $format)) {
                 $hours = (string) (int) round(
                     self::INTERVAL_MULTIPLIER[$format] * $value,
                     self::INTERVAL_ROUND_PRECISION[$format]
@@ -116,8 +115,8 @@ class DateFormatter
         }
     }
 
-    /** @param mixed $value */
-    public static function format($value, string $format): string
+    /** @param float|int $value value to be formatted */
+    public static function format(mixed $value, string $format): string
     {
         // strip off first part containing e.g. [$-F800] or [$USD-409]
         // general syntax: [$<Currency string>-<language info>]
@@ -127,7 +126,7 @@ class DateFormatter
 
         // OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
         //    but we don't want to change any quoted strings
-        /** @var callable */
+        /** @var callable $callable */
         $callable = [self::class, 'setLowercaseCallback'];
         $format = (string) preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', $callable, $format);
 
@@ -157,7 +156,7 @@ class DateFormatter
         $format = implode('"', $blocks);
 
         // escape any quoted characters so that DateTime format() will render them correctly
-        /** @var callable */
+        /** @var callable $callback */
         $callback = [self::class, 'escapeQuotesCallback'];
         $format = (string) preg_replace_callback('/"(.*)"/U', $callback, $format);
 
@@ -166,6 +165,37 @@ class DateFormatter
         // Excel 2003 XML formats, m will not have been changed to i above.
         // Change it now.
         $format = (string) \preg_replace('/\\\\:m/', ':i', $format);
+        $microseconds = (int) $dateObj->format('u');
+        if (str_contains($format, ':s.000')) {
+            $milliseconds = (int) round($microseconds / 1000.0);
+            if ($milliseconds === 1000) {
+                $milliseconds = 0;
+                $dateObj->modify('+1 second');
+            }
+            $dateObj->modify("-$microseconds microseconds");
+            $format = str_replace(':s.000', ':s.' . sprintf('%03d', $milliseconds), $format);
+        } elseif (str_contains($format, ':s.00')) {
+            $centiseconds = (int) round($microseconds / 10000.0);
+            if ($centiseconds === 100) {
+                $centiseconds = 0;
+                $dateObj->modify('+1 second');
+            }
+            $dateObj->modify("-$microseconds microseconds");
+            $format = str_replace(':s.00', ':s.' . sprintf('%02d', $centiseconds), $format);
+        } elseif (str_contains($format, ':s.0')) {
+            $deciseconds = (int) round($microseconds / 100000.0);
+            if ($deciseconds === 10) {
+                $deciseconds = 0;
+                $dateObj->modify('+1 second');
+            }
+            $dateObj->modify("-$microseconds microseconds");
+            $format = str_replace(':s.0', ':s.' . sprintf('%1d', $deciseconds), $format);
+        } else { // no fractional second
+            if ($microseconds >= 500000) {
+                $dateObj->modify('+1 second');
+            }
+            $dateObj->modify("-$microseconds microseconds");
+        }
 
         return $dateObj->format($format);
     }
@@ -177,6 +207,6 @@ class DateFormatter
 
     private static function escapeQuotesCallback(array $matches): string
     {
-        return '\\' . implode('\\', /** @scrutinizer ignore-type */ str_split($matches[1]));
+        return '\\' . implode('\\', str_split($matches[1]));
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
index be195a8..f2d492e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
@@ -2,38 +2,48 @@
 
 namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Reader\Xls\Color\BIFF8;
+use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Style\Color;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
-class Formatter
+class Formatter extends BaseFormatter
 {
-    private static function splitFormatCompare($value, $cond, $val, $dfcond, $dfval)
-    {
-        if (!$cond) {
-            $cond = $dfcond;
-            $val = $dfval;
-        }
-        switch ($cond) {
-            case '>':
-                return $value > $val;
+    /**
+     * Matches any @ symbol that isn't enclosed in quotes.
+     */
+    private const SYMBOL_AT = '/@(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu';
 
-            case '<':
-                return $value < $val;
+    /**
+     * Matches any ; symbol that isn't enclosed in quotes, for a "section" split.
+     */
+    private const SECTION_SPLIT = '/;(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu';
 
-            case '<=':
-                return $value <= $val;
-
-            case '<>':
-                return $value != $val;
-
-            case '=':
-                return $value == $val;
+    private static function splitFormatComparison(
+        mixed $value,
+        ?string $condition,
+        mixed $comparisonValue,
+        string $defaultCondition,
+        mixed $defaultComparisonValue
+    ): bool {
+        if (!$condition) {
+            $condition = $defaultCondition;
+            $comparisonValue = $defaultComparisonValue;
         }
 
-        return $value >= $val;
+        return match ($condition) {
+            '>' => $value > $comparisonValue,
+            '<' => $value < $comparisonValue,
+            '<=' => $value <= $comparisonValue,
+            '<>' => $value != $comparisonValue,
+            '=' => $value == $comparisonValue,
+            default => $value >= $comparisonValue,
+        };
     }
 
-    private static function splitFormat($sections, $value)
+    /** @param float|int|string $value value to be formatted */
+    private static function splitFormatForSectionSelection(array $sections, mixed $value): array
     {
         // Extract the relevant section depending on whether number is positive, negative, or zero?
         // Text not supported yet.
@@ -42,30 +52,35 @@ class Formatter
         //   2 sections:  [POSITIVE/ZERO/TEXT] [NEGATIVE]
         //   3 sections:  [POSITIVE/TEXT] [NEGATIVE] [ZERO]
         //   4 sections:  [POSITIVE] [NEGATIVE] [ZERO] [TEXT]
-        $cnt = count($sections);
-        $color_regex = '/\\[(' . implode('|', Color::NAMED_COLORS) . ')\\]/mui';
+        $sectionCount = count($sections);
+        // Colour could be a named colour, or a numeric index entry in the colour-palette
+        $color_regex = '/\\[(' . implode('|', Color::NAMED_COLORS) . '|color\\s*(\\d+))\\]/mui';
         $cond_regex = '/\\[(>|>=|<|<=|=|<>)([+-]?\\d+([.]\\d+)?)\\]/';
         $colors = ['', '', '', '', ''];
-        $condops = ['', '', '', '', ''];
-        $condvals = [0, 0, 0, 0, 0];
-        for ($idx = 0; $idx < $cnt; ++$idx) {
+        $conditionOperations = ['', '', '', '', ''];
+        $conditionComparisonValues = [0, 0, 0, 0, 0];
+        for ($idx = 0; $idx < $sectionCount; ++$idx) {
             if (preg_match($color_regex, $sections[$idx], $matches)) {
-                $colors[$idx] = $matches[0];
+                if (isset($matches[2])) {
+                    $colors[$idx] = '#' . BIFF8::lookup((int) $matches[2] + 7)['rgb'];
+                } else {
+                    $colors[$idx] = $matches[0];
+                }
                 $sections[$idx] = (string) preg_replace($color_regex, '', $sections[$idx]);
             }
             if (preg_match($cond_regex, $sections[$idx], $matches)) {
-                $condops[$idx] = $matches[1];
-                $condvals[$idx] = $matches[2];
+                $conditionOperations[$idx] = $matches[1];
+                $conditionComparisonValues[$idx] = $matches[2];
                 $sections[$idx] = (string) preg_replace($cond_regex, '', $sections[$idx]);
             }
         }
         $color = $colors[0];
         $format = $sections[0];
         $absval = $value;
-        switch ($cnt) {
+        switch ($sectionCount) {
             case 2:
                 $absval = abs($value);
-                if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>=', 0)) {
+                if (!self::splitFormatComparison($value, $conditionOperations[0], $conditionComparisonValues[0], '>=', 0)) {
                     $color = $colors[1];
                     $format = $sections[1];
                 }
@@ -74,8 +89,8 @@ class Formatter
             case 3:
             case 4:
                 $absval = abs($value);
-                if (!self::splitFormatCompare($value, $condops[0], $condvals[0], '>', 0)) {
-                    if (self::splitFormatCompare($value, $condops[1], $condvals[1], '<', 0)) {
+                if (!self::splitFormatComparison($value, $conditionOperations[0], $conditionComparisonValues[0], '>', 0)) {
+                    if (self::splitFormatComparison($value, $conditionOperations[1], $conditionComparisonValues[1], '<', 0)) {
                         $color = $colors[1];
                         $format = $sections[1];
                     } else {
@@ -93,23 +108,33 @@ class Formatter
     /**
      * Convert a value in a pre-defined format to a PHP string.
      *
-     * @param mixed $value Value to format
-     * @param string $format Format code, see = NumberFormat::FORMAT_*
-     * @param array $callBack Callback function for additional formatting of string
+     * @param null|bool|float|int|RichText|string $value Value to format
+     * @param string $format Format code: see = self::FORMAT_* for predefined values;
+     *                          or can be any valid MS Excel custom format string
+     * @param ?array $callBack Callback function for additional formatting of string
      *
      * @return string Formatted string
      */
-    public static function toFormattedString($value, $format, $callBack = null)
+    public static function toFormattedString($value, string $format, ?array $callBack = null): string
     {
-        // For now we do not treat strings although section 4 of a format code affects strings
+        if (is_bool($value)) {
+            return $value ? Calculation::getTRUE() : Calculation::getFALSE();
+        }
+        // For now we do not treat strings in sections, although section 4 of a format code affects strings
+        // Process a single block format code containing @ for text substitution
+        if (preg_match(self::SECTION_SPLIT, $format) === 0 && preg_match(self::SYMBOL_AT, $format) === 1) {
+            return str_replace('"', '', preg_replace(self::SYMBOL_AT, (string) $value, $format) ?? '');
+        }
+
+        // If we have a text value, return it "as is"
         if (!is_numeric($value)) {
-            return $value;
+            return (string) $value;
         }
 
         // For 'General' format code, we just pass the value although this is not entirely the way Excel does it,
         // it seems to round numbers to a total of 10 digits.
         if (($format === NumberFormat::FORMAT_GENERAL) || ($format === NumberFormat::FORMAT_TEXT)) {
-            return $value;
+            return self::adjustSeparators((string) $value);
         }
 
         // Ignore square-$-brackets prefix in format string, like "[$-411]ge.m.d", "[$-010419]0%", etc
@@ -117,9 +142,7 @@ class Formatter
 
         $format = (string) preg_replace_callback(
             '/(["])(?:(?=(\\\\?))\\2.)*?\\1/u',
-            function ($matches) {
-                return str_replace('.', chr(0x00), $matches[0]);
-            },
+            fn (array $matches): string => str_replace('.', chr(0x00), $matches[0]),
             $format
         );
 
@@ -127,26 +150,29 @@ class Formatter
         $format = (string) preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/ui', '"${2}"', $format);
 
         // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
-        $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format);
+        $sections = preg_split(self::SECTION_SPLIT, $format) ?: [];
 
-        [$colors, $format, $value] = self::splitFormat($sections, $value);
+        [$colors, $format, $value] = self::splitFormatForSectionSelection($sections, $value);
 
         // In Excel formats, "_" is used to add spacing,
         //    The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space
         $format = (string) preg_replace('/_.?/ui', ' ', $format);
 
         // Let's begin inspecting the format and converting the value to a formatted string
-
-        //  Check for date/time characters (not inside quotes)
-        if (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format, $matches)) {
+        if (
+            //  Check for date/time characters (not inside quotes)
+            (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format))
+            // A date/time with a decimal time shouldn't have a digit placeholder before the decimal point
+            && (preg_match('/[0\?#]\.(?![^\[]*\])/miu', $format) === 0)
+        ) {
             // datetime format
             $value = DateFormatter::format($value, $format);
         } else {
-            if (substr($format, 0, 1) === '"' && substr($format, -1, 1) === '"' && substr_count($format, '"') === 2) {
+            if (str_starts_with($format, '"') && str_ends_with($format, '"') && substr_count($format, '"') === 2) {
                 $value = substr($format, 1, -1);
             } elseif (preg_match('/[0#, ]%/', $format)) {
-                // % number format
-                $value = PercentageFormatter::format($value, $format);
+                // % number format - avoid weird '-0' problem
+                $value = PercentageFormatter::format(0 + (float) $value, $format);
             } else {
                 $value = NumberFormatter::format($value, $format);
             }
@@ -158,8 +184,6 @@ class Formatter
             $value = $writerInstance->$function($value, $colors);
         }
 
-        $value = str_replace(chr(0x00), '.', $value);
-
-        return $value;
+        return str_replace(chr(0x00), '.', $value);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php
index d1fc89f..ff80aa2 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/FractionFormatter.php
@@ -6,10 +6,8 @@ use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
 
 class FractionFormatter extends BaseFormatter
 {
-    /**
-     * @param mixed $value
-     */
-    public static function format($value, string $format): string
+    /** @param null|bool|float|int|string $value  value to be formatted */
+    public static function format(mixed $value, string $format): string
     {
         $format = self::stripQuotes($format);
         $value = (float) $value;
@@ -26,23 +24,28 @@ class FractionFormatter extends BaseFormatter
         $decimalLength = strlen($decimalPart);
         $decimalDivisor = 10 ** $decimalLength;
 
-        /** @var float */
-        $GCD = MathTrig\Gcd::evaluate($decimalPart, $decimalDivisor);
-        /** @var float */
-        $decimalPartx = $decimalPart;
+        preg_match('/(#?.*\?)\/(\?+|\d+)/', $format, $matches);
+        $formatIntegerPart = $matches[1];
 
-        $adjustedDecimalPart = $decimalPartx / $GCD;
-        $adjustedDecimalDivisor = $decimalDivisor / $GCD;
+        if (is_numeric($matches[2])) {
+            $fractionDivisor = 100 / (int) $matches[2];
+        } else {
+            /** @var float $fractionDivisor */
+            $fractionDivisor = MathTrig\Gcd::evaluate((int) $decimalPart, $decimalDivisor);
+        }
 
-        if ((strpos($format, '0') !== false)) {
+        $adjustedDecimalPart = (int) round((int) $decimalPart / $fractionDivisor, 0);
+        $adjustedDecimalDivisor = $decimalDivisor / $fractionDivisor;
+
+        if ((str_contains($formatIntegerPart, '0'))) {
             return "{$sign}{$integerPart} {$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
-        } elseif ((strpos($format, '#') !== false)) {
+        } elseif ((str_contains($formatIntegerPart, '#'))) {
             if ($integerPart == 0) {
                 return "{$sign}{$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
             }
 
             return "{$sign}{$integerPart} {$adjustedDecimalPart}/{$adjustedDecimalDivisor}";
-        } elseif ((substr($format, 0, 3) == '? ?')) {
+        } elseif ((str_starts_with($formatIntegerPart, '? ?'))) {
             if ($integerPart == 0) {
                 $integerPart = '';
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php
index 989f33a..80f70dc 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php
@@ -5,7 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
-class NumberFormatter
+class NumberFormatter extends BaseFormatter
 {
     private const NUMBER_REGEX = '/(0+)(\\.?)(0*)/';
 
@@ -28,12 +28,9 @@ class NumberFormatter
         ];
     }
 
-    /**
-     * @param mixed $number
-     */
-    private static function processComplexNumberFormatMask($number, string $mask): string
+    private static function processComplexNumberFormatMask(mixed $number, string $mask): string
     {
-        /** @var string */
+        /** @var string $result */
         $result = $number;
         $maskingBlockCount = preg_match_all('/0+/', $mask, $maskingBlocks, PREG_OFFSET_CAPTURE);
 
@@ -46,13 +43,13 @@ class NumberFormatter
                 $divisor = 10 ** $size;
                 $offset = $block[1];
 
-                /** @var float */
+                /** @var float $numberFloat */
                 $numberFloat = $number;
                 $blockValue = sprintf("%0{$size}d", fmod($numberFloat, $divisor));
                 $number = floor($numberFloat / $divisor);
                 $mask = substr_replace($mask, $blockValue, $offset, $size);
             }
-            /** @var string */
+            /** @var string $numberString */
             $numberString = $number;
             if ($number > 0) {
                 $mask = substr_replace($mask, $numberString, $offset, 0);
@@ -63,12 +60,9 @@ class NumberFormatter
         return self::makeString($result);
     }
 
-    /**
-     * @param mixed $number
-     */
-    private static function complexNumberFormatMask($number, string $mask, bool $splitOnPoint = true): string
+    private static function complexNumberFormatMask(mixed $number, string $mask, bool $splitOnPoint = true): string
     {
-        /** @var float */
+        /** @var float $numberFloat */
         $numberFloat = $number;
         if ($splitOnPoint) {
             $masks = explode('.', $mask);
@@ -81,7 +75,7 @@ class NumberFormatter
         $sign = ($numberFloat < 0.0) ? '-' : '';
         $number = self::f2s(abs($numberFloat));
 
-        if ($splitOnPoint && strpos($mask, '.') !== false && strpos($number, '.') !== false) {
+        if ($splitOnPoint && str_contains($mask, '.') && str_contains($number, '.')) {
             $numbers = explode('.', $number);
             $masks = explode('.', $mask);
             if (count($masks) > 2) {
@@ -138,12 +132,9 @@ class NumberFormatter
         return $s;
     }
 
-    /**
-     * @param mixed $value
-     */
-    private static function formatStraightNumericValue($value, string $format, array $matches, bool $useThousands): string
+    private static function formatStraightNumericValue(mixed $value, string $format, array $matches, bool $useThousands): string
     {
-        /** @var float */
+        /** @var float $valueFloat */
         $valueFloat = $value;
         $left = $matches[1];
         $dec = $matches[2];
@@ -164,14 +155,17 @@ class NumberFormatter
 
         if (preg_match('/[0#]E[+-]0/i', $format)) {
             //    Scientific format
-            return sprintf('%5.2E', $valueFloat);
+            $decimals = strlen($right);
+            $size = $decimals + 3;
+
+            return sprintf("%{$size}.{$decimals}E", $valueFloat);
         } elseif (preg_match('/0([^\d\.]+)0/', $format) || substr_count($format, '.') > 1) {
             if ($valueFloat == floor($valueFloat) && substr_count($format, '.') === 1) {
                 $value *= 10 ** strlen(explode('.', $format)[1]);
             }
 
             $result = self::complexNumberFormatMask($value, $format);
-            if (strpos($result, 'E') !== false) {
+            if (str_contains($result, 'E')) {
                 // This is a hack and doesn't match Excel.
                 // It will, at least, be an accurate representation,
                 //  even if formatted incorrectly.
@@ -182,67 +176,57 @@ class NumberFormatter
             return $result;
         }
 
-        $sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
-        /** @var float */
+        $sprintf_pattern = "%0$minWidth." . strlen($right) . 'F';
+
+        /** @var float $valueFloat */
         $valueFloat = $value;
-        $value = sprintf($sprintf_pattern, round($valueFloat, strlen($right)));
+        $value = self::adjustSeparators(sprintf($sprintf_pattern, round($valueFloat, strlen($right))));
 
         return self::pregReplace(self::NUMBER_REGEX, $value, $format);
     }
 
-    /**
-     * @param mixed $value
-     */
-    public static function format($value, string $format): string
+    /** @param float|int|numeric-string $value value to be formatted */
+    public static function format(mixed $value, string $format): string
     {
         // The "_" in this string has already been stripped out,
         // so this test is never true. Furthermore, testing
         // on Excel shows this format uses Euro symbol, not "EUR".
-        //if ($format === NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE) {
-        //    return 'EUR ' . sprintf('%1.2f', $value);
-        //}
+        // if ($format === NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE) {
+        //     return 'EUR ' . sprintf('%1.2f', $value);
+        // }
 
-        // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
-        $format = self::makeString(str_replace(['"', '*'], '', $format));
+        $baseFormat = $format;
 
-        // Find out if we need thousands separator
-        // This is indicated by a comma enclosed by a digit placeholder:
-        //        #,#   or   0,0
-        $useThousands = (bool) preg_match('/(#,#|0,0)/', $format);
-        if ($useThousands) {
-            $format = self::pregReplace('/0,0/', '00', $format);
-            $format = self::pregReplace('/#,#/', '##', $format);
-        }
+        $useThousands = self::areThousandsRequired($format);
+        $scale = self::scaleThousandsMillions($format);
 
-        // Scale thousands, millions,...
-        // This is indicated by a number of commas after a digit placeholder:
-        //        #,   or    0.0,,
-        $scale = 1; // same as no scale
-        $matches = [];
-        if (preg_match('/(#|0)(,+)/', $format, $matches)) {
-            $scale = 1000 ** strlen($matches[2]);
-
-            // strip the commas
-            $format = self::pregReplace('/0,+/', '0', $format);
-            $format = self::pregReplace('/#,+/', '#', $format);
-        }
-        if (preg_match('/#?.*\?\/\?/', $format, $m)) {
+        if (preg_match('/[#\?0]?.*[#\?0]\/(\?+|\d+|#)/', $format)) {
+            // It's a dirty hack; but replace # and 0 digit placeholders with ?
+            $format = (string) preg_replace('/[#0]+\//', '?/', $format);
+            $format = (string) preg_replace('/\/[#0]+/', '/?', $format);
             $value = FractionFormatter::format($value, $format);
         } else {
             // Handle the number itself
-
             // scale number
             $value = $value / $scale;
-            // Strip #
-            $format = self::pregReplace('/\\#/', '0', $format);
-            // Remove locale code [$-###]
+            $paddingPlaceholder = (str_contains($format, '?'));
+
+            // Replace # or ? with 0
+            $format = self::pregReplace('/[\\#\?](?=(?:[^"]*"[^"]*")*[^"]*\Z)/', '0', $format);
+            // Remove locale code [$-###] for an LCID
             $format = self::pregReplace('/\[\$\-.*\]/', '', $format);
 
             $n = '/\\[[^\\]]+\\]/';
             $m = self::pregReplace($n, '', $format);
+
+            // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
+            $format = self::makeString(str_replace(['"', '*'], '', $format));
             if (preg_match(self::NUMBER_REGEX, $m, $matches)) {
                 // There are placeholders for digits, so inject digits from the value into the mask
                 $value = self::formatStraightNumericValue($value, $format, $matches, $useThousands);
+                if ($paddingPlaceholder === true) {
+                    $value = self::padValue($value, $baseFormat);
+                }
             } elseif ($format !== NumberFormat::FORMAT_GENERAL) {
                 // Yes, I know that this is basically just a hack;
                 //      if there's no placeholders for digits, just return the format mask "as is"
@@ -260,13 +244,17 @@ class NumberFormatter
             $value = self::pregReplace('/\[\$([^\]]*)\]/u', $currencyCode, (string) $value);
         }
 
+        if (
+            (str_contains((string) $value, '0.'))
+            && ((str_contains($baseFormat, '#.')) || (str_contains($baseFormat, '?.')))
+        ) {
+            $value = preg_replace('/(\b)0\.|([^\d])0\./', '${2}.', (string) $value);
+        }
+
         return (string) $value;
     }
 
-    /**
-     * @param array|string $value
-     */
-    private static function makeString($value): string
+    private static function makeString(array|string $value): string
     {
         return is_array($value) ? '' : "$value";
     }
@@ -275,4 +263,54 @@ class NumberFormatter
     {
         return self::makeString(preg_replace($pattern, $replacement, $subject) ?? '');
     }
+
+    public static function padValue(string $value, string $baseFormat): string
+    {
+        $preDecimal = $postDecimal = '';
+        $pregArray = preg_split('/\.(?=(?:[^"]*"[^"]*")*[^"]*\Z)/miu', $baseFormat . '.?');
+        if (is_array($pregArray)) {
+            $preDecimal = $pregArray[0] ?? '';
+            $postDecimal = $pregArray[1] ?? '';
+        }
+
+        $length = strlen($value);
+        if (str_contains($postDecimal, '?')) {
+            $value = str_pad(rtrim($value, '0. '), $length, ' ', STR_PAD_RIGHT);
+        }
+        if (str_contains($preDecimal, '?')) {
+            $value = str_pad(ltrim($value, '0, '), $length, ' ', STR_PAD_LEFT);
+        }
+
+        return $value;
+    }
+
+    /**
+     * Find out if we need thousands separator
+     * This is indicated by a comma enclosed by a digit placeholders: #, 0 or ?
+     */
+    public static function areThousandsRequired(string &$format): bool
+    {
+        $useThousands = (bool) preg_match('/([#\?0]),([#\?0])/', $format);
+        if ($useThousands) {
+            $format = self::pregReplace('/([#\?0]),([#\?0])/', '${1}${2}', $format);
+        }
+
+        return $useThousands;
+    }
+
+    /**
+     * Scale thousands, millions,...
+     * This is indicated by a number of commas after a digit placeholder: #, or 0.0,, or ?,.
+     */
+    public static function scaleThousandsMillions(string &$format): int
+    {
+        $scale = 1; // same as no scale
+        if (preg_match('/(#|0|\?)(,+)/', $format, $matches)) {
+            $scale = 1000 ** strlen($matches[2]);
+            // strip the commas
+            $format = self::pregReplace('/([#\?0]),+/', '${1}', $format);
+        }
+
+        return $scale;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
index 74f3877..bb5b355 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 
 class PercentageFormatter extends BaseFormatter
 {
+    /** @param float|int $value */
     public static function format($value, string $format): string
     {
         if ($format === NumberFormat::FORMAT_PERCENTAGE) {
@@ -19,7 +20,7 @@ class PercentageFormatter extends BaseFormatter
         $vDecimalCount = strlen(rtrim($vDecimals, '0'));
 
         $format = str_replace('%', '%%', $format);
-        $wholePartSize = strlen((string) floor($value));
+        $wholePartSize = strlen((string) floor(abs($value)));
         $decimalPartSize = 0;
         $placeHolders = '';
         // Number of decimals
@@ -37,11 +38,11 @@ class PercentageFormatter extends BaseFormatter
 
         $wholePartSize += $decimalPartSize + (int) ($decimalPartSize > 0);
         $replacement = "0{$wholePartSize}.{$decimalPartSize}";
-        $mask = (string) preg_replace('/[#0,]+\.?[?#0,]*/ui', "%{$replacement}f{$placeHolders}", $format);
+        $mask = (string) preg_replace('/[#0,]+\.?[?#0,]*/ui', "%{$replacement}F{$placeHolders}", $format);
 
-        /** @var float */
+        /** @var float $valueFloat */
         $valueFloat = $value;
 
-        return sprintf($mask, round($valueFloat, $decimalPartSize));
+        return self::adjustSeparators(sprintf($mask, round($valueFloat, $decimalPartSize)));
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php
new file mode 100644
index 0000000..43fd8ca
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Accounting.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use NumberFormatter;
+use PhpOffice\PhpSpreadsheet\Exception;
+
+class Accounting extends Currency
+{
+    /**
+     * @param string $currencyCode the currency symbol or code to display for this mask
+     * @param int $decimals number of decimal places to display, in the range 0-30
+     * @param bool $thousandsSeparator indicator whether the thousands separator should be used, or not
+     * @param bool $currencySymbolPosition indicates whether the currency symbol comes before or after the value
+     *              Possible values are Currency::LEADING_SYMBOL and Currency::TRAILING_SYMBOL
+     * @param bool $currencySymbolSpacing indicates whether there is spacing between the currency symbol and the value
+     *              Possible values are Currency::SYMBOL_WITH_SPACING and Currency::SYMBOL_WITHOUT_SPACING
+     * @param ?string $locale Set the locale for the currency format; or leave as the default null.
+     *          If provided, Locale values must be a valid formatted locale string (e.g. 'en-GB', 'fr', uz-Arab-AF).
+     *          Note that setting a locale will override any other settings defined in this class
+     *          other than the currency code; or decimals (unless the decimals value is set to 0).
+     *
+     * @throws Exception If a provided locale code is not a valid format
+     */
+    public function __construct(
+        string $currencyCode = '$',
+        int $decimals = 2,
+        bool $thousandsSeparator = true,
+        bool $currencySymbolPosition = self::LEADING_SYMBOL,
+        bool $currencySymbolSpacing = self::SYMBOL_WITHOUT_SPACING,
+        ?string $locale = null,
+        bool $stripLeadingRLM = self::DEFAULT_STRIP_LEADING_RLM
+    ) {
+        $this->setCurrencyCode($currencyCode);
+        $this->setThousandsSeparator($thousandsSeparator);
+        $this->setDecimals($decimals);
+        $this->setCurrencySymbolPosition($currencySymbolPosition);
+        $this->setCurrencySymbolSpacing($currencySymbolSpacing);
+        $this->setLocale($locale);
+        $this->stripLeadingRLM = $stripLeadingRLM;
+    }
+
+    /**
+     * @throws Exception if the Intl extension and ICU version don't support Accounting formats
+     */
+    protected function getLocaleFormat(): string
+    {
+        if (self::icuVersion() < 53.0) {
+            // @codeCoverageIgnoreStart
+            throw new Exception('The Intl extension does not support Accounting Formats without ICU 53');
+            // @codeCoverageIgnoreEnd
+        }
+
+        // Scrutinizer does not recognize CURRENCY_ACCOUNTING
+        $formatter = new Locale($this->fullLocale, NumberFormatter::CURRENCY_ACCOUNTING);
+        $mask = $formatter->format($this->stripLeadingRLM);
+        if ($this->decimals === 0) {
+            $mask = (string) preg_replace('/\.0+/miu', '', $mask);
+        }
+
+        return str_replace('¤', $this->formatCurrencyCode(), $mask);
+    }
+
+    public static function icuVersion(): float
+    {
+        [$major, $minor] = explode('.', INTL_ICU_VERSION);
+
+        return (float) "{$major}.{$minor}";
+    }
+
+    private function formatCurrencyCode(): string
+    {
+        if ($this->locale === null) {
+            return $this->currencyCode . '*';
+        }
+
+        return "[\${$this->currencyCode}-{$this->locale}]";
+    }
+
+    public function format(): string
+    {
+        if ($this->localeFormat !== null) {
+            return $this->localeFormat;
+        }
+
+        return sprintf(
+            '_-%s%s%s0%s%s%s_-',
+            $this->currencySymbolPosition === self::LEADING_SYMBOL ? $this->formatCurrencyCode() : null,
+            (
+                $this->currencySymbolPosition === self::LEADING_SYMBOL
+                && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+            ) ? "\u{a0}" : '',
+            $this->thousandsSeparator ? '#,##' : null,
+            $this->decimals > 0 ? '.' . str_repeat('0', $this->decimals) : null,
+            (
+                $this->currencySymbolPosition === self::TRAILING_SYMBOL
+                && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+            ) ? "\u{a0}" : '',
+            $this->currencySymbolPosition === self::TRAILING_SYMBOL ? $this->formatCurrencyCode() : null
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php
new file mode 100644
index 0000000..bba4d25
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Currency.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use NumberFormatter;
+use PhpOffice\PhpSpreadsheet\Exception;
+
+class Currency extends Number
+{
+    public const LEADING_SYMBOL = true;
+
+    public const TRAILING_SYMBOL = false;
+
+    public const SYMBOL_WITH_SPACING = true;
+
+    public const SYMBOL_WITHOUT_SPACING = false;
+
+    protected string $currencyCode = '$';
+
+    protected bool $currencySymbolPosition = self::LEADING_SYMBOL;
+
+    protected bool $currencySymbolSpacing = self::SYMBOL_WITHOUT_SPACING;
+
+    protected const DEFAULT_STRIP_LEADING_RLM = false;
+
+    protected bool $stripLeadingRLM = self::DEFAULT_STRIP_LEADING_RLM;
+
+    /**
+     * @param string $currencyCode the currency symbol or code to display for this mask
+     * @param int $decimals number of decimal places to display, in the range 0-30
+     * @param bool $thousandsSeparator indicator whether the thousands separator should be used, or not
+     * @param bool $currencySymbolPosition indicates whether the currency symbol comes before or after the value
+     *              Possible values are Currency::LEADING_SYMBOL and Currency::TRAILING_SYMBOL
+     * @param bool $currencySymbolSpacing indicates whether there is spacing between the currency symbol and the value
+     *              Possible values are Currency::SYMBOL_WITH_SPACING and Currency::SYMBOL_WITHOUT_SPACING
+     * @param ?string $locale Set the locale for the currency format; or leave as the default null.
+     *          If provided, Locale values must be a valid formatted locale string (e.g. 'en-GB', 'fr', uz-Arab-AF).
+     *          Note that setting a locale will override any other settings defined in this class
+     *          other than the currency code; or decimals (unless the decimals value is set to 0).
+     * @param bool $stripLeadingRLM remove leading RLM added with
+     *          ICU 72.1+.
+     *
+     * @throws Exception If a provided locale code is not a valid format
+     */
+    public function __construct(
+        string $currencyCode = '$',
+        int $decimals = 2,
+        bool $thousandsSeparator = true,
+        bool $currencySymbolPosition = self::LEADING_SYMBOL,
+        bool $currencySymbolSpacing = self::SYMBOL_WITHOUT_SPACING,
+        ?string $locale = null,
+        bool $stripLeadingRLM = self::DEFAULT_STRIP_LEADING_RLM
+    ) {
+        $this->setCurrencyCode($currencyCode);
+        $this->setThousandsSeparator($thousandsSeparator);
+        $this->setDecimals($decimals);
+        $this->setCurrencySymbolPosition($currencySymbolPosition);
+        $this->setCurrencySymbolSpacing($currencySymbolSpacing);
+        $this->setLocale($locale);
+        $this->stripLeadingRLM = $stripLeadingRLM;
+    }
+
+    public function setCurrencyCode(string $currencyCode): void
+    {
+        $this->currencyCode = $currencyCode;
+    }
+
+    public function setCurrencySymbolPosition(bool $currencySymbolPosition = self::LEADING_SYMBOL): void
+    {
+        $this->currencySymbolPosition = $currencySymbolPosition;
+    }
+
+    public function setCurrencySymbolSpacing(bool $currencySymbolSpacing = self::SYMBOL_WITHOUT_SPACING): void
+    {
+        $this->currencySymbolSpacing = $currencySymbolSpacing;
+    }
+
+    public function setStripLeadingRLM(bool $stripLeadingRLM): void
+    {
+        $this->stripLeadingRLM = $stripLeadingRLM;
+    }
+
+    protected function getLocaleFormat(): string
+    {
+        $formatter = new Locale($this->fullLocale, NumberFormatter::CURRENCY);
+        $mask = $formatter->format($this->stripLeadingRLM);
+        if ($this->decimals === 0) {
+            $mask = (string) preg_replace('/\.0+/miu', '', $mask);
+        }
+
+        return str_replace('¤', $this->formatCurrencyCode(), $mask);
+    }
+
+    private function formatCurrencyCode(): string
+    {
+        if ($this->locale === null) {
+            return $this->currencyCode;
+        }
+
+        return "[\${$this->currencyCode}-{$this->locale}]";
+    }
+
+    public function format(): string
+    {
+        if ($this->localeFormat !== null) {
+            return $this->localeFormat;
+        }
+
+        return sprintf(
+            '%s%s%s0%s%s%s',
+            $this->currencySymbolPosition === self::LEADING_SYMBOL ? $this->formatCurrencyCode() : null,
+            (
+                $this->currencySymbolPosition === self::LEADING_SYMBOL
+                && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+            ) ? "\u{a0}" : '',
+            $this->thousandsSeparator ? '#,##' : null,
+            $this->decimals > 0 ? '.' . str_repeat('0', $this->decimals) : null,
+            (
+                $this->currencySymbolPosition === self::TRAILING_SYMBOL
+                && $this->currencySymbolSpacing === self::SYMBOL_WITH_SPACING
+            ) ? "\u{a0}" : '',
+            $this->currencySymbolPosition === self::TRAILING_SYMBOL ? $this->formatCurrencyCode() : null
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php
new file mode 100644
index 0000000..61ac117
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Date.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+class Date extends DateTimeWizard
+{
+    /**
+     * Year (4 digits), e.g. 2023.
+     */
+    public const YEAR_FULL = 'yyyy';
+
+    /**
+     * Year (last 2 digits), e.g. 23.
+     */
+    public const YEAR_SHORT = 'yy';
+
+    public const MONTH_FIRST_LETTER = 'mmmmm';
+    /**
+     * Month name, long form, e.g. January.
+     */
+    public const MONTH_NAME_FULL = 'mmmm';
+    /**
+     * Month name, short form, e.g. Jan.
+     */
+    public const MONTH_NAME_SHORT = 'mmm';
+    /**
+     * Month number with a leading zero if required, e.g. 01.
+     */
+    public const MONTH_NUMBER_LONG = 'mm';
+
+    /**
+     * Month number without a leading zero, e.g. 1.
+     */
+    public const MONTH_NUMBER_SHORT = 'm';
+
+    /**
+     * Day of the week, full form, e.g. Tuesday.
+     */
+    public const WEEKDAY_NAME_LONG = 'dddd';
+
+    /**
+     * Day of the week, short form, e.g. Tue.
+     */
+    public const WEEKDAY_NAME_SHORT = 'ddd';
+
+    /**
+     * Day number with a leading zero, e.g. 03.
+     */
+    public const DAY_NUMBER_LONG = 'dd';
+
+    /**
+     * Day number without a leading zero, e.g. 3.
+     */
+    public const DAY_NUMBER_SHORT = 'd';
+
+    protected const DATE_BLOCKS = [
+        self::YEAR_FULL,
+        self::YEAR_SHORT,
+        self::MONTH_FIRST_LETTER,
+        self::MONTH_NAME_FULL,
+        self::MONTH_NAME_SHORT,
+        self::MONTH_NUMBER_LONG,
+        self::MONTH_NUMBER_SHORT,
+        self::WEEKDAY_NAME_LONG,
+        self::WEEKDAY_NAME_SHORT,
+        self::DAY_NUMBER_LONG,
+        self::DAY_NUMBER_SHORT,
+    ];
+
+    public const SEPARATOR_DASH = '-';
+    public const SEPARATOR_DOT = '.';
+    public const SEPARATOR_SLASH = '/';
+    public const SEPARATOR_SPACE_NONBREAKING = "\u{a0}";
+    public const SEPARATOR_SPACE = ' ';
+
+    protected const DATE_DEFAULT = [
+        self::YEAR_FULL,
+        self::MONTH_NUMBER_LONG,
+        self::DAY_NUMBER_LONG,
+    ];
+
+    /**
+     * @var string[]
+     */
+    protected array $separators;
+
+    /**
+     * @var string[]
+     */
+    protected array $formatBlocks;
+
+    /**
+     * @param null|string|string[] $separators
+     *        If you want to use the same separator for all format blocks, then it can be passed as a string literal;
+     *           if you wish to use different separators, then they should be passed as an array.
+     *        If you want to use only a single format block, then pass a null as the separator argument
+     */
+    public function __construct($separators = self::SEPARATOR_DASH, string ...$formatBlocks)
+    {
+        $separators ??= self::SEPARATOR_DASH;
+        $formatBlocks = (count($formatBlocks) === 0) ? self::DATE_DEFAULT : $formatBlocks;
+
+        $this->separators = $this->padSeparatorArray(
+            is_array($separators) ? $separators : [$separators],
+            count($formatBlocks) - 1
+        );
+        $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
+    }
+
+    private function mapFormatBlocks(string $value): string
+    {
+        // Any date masking codes are returned as lower case values
+        if (in_array(mb_strtolower($value), self::DATE_BLOCKS, true)) {
+            return mb_strtolower($value);
+        }
+
+        // Wrap any string literals in quotes, so that they're clearly defined as string literals
+        return $this->wrapLiteral($value);
+    }
+
+    public function format(): string
+    {
+        return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php
new file mode 100644
index 0000000..c0fdeed
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTime.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+class DateTime extends DateTimeWizard
+{
+    /**
+     * @var string[]
+     */
+    protected array $separators;
+
+    /**
+     * @var array<DateTimeWizard|string>
+     */
+    protected array $formatBlocks;
+
+    /**
+     * @param null|string|string[] $separators
+     *          If you want to use only a single format block, then pass a null as the separator argument
+     * @param DateTimeWizard|string ...$formatBlocks
+     */
+    public function __construct($separators, ...$formatBlocks)
+    {
+        $this->separators = $this->padSeparatorArray(
+            is_array($separators) ? $separators : [$separators],
+            count($formatBlocks) - 1
+        );
+        $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
+    }
+
+    private function mapFormatBlocks(DateTimeWizard|string $value): string
+    {
+        // Any date masking codes are returned as lower case values
+        if ($value instanceof DateTimeWizard) {
+            return $value->__toString();
+        }
+
+        // Wrap any string literals in quotes, so that they're clearly defined as string literals
+        return $this->wrapLiteral($value);
+    }
+
+    public function format(): string
+    {
+        return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php
new file mode 100644
index 0000000..8cd7da7
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/DateTimeWizard.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use Stringable;
+
+abstract class DateTimeWizard implements Stringable, Wizard
+{
+    protected const NO_ESCAPING_NEEDED = "$+-/():!^&'~{}<>= ";
+
+    protected function padSeparatorArray(array $separators, int $count): array
+    {
+        $lastSeparator = array_pop($separators);
+
+        return $separators + array_fill(0, $count, $lastSeparator);
+    }
+
+    protected function escapeSingleCharacter(string $value): string
+    {
+        if (str_contains(self::NO_ESCAPING_NEEDED, $value)) {
+            return $value;
+        }
+
+        return "\\{$value}";
+    }
+
+    protected function wrapLiteral(string $value): string
+    {
+        if (mb_strlen($value, 'UTF-8') === 1) {
+            return $this->escapeSingleCharacter($value);
+        }
+
+        // Wrap any other string literals in quotes, so that they're clearly defined as string literals
+        return '"' . str_replace('"', '""', $value) . '"';
+    }
+
+    protected function intersperse(string $formatBlock, ?string $separator): string
+    {
+        return "{$formatBlock}{$separator}";
+    }
+
+    public function __toString(): string
+    {
+        return $this->format();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php
new file mode 100644
index 0000000..b81f77a
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Duration.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+class Duration extends DateTimeWizard
+{
+    public const DAYS_DURATION = 'd';
+
+    /**
+     * Hours as a duration (can exceed 24), e.g. 29.
+     */
+    public const HOURS_DURATION = '[h]';
+
+    /**
+     * Hours without a leading zero, e.g. 9.
+     */
+    public const HOURS_SHORT = 'h';
+
+    /**
+     * Hours with a leading zero, e.g. 09.
+     */
+    public const HOURS_LONG = 'hh';
+
+    /**
+     * Minutes as a duration (can exceed 60), e.g. 109.
+     */
+    public const MINUTES_DURATION = '[m]';
+
+    /**
+     * Minutes without a leading zero, e.g. 5.
+     */
+    public const MINUTES_SHORT = 'm';
+
+    /**
+     * Minutes with a leading zero, e.g. 05.
+     */
+    public const MINUTES_LONG = 'mm';
+
+    /**
+     * Seconds as a duration (can exceed 60), e.g. 129.
+     */
+    public const SECONDS_DURATION = '[s]';
+
+    /**
+     * Seconds without a leading zero, e.g. 2.
+     */
+    public const SECONDS_SHORT = 's';
+
+    /**
+     * Seconds with a leading zero, e.g. 02.
+     */
+    public const SECONDS_LONG = 'ss';
+
+    protected const DURATION_BLOCKS = [
+        self::DAYS_DURATION,
+        self::HOURS_DURATION,
+        self::HOURS_LONG,
+        self::HOURS_SHORT,
+        self::MINUTES_DURATION,
+        self::MINUTES_LONG,
+        self::MINUTES_SHORT,
+        self::SECONDS_DURATION,
+        self::SECONDS_LONG,
+        self::SECONDS_SHORT,
+    ];
+
+    protected const DURATION_MASKS = [
+        self::DAYS_DURATION => self::DAYS_DURATION,
+        self::HOURS_DURATION => self::HOURS_SHORT,
+        self::MINUTES_DURATION => self::MINUTES_LONG,
+        self::SECONDS_DURATION => self::SECONDS_LONG,
+    ];
+
+    protected const DURATION_DEFAULTS = [
+        self::HOURS_LONG => self::HOURS_DURATION,
+        self::HOURS_SHORT => self::HOURS_DURATION,
+        self::MINUTES_LONG => self::MINUTES_DURATION,
+        self::MINUTES_SHORT => self::MINUTES_DURATION,
+        self::SECONDS_LONG => self::SECONDS_DURATION,
+        self::SECONDS_SHORT => self::SECONDS_DURATION,
+    ];
+
+    public const SEPARATOR_COLON = ':';
+    public const SEPARATOR_SPACE_NONBREAKING = "\u{a0}";
+    public const SEPARATOR_SPACE = ' ';
+
+    public const DURATION_DEFAULT = [
+        self::HOURS_DURATION,
+        self::MINUTES_LONG,
+        self::SECONDS_LONG,
+    ];
+
+    /**
+     * @var string[]
+     */
+    protected array $separators;
+
+    /**
+     * @var string[]
+     */
+    protected array $formatBlocks;
+
+    protected bool $durationIsSet = false;
+
+    /**
+     * @param null|string|string[] $separators
+     *        If you want to use the same separator for all format blocks, then it can be passed as a string literal;
+     *           if you wish to use different separators, then they should be passed as an array.
+     *        If you want to use only a single format block, then pass a null as the separator argument
+     */
+    public function __construct($separators = self::SEPARATOR_COLON, string ...$formatBlocks)
+    {
+        $separators ??= self::SEPARATOR_COLON;
+        $formatBlocks = (count($formatBlocks) === 0) ? self::DURATION_DEFAULT : $formatBlocks;
+
+        $this->separators = $this->padSeparatorArray(
+            is_array($separators) ? $separators : [$separators],
+            count($formatBlocks) - 1
+        );
+        $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
+
+        if ($this->durationIsSet === false) {
+            // We need at least one duration mask, so if none has been set we change the first mask element
+            //    to a duration.
+            $this->formatBlocks[0] = self::DURATION_DEFAULTS[mb_strtolower($this->formatBlocks[0])];
+        }
+    }
+
+    private function mapFormatBlocks(string $value): string
+    {
+        // Any duration masking codes are returned as lower case values
+        if (in_array(mb_strtolower($value), self::DURATION_BLOCKS, true)) {
+            if (array_key_exists(mb_strtolower($value), self::DURATION_MASKS)) {
+                if ($this->durationIsSet) {
+                    // We should only have a single duration mask, the first defined in the mask set,
+                    //    so convert any additional duration masks to standard time masks.
+                    $value = self::DURATION_MASKS[mb_strtolower($value)];
+                }
+                $this->durationIsSet = true;
+            }
+
+            return mb_strtolower($value);
+        }
+
+        // Wrap any string literals in quotes, so that they're clearly defined as string literals
+        return $this->wrapLiteral($value);
+    }
+
+    public function format(): string
+    {
+        return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php
new file mode 100644
index 0000000..0c02286
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Locale.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use NumberFormatter;
+use PhpOffice\PhpSpreadsheet\Exception;
+
+final class Locale
+{
+    /**
+     * Language code: ISO-639 2 character, alpha.
+     * Optional script code: ISO-15924 4 alpha.
+     * Optional country code: ISO-3166-1, 2 character alpha.
+     * Separated by underscores or dashes.
+     */
+    public const STRUCTURE = '/^(?P<language>[a-z]{2})([-_](?P<script>[a-z]{4}))?([-_](?P<country>[a-z]{2}))?$/i';
+
+    private NumberFormatter $formatter;
+
+    public function __construct(?string $locale, int $style)
+    {
+        if (class_exists(NumberFormatter::class) === false) {
+            throw new Exception();
+        }
+
+        $formatterLocale = str_replace('-', '_', $locale ?? '');
+        $this->formatter = new NumberFormatter($formatterLocale, $style);
+        if ($this->formatter->getLocale() !== $formatterLocale) {
+            throw new Exception("Unable to read locale data for '{$locale}'");
+        }
+    }
+
+    public function format(bool $stripRlm = true): string
+    {
+        $str = $this->formatter->getPattern();
+
+        return ($stripRlm && str_starts_with($str, "\xe2\x80\x8f")) ? substr($str, 3) : $str;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Number.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Number.php
new file mode 100644
index 0000000..d7e7cd0
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Number.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use PhpOffice\PhpSpreadsheet\Exception;
+
+class Number extends NumberBase implements Wizard
+{
+    public const WITH_THOUSANDS_SEPARATOR = true;
+
+    public const WITHOUT_THOUSANDS_SEPARATOR = false;
+
+    protected bool $thousandsSeparator = true;
+
+    /**
+     * @param int $decimals number of decimal places to display, in the range 0-30
+     * @param bool $thousandsSeparator indicator whether the thousands separator should be used, or not
+     * @param ?string $locale Set the locale for the number format; or leave as the default null.
+     *          Locale has no effect for Number Format values, and is retained here only for compatibility
+     *              with the other Wizards.
+     *          If provided, Locale values must be a valid formatted locale string (e.g. 'en-GB', 'fr', uz-Arab-AF).
+     *
+     * @throws Exception If a provided locale code is not a valid format
+     */
+    public function __construct(
+        int $decimals = 2,
+        bool $thousandsSeparator = self::WITH_THOUSANDS_SEPARATOR,
+        ?string $locale = null
+    ) {
+        $this->setDecimals($decimals);
+        $this->setThousandsSeparator($thousandsSeparator);
+        $this->setLocale($locale);
+    }
+
+    public function setThousandsSeparator(bool $thousandsSeparator = self::WITH_THOUSANDS_SEPARATOR): void
+    {
+        $this->thousandsSeparator = $thousandsSeparator;
+    }
+
+    /**
+     * As MS Excel cannot easily handle Lakh, which is the only locale-specific Number format variant,
+     *       we don't use locale with Numbers.
+     */
+    protected function getLocaleFormat(): string
+    {
+        return $this->format();
+    }
+
+    public function format(): string
+    {
+        return sprintf(
+            '%s0%s',
+            $this->thousandsSeparator ? '#,##' : null,
+            $this->decimals > 0 ? '.' . str_repeat('0', $this->decimals) : null
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/NumberBase.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/NumberBase.php
new file mode 100644
index 0000000..53f6b3d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/NumberBase.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use NumberFormatter;
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use Stringable;
+
+abstract class NumberBase implements Stringable
+{
+    protected const MAX_DECIMALS = 30;
+
+    protected int $decimals = 2;
+
+    protected ?string $locale = null;
+
+    protected ?string $fullLocale = null;
+
+    protected ?string $localeFormat = null;
+
+    public function setDecimals(int $decimals = 2): void
+    {
+        $this->decimals = ($decimals > self::MAX_DECIMALS) ? self::MAX_DECIMALS : max($decimals, 0);
+    }
+
+    /**
+     * Setting a locale will override any settings defined in this class.
+     *
+     * @throws Exception If the locale code is not a valid format
+     */
+    public function setLocale(?string $locale = null): void
+    {
+        if ($locale === null) {
+            $this->localeFormat = $this->locale = $this->fullLocale = null;
+
+            return;
+        }
+
+        $this->locale = $this->validateLocale($locale);
+
+        if (class_exists(NumberFormatter::class)) {
+            $this->localeFormat = $this->getLocaleFormat();
+        }
+    }
+
+    /**
+     * Stub: should be implemented as a concrete method in concrete wizards.
+     */
+    abstract protected function getLocaleFormat(): string;
+
+    /**
+     * @throws Exception If the locale code is not a valid format
+     */
+    private function validateLocale(string $locale): string
+    {
+        if (preg_match(Locale::STRUCTURE, $locale, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
+            throw new Exception("Invalid locale code '{$locale}'");
+        }
+
+        ['language' => $language, 'script' => $script, 'country' => $country] = $matches;
+        // Set case and separator to match standardised locale case
+        $language = strtolower($language ?? '');
+        $script = ($script === null) ? null : ucfirst(strtolower($script));
+        $country = ($country === null) ? null : strtoupper($country);
+
+        $this->fullLocale = implode('-', array_filter([$language, $script, $country]));
+
+        return $country === null ? $language : "{$language}-{$country}";
+    }
+
+    public function format(): string
+    {
+        return NumberFormat::FORMAT_GENERAL;
+    }
+
+    public function __toString(): string
+    {
+        return $this->format();
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Percentage.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Percentage.php
new file mode 100644
index 0000000..003c0dd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Percentage.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use NumberFormatter;
+use PhpOffice\PhpSpreadsheet\Exception;
+
+class Percentage extends NumberBase implements Wizard
+{
+    /**
+     * @param int $decimals number of decimal places to display, in the range 0-30
+     * @param ?string $locale Set the locale for the percentage format; or leave as the default null.
+     *          If provided, Locale values must be a valid formatted locale string (e.g. 'en-GB', 'fr', uz-Arab-AF).
+     *
+     * @throws Exception If a provided locale code is not a valid format
+     */
+    public function __construct(int $decimals = 2, ?string $locale = null)
+    {
+        $this->setDecimals($decimals);
+        $this->setLocale($locale);
+    }
+
+    protected function getLocaleFormat(): string
+    {
+        $formatter = new Locale($this->fullLocale, NumberFormatter::PERCENT);
+
+        return $this->decimals > 0
+            ? str_replace('0', '0.' . str_repeat('0', $this->decimals), $formatter->format())
+            : $formatter->format();
+    }
+
+    public function format(): string
+    {
+        if ($this->localeFormat !== null) {
+            return $this->localeFormat;
+        }
+
+        return sprintf('0%s%%', $this->decimals > 0 ? '.' . str_repeat('0', $this->decimals) : null);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Scientific.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Scientific.php
new file mode 100644
index 0000000..2c25efd
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Scientific.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+use PhpOffice\PhpSpreadsheet\Exception;
+
+class Scientific extends NumberBase implements Wizard
+{
+    /**
+     * @param int $decimals number of decimal places to display, in the range 0-30
+     * @param ?string $locale Set the locale for the scientific format; or leave as the default null.
+     *          Locale has no effect for Scientific Format values, and is retained here for compatibility
+     *              with the other Wizards.
+     *          If provided, Locale values must be a valid formatted locale string (e.g. 'en-GB', 'fr', uz-Arab-AF).
+     *
+     * @throws Exception If a provided locale code is not a valid format
+     */
+    public function __construct(int $decimals = 2, ?string $locale = null)
+    {
+        $this->setDecimals($decimals);
+        $this->setLocale($locale);
+    }
+
+    protected function getLocaleFormat(): string
+    {
+        return $this->format();
+    }
+
+    public function format(): string
+    {
+        return sprintf('0%sE+00', $this->decimals > 0 ? '.' . str_repeat('0', $this->decimals) : null);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Time.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Time.php
new file mode 100644
index 0000000..64b9104
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Time.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+class Time extends DateTimeWizard
+{
+    /**
+     * Hours without a leading zero, e.g. 9.
+     */
+    public const HOURS_SHORT = 'h';
+
+    /**
+     * Hours with a leading zero, e.g. 09.
+     */
+    public const HOURS_LONG = 'hh';
+
+    /**
+     * Minutes without a leading zero, e.g. 5.
+     */
+    public const MINUTES_SHORT = 'm';
+
+    /**
+     * Minutes with a leading zero, e.g. 05.
+     */
+    public const MINUTES_LONG = 'mm';
+
+    /**
+     * Seconds without a leading zero, e.g. 2.
+     */
+    public const SECONDS_SHORT = 's';
+
+    /**
+     * Seconds with a leading zero, e.g. 02.
+     */
+    public const SECONDS_LONG = 'ss';
+
+    public const MORNING_AFTERNOON = 'AM/PM';
+
+    protected const TIME_BLOCKS = [
+        self::HOURS_LONG,
+        self::HOURS_SHORT,
+        self::MINUTES_LONG,
+        self::MINUTES_SHORT,
+        self::SECONDS_LONG,
+        self::SECONDS_SHORT,
+        self::MORNING_AFTERNOON,
+    ];
+
+    public const SEPARATOR_COLON = ':';
+    public const SEPARATOR_SPACE_NONBREAKING = "\u{a0}";
+    public const SEPARATOR_SPACE = ' ';
+
+    protected const TIME_DEFAULT = [
+        self::HOURS_LONG,
+        self::MINUTES_LONG,
+        self::SECONDS_LONG,
+    ];
+
+    /**
+     * @var string[]
+     */
+    protected array $separators;
+
+    /**
+     * @var string[]
+     */
+    protected array $formatBlocks;
+
+    /**
+     * @param null|string|string[] $separators
+     *        If you want to use the same separator for all format blocks, then it can be passed as a string literal;
+     *           if you wish to use different separators, then they should be passed as an array.
+     *        If you want to use only a single format block, then pass a null as the separator argument
+     */
+    public function __construct($separators = self::SEPARATOR_COLON, string ...$formatBlocks)
+    {
+        $separators ??= self::SEPARATOR_COLON;
+        $formatBlocks = (count($formatBlocks) === 0) ? self::TIME_DEFAULT : $formatBlocks;
+
+        $this->separators = $this->padSeparatorArray(
+            is_array($separators) ? $separators : [$separators],
+            count($formatBlocks) - 1
+        );
+        $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
+    }
+
+    private function mapFormatBlocks(string $value): string
+    {
+        // Any date masking codes are returned as lower case values
+        //     except for AM/PM, which is set to uppercase
+        if (in_array(mb_strtolower($value), self::TIME_BLOCKS, true)) {
+            return mb_strtolower($value);
+        } elseif (mb_strtoupper($value) === self::MORNING_AFTERNOON) {
+            return mb_strtoupper($value);
+        }
+
+        // Wrap any string literals in quotes, so that they're clearly defined as string literals
+        return $this->wrapLiteral($value);
+    }
+
+    public function format(): string
+    {
+        return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Wizard.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Wizard.php
new file mode 100644
index 0000000..de4dc4f
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat/Wizard/Wizard.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
+
+interface Wizard
+{
+    public function format(): string;
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
index 1c174e7..55a6526 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Protection.php
@@ -11,17 +11,13 @@ class Protection extends Supervisor
 
     /**
      * Locked.
-     *
-     * @var string
      */
-    protected $locked;
+    protected ?string $locked = null;
 
     /**
      * Hidden.
-     *
-     * @var string
      */
-    protected $hidden;
+    protected ?string $hidden = null;
 
     /**
      * Create a new Protection.
@@ -33,7 +29,7 @@ class Protection extends Supervisor
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($isSupervisor = false, $isConditional = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         // Supervisor?
         parent::__construct($isSupervisor);
@@ -48,12 +44,10 @@ class Protection extends Supervisor
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return Protection
      */
-    public function getSharedComponent()
+    public function getSharedComponent(): self
     {
-        /** @var Style */
+        /** @var Style $parent */
         $parent = $this->parent;
 
         return $parent->getSharedComponent()->getProtection();
@@ -61,12 +55,8 @@ class Protection extends Supervisor
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
         return ['protection' => $array];
     }
@@ -87,7 +77,7 @@ class Protection extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray)
+    public function applyFromArray(array $styleArray): static
     {
         if ($this->isSupervisor) {
             $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
@@ -105,10 +95,8 @@ class Protection extends Supervisor
 
     /**
      * Get locked.
-     *
-     * @return string
      */
-    public function getLocked()
+    public function getLocked(): ?string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getLocked();
@@ -124,7 +112,7 @@ class Protection extends Supervisor
      *
      * @return $this
      */
-    public function setLocked($lockType)
+    public function setLocked(string $lockType): static
     {
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['locked' => $lockType]);
@@ -138,10 +126,8 @@ class Protection extends Supervisor
 
     /**
      * Get hidden.
-     *
-     * @return string
      */
-    public function getHidden()
+    public function getHidden(): ?string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHidden();
@@ -157,7 +143,7 @@ class Protection extends Supervisor
      *
      * @return $this
      */
-    public function setHidden($hiddenType)
+    public function setHidden(string $hiddenType): static
     {
         if ($this->isSupervisor) {
             $styleArray = $this->getStyleArray(['hidden' => $hiddenType]);
@@ -174,16 +160,16 @@ class Protection extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getHashCode();
         }
 
         return md5(
-            $this->locked .
-            $this->hidden .
-            __CLASS__
+            $this->locked
+            . $this->hidden
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/RgbTint.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/RgbTint.php
new file mode 100644
index 0000000..07d0d77
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/RgbTint.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Style;
+
+/**
+ * Class to handle tint applied to color.
+ * Code borrows heavily from some Python projects.
+ *
+ * @see https://docs.python.org/3/library/colorsys.html
+ * @see https://gist.github.com/Mike-Honey/b36e651e9a7f1d2e1d60ce1c63b9b633
+ */
+class RgbTint
+{
+    private const ONE_THIRD = 1.0 / 3.0;
+    private const ONE_SIXTH = 1.0 / 6.0;
+    private const TWO_THIRD = 2.0 / 3.0;
+    private const RGBMAX = 255.0;
+    /**
+     * MS excel's tint function expects that HLS is base 240.
+     *
+     * @see https://social.msdn.microsoft.com/Forums/en-US/e9d8c136-6d62-4098-9b1b-dac786149f43/excel-color-tint-algorithm-incorrect?forum=os_binaryfile#d3c2ac95-52e0-476b-86f1-e2a697f24969
+     */
+    private const HLSMAX = 240.0;
+
+    /**
+     * Convert red/green/blue to hue/luminance/saturation.
+     *
+     * @param float $red 0.0 through 1.0
+     * @param float $green 0.0 through 1.0
+     * @param float $blue 0.0 through 1.0
+     *
+     * @return float[]
+     */
+    private static function rgbToHls(float $red, float $green, float $blue): array
+    {
+        $maxc = max($red, $green, $blue);
+        $minc = min($red, $green, $blue);
+        $luminance = ($minc + $maxc) / 2.0;
+        if ($minc === $maxc) {
+            return [0.0, $luminance, 0.0];
+        }
+        $maxMinusMin = $maxc - $minc;
+        if ($luminance <= 0.5) {
+            $s = $maxMinusMin / ($maxc + $minc);
+        } else {
+            $s = $maxMinusMin / (2.0 - $maxc - $minc);
+        }
+        $rc = ($maxc - $red) / $maxMinusMin;
+        $gc = ($maxc - $green) / $maxMinusMin;
+        $bc = ($maxc - $blue) / $maxMinusMin;
+        if ($red === $maxc) {
+            $h = $bc - $gc;
+        } elseif ($green === $maxc) {
+            $h = 2.0 + $rc - $bc;
+        } else {
+            $h = 4.0 + $gc - $rc;
+        }
+        $h = self::positiveDecimalPart($h / 6.0);
+
+        return [$h, $luminance, $s];
+    }
+
+    /**
+     * Convert hue/luminance/saturation to red/green/blue.
+     *
+     * @param float $hue 0.0 through 1.0
+     * @param float $luminance 0.0 through 1.0
+     * @param float $saturation 0.0 through 1.0
+     *
+     * @return float[]
+     */
+    private static function hlsToRgb(float $hue, float $luminance, float $saturation): array
+    {
+        if ($saturation === 0.0) {
+            return [$luminance, $luminance, $luminance];
+        }
+        if ($luminance <= 0.5) {
+            $m2 = $luminance * (1.0 + $saturation);
+        } else {
+            $m2 = $luminance + $saturation - ($luminance * $saturation);
+        }
+        $m1 = 2.0 * $luminance - $m2;
+
+        return [
+            self::vFunction($m1, $m2, $hue + self::ONE_THIRD),
+            self::vFunction($m1, $m2, $hue),
+            self::vFunction($m1, $m2, $hue - self::ONE_THIRD),
+        ];
+    }
+
+    private static function vFunction(float $m1, float $m2, float $hue): float
+    {
+        $hue = self::positiveDecimalPart($hue);
+        if ($hue < self::ONE_SIXTH) {
+            return $m1 + ($m2 - $m1) * $hue * 6.0;
+        }
+        if ($hue < 0.5) {
+            return $m2;
+        }
+        if ($hue < self::TWO_THIRD) {
+            return $m1 + ($m2 - $m1) * (self::TWO_THIRD - $hue) * 6.0;
+        }
+
+        return $m1;
+    }
+
+    private static function positiveDecimalPart(float $hue): float
+    {
+        $hue = fmod($hue, 1.0);
+
+        return ($hue >= 0.0) ? $hue : (1.0 + $hue);
+    }
+
+    /**
+     * Convert red/green/blue to HLSMAX-based hue/luminance/saturation.
+     *
+     * @return int[]
+     */
+    private static function rgbToMsHls(int $red, int $green, int $blue): array
+    {
+        $red01 = $red / self::RGBMAX;
+        $green01 = $green / self::RGBMAX;
+        $blue01 = $blue / self::RGBMAX;
+        [$hue, $luminance, $saturation] = self::rgbToHls($red01, $green01, $blue01);
+
+        return [
+            (int) round($hue * self::HLSMAX),
+            (int) round($luminance * self::HLSMAX),
+            (int) round($saturation * self::HLSMAX),
+        ];
+    }
+
+    /**
+     * Converts HLSMAX based HLS values to rgb values in the range (0,1).
+     *
+     * @return float[]
+     */
+    private static function msHlsToRgb(int $hue, int $lightness, int $saturation): array
+    {
+        return self::hlsToRgb($hue / self::HLSMAX, $lightness / self::HLSMAX, $saturation / self::HLSMAX);
+    }
+
+    /**
+     * Tints HLSMAX based luminance.
+     *
+     * @see http://ciintelligence.blogspot.co.uk/2012/02/converting-excel-theme-color-and-tint.html
+     */
+    private static function tintLuminance(float $tint, float $luminance): int
+    {
+        if ($tint < 0) {
+            return (int) round($luminance * (1.0 + $tint));
+        }
+
+        return (int) round($luminance * (1.0 - $tint) + (self::HLSMAX - self::HLSMAX * (1.0 - $tint)));
+    }
+
+    /**
+     * Return result of tinting supplied rgb as 6 hex digits.
+     */
+    public static function rgbAndTintToRgb(int $red, int $green, int $blue, float $tint): string
+    {
+        [$hue, $luminance, $saturation] = self::rgbToMsHls($red, $green, $blue);
+        [$red, $green, $blue] = self::msHlsToRgb($hue, self::tintLuminance($tint, $luminance), $saturation);
+
+        return sprintf(
+            '%02X%02X%02X',
+            (int) round($red * self::RGBMAX),
+            (int) round($green * self::RGBMAX),
+            (int) round($blue * self::RGBMAX)
+        );
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
index 506159d..68ca39e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Style.php
@@ -2,66 +2,53 @@
 
 namespace PhpOffice\PhpSpreadsheet\Style;
 
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Exception;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 
 class Style extends Supervisor
 {
     /**
      * Font.
-     *
-     * @var Font
      */
-    protected $font;
+    protected Font $font;
 
     /**
      * Fill.
-     *
-     * @var Fill
      */
-    protected $fill;
+    protected Fill $fill;
 
     /**
      * Borders.
-     *
-     * @var Borders
      */
-    protected $borders;
+    protected Borders $borders;
 
     /**
      * Alignment.
-     *
-     * @var Alignment
      */
-    protected $alignment;
+    protected Alignment $alignment;
 
     /**
      * Number Format.
-     *
-     * @var NumberFormat
      */
-    protected $numberFormat;
+    protected NumberFormat $numberFormat;
 
     /**
      * Protection.
-     *
-     * @var Protection
      */
-    protected $protection;
+    protected Protection $protection;
 
     /**
      * Index of style in collection. Only used for real style.
-     *
-     * @var int
      */
-    protected $index;
+    protected int $index;
 
     /**
      * Use Quote Prefix when displaying in cell editor. Only used for real style.
-     *
-     * @var bool
      */
-    protected $quotePrefix = false;
+    protected bool $quotePrefix = false;
 
     /**
      * Internal cache for styles
@@ -80,7 +67,7 @@ class Style extends Supervisor
      *
      * @var null|array<string, array>
      */
-    private static $cachedStyles;
+    private static ?array $cachedStyles = null;
 
     /**
      * Create a new Style.
@@ -92,14 +79,14 @@ class Style extends Supervisor
      *       Leave this value at default unless you understand exactly what
      *    its ramifications are
      */
-    public function __construct($isSupervisor = false, $isConditional = false)
+    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
     {
         parent::__construct($isSupervisor);
 
         // Initialise values
         $this->font = new Font($isSupervisor, $isConditional);
         $this->fill = new Fill($isSupervisor, $isConditional);
-        $this->borders = new Borders($isSupervisor);
+        $this->borders = new Borders($isSupervisor, $isConditional);
         $this->alignment = new Alignment($isSupervisor, $isConditional);
         $this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
         $this->protection = new Protection($isSupervisor, $isConditional);
@@ -122,7 +109,7 @@ class Style extends Supervisor
     public function getSharedComponent(): self
     {
         $activeSheet = $this->getActiveSheet();
-        $selectedCell = $this->getActiveCell(); // e.g. 'A1'
+        $selectedCell = Functions::trimSheetFromCellReference($this->getActiveCell()); // e.g. 'A1'
 
         if ($activeSheet->cellExists($selectedCell)) {
             $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
@@ -130,7 +117,7 @@ class Style extends Supervisor
             $xfIndex = 0;
         }
 
-        return $activeSheet->getParent()->getCellXfByIndex($xfIndex);
+        return $activeSheet->getParentOrThrow()->getCellXfByIndex($xfIndex);
     }
 
     /**
@@ -138,17 +125,13 @@ class Style extends Supervisor
      */
     public function getParent(): Spreadsheet
     {
-        return $this->getActiveSheet()->getParent();
+        return $this->getActiveSheet()->getParentOrThrow();
     }
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    public function getStyleArray($array)
+    public function getStyleArray(array $array): array
     {
         return ['quotePrefix' => $array];
     }
@@ -198,16 +181,23 @@ class Style extends Supervisor
      *
      * @return $this
      */
-    public function applyFromArray(array $styleArray, $advancedBorders = true)
+    public function applyFromArray(array $styleArray, bool $advancedBorders = true): static
     {
         if ($this->isSupervisor) {
             $pRange = $this->getSelectedCells();
 
-            // Uppercase coordinate
+            // Uppercase coordinate and strip any Worksheet reference from the selected range
             $pRange = strtoupper($pRange);
+            if (str_contains($pRange, '!')) {
+                $pRangeWorksheet = StringHelper::strToUpper(trim(substr($pRange, 0, (int) strrpos($pRange, '!')), "'"));
+                if ($pRangeWorksheet !== '' && StringHelper::strToUpper($this->getActiveSheet()->getTitle()) !== $pRangeWorksheet) {
+                    throw new Exception('Invalid Worksheet for specified Range');
+                }
+                $pRange = strtoupper(Functions::trimSheetFromCellReference($pRange));
+            }
 
             // Is it a cell range or a single cell?
-            if (strpos($pRange, ':') === false) {
+            if (!str_contains($pRange, ':')) {
                 $rangeA = $pRange;
                 $rangeB = $pRange;
             } else {
@@ -269,12 +259,12 @@ class Style extends Supervisor
                 // loop through up to 3 x 3 = 9 regions
                 for ($x = 1; $x <= $xMax; ++$x) {
                     // start column index for region
-                    $colStart = ($x == 3) ?
-                        Coordinate::stringFromColumnIndex($rangeEndIndexes[0])
+                    $colStart = ($x == 3)
+                        ? Coordinate::stringFromColumnIndex($rangeEndIndexes[0])
                         : Coordinate::stringFromColumnIndex($rangeStartIndexes[0] + $x - 1);
                     // end column index for region
-                    $colEnd = ($x == 1) ?
-                        Coordinate::stringFromColumnIndex($rangeStartIndexes[0])
+                    $colEnd = ($x == 1)
+                        ? Coordinate::stringFromColumnIndex($rangeStartIndexes[0])
                         : Coordinate::stringFromColumnIndex($rangeEndIndexes[0] - $xMax + $x);
 
                     for ($y = 1; $y <= $yMax; ++$y) {
@@ -298,12 +288,12 @@ class Style extends Supervisor
                         }
 
                         // start row index for region
-                        $rowStart = ($y == 3) ?
-                            $rangeEndIndexes[1] : $rangeStartIndexes[1] + $y - 1;
+                        $rowStart = ($y == 3)
+                            ? $rangeEndIndexes[1] : $rangeStartIndexes[1] + $y - 1;
 
                         // end row index for region
-                        $rowEnd = ($y == 1) ?
-                            $rangeStartIndexes[1] : $rangeEndIndexes[1] - $yMax + $y;
+                        $rowEnd = ($y == 1)
+                            ? $rangeStartIndexes[1] : $rangeEndIndexes[1] - $yMax + $y;
 
                         // build range for region
                         $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
@@ -372,7 +362,7 @@ class Style extends Supervisor
             $oldXfIndexes = $this->getOldXfIndexes($selectionType, $rangeStartIndexes, $rangeEndIndexes, $columnStart, $columnEnd, $styleArray);
 
             // clone each of the affected styles, apply the style array, and add the new styles to the workbook
-            $workbook = $this->getActiveSheet()->getParent();
+            $workbook = $this->getActiveSheet()->getParentOrThrow();
             $newXfIndexes = [];
             foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
                 $style = $workbook->getCellXfByIndex($oldXfIndex);
@@ -550,20 +540,16 @@ class Style extends Supervisor
 
     /**
      * Get Fill.
-     *
-     * @return Fill
      */
-    public function getFill()
+    public function getFill(): Fill
     {
         return $this->fill;
     }
 
     /**
      * Get Font.
-     *
-     * @return Font
      */
-    public function getFont()
+    public function getFont(): Font
     {
         return $this->font;
     }
@@ -573,7 +559,7 @@ class Style extends Supervisor
      *
      * @return $this
      */
-    public function setFont(Font $font)
+    public function setFont(Font $font): static
     {
         $this->font = $font;
 
@@ -582,30 +568,24 @@ class Style extends Supervisor
 
     /**
      * Get Borders.
-     *
-     * @return Borders
      */
-    public function getBorders()
+    public function getBorders(): Borders
     {
         return $this->borders;
     }
 
     /**
      * Get Alignment.
-     *
-     * @return Alignment
      */
-    public function getAlignment()
+    public function getAlignment(): Alignment
     {
         return $this->alignment;
     }
 
     /**
      * Get Number Format.
-     *
-     * @return NumberFormat
      */
-    public function getNumberFormat()
+    public function getNumberFormat(): NumberFormat
     {
         return $this->numberFormat;
     }
@@ -615,7 +595,7 @@ class Style extends Supervisor
      *
      * @return Conditional[]
      */
-    public function getConditionalStyles()
+    public function getConditionalStyles(): array
     {
         return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
     }
@@ -627,7 +607,7 @@ class Style extends Supervisor
      *
      * @return $this
      */
-    public function setConditionalStyles(array $conditionalStyleArray)
+    public function setConditionalStyles(array $conditionalStyleArray): static
     {
         $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $conditionalStyleArray);
 
@@ -636,20 +616,16 @@ class Style extends Supervisor
 
     /**
      * Get Protection.
-     *
-     * @return Protection
      */
-    public function getProtection()
+    public function getProtection(): Protection
     {
         return $this->protection;
     }
 
     /**
      * Get quote prefix.
-     *
-     * @return bool
      */
-    public function getQuotePrefix()
+    public function getQuotePrefix(): bool
     {
         if ($this->isSupervisor) {
             return $this->getSharedComponent()->getQuotePrefix();
@@ -661,11 +637,9 @@ class Style extends Supervisor
     /**
      * Set quote prefix.
      *
-     * @param bool $quotePrefix
-     *
      * @return $this
      */
-    public function setQuotePrefix($quotePrefix)
+    public function setQuotePrefix(bool $quotePrefix): static
     {
         if ($quotePrefix == '') {
             $quotePrefix = false;
@@ -685,36 +659,32 @@ class Style extends Supervisor
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->fill->getHashCode() .
-            $this->font->getHashCode() .
-            $this->borders->getHashCode() .
-            $this->alignment->getHashCode() .
-            $this->numberFormat->getHashCode() .
-            $this->protection->getHashCode() .
-            ($this->quotePrefix ? 't' : 'f') .
-            __CLASS__
+            $this->fill->getHashCode()
+            . $this->font->getHashCode()
+            . $this->borders->getHashCode()
+            . $this->alignment->getHashCode()
+            . $this->numberFormat->getHashCode()
+            . $this->protection->getHashCode()
+            . ($this->quotePrefix ? 't' : 'f')
+            . __CLASS__
         );
     }
 
     /**
      * Get own index in style collection.
-     *
-     * @return int
      */
-    public function getIndex()
+    public function getIndex(): int
     {
         return $this->index;
     }
 
     /**
      * Set own index in style collection.
-     *
-     * @param int $index
      */
-    public function setIndex($index): void
+    public function setIndex(int $index): void
     {
         $this->index = $index;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
index 8a5c350..8388ef6 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/Supervisor.php
@@ -10,10 +10,8 @@ abstract class Supervisor implements IComparable
 {
     /**
      * Supervisor?
-     *
-     * @var bool
      */
-    protected $isSupervisor;
+    protected bool $isSupervisor;
 
     /**
      * Parent. Only used for supervisor.
@@ -24,10 +22,8 @@ abstract class Supervisor implements IComparable
 
     /**
      * Parent property name.
-     *
-     * @var null|string
      */
-    protected $parentPropertyName;
+    protected ?string $parentPropertyName = null;
 
     /**
      * Create a new Supervisor.
@@ -36,7 +32,7 @@ abstract class Supervisor implements IComparable
      *                                    Leave this value at default unless you understand exactly what
      *                                        its ramifications are
      */
-    public function __construct($isSupervisor = false)
+    public function __construct(bool $isSupervisor = false)
     {
         // Supervisor?
         $this->isSupervisor = $isSupervisor;
@@ -45,12 +41,9 @@ abstract class Supervisor implements IComparable
     /**
      * Bind parent. Only used for supervisor.
      *
-     * @param Spreadsheet|Supervisor $parent
-     * @param null|string $parentPropertyName
-     *
      * @return $this
      */
-    public function bindParent($parent, $parentPropertyName = null)
+    public function bindParent(Spreadsheet|self $parent, ?string $parentPropertyName = null)
     {
         $this->parent = $parent;
         $this->parentPropertyName = $parentPropertyName;
@@ -60,20 +53,16 @@ abstract class Supervisor implements IComparable
 
     /**
      * Is this a supervisor or a cell style component?
-     *
-     * @return bool
      */
-    public function getIsSupervisor()
+    public function getIsSupervisor(): bool
     {
         return $this->isSupervisor;
     }
 
     /**
      * Get the currently active sheet. Only used for supervisor.
-     *
-     * @return Worksheet
      */
-    public function getActiveSheet()
+    public function getActiveSheet(): Worksheet
     {
         return $this->parent->getActiveSheet();
     }
@@ -84,7 +73,7 @@ abstract class Supervisor implements IComparable
      *
      * @return string E.g. 'A1'
      */
-    public function getSelectedCells()
+    public function getSelectedCells(): string
     {
         return $this->getActiveSheet()->getSelectedCells();
     }
@@ -95,7 +84,7 @@ abstract class Supervisor implements IComparable
      *
      * @return string E.g. 'A1'
      */
-    public function getActiveCell()
+    public function getActiveCell(): string
     {
         return $this->getActiveSheet()->getActiveCell();
     }
@@ -144,10 +133,8 @@ abstract class Supervisor implements IComparable
      * The parameter objOrValue is either a primitive type,
      * which is the value added to the array,
      * or a Style object to be recursively added via exportArray.
-     *
-     * @param mixed $objOrValue
      */
-    final protected function exportArray2(array &$exportedArray, string $index, $objOrValue): void
+    final protected function exportArray2(array &$exportedArray, string $index, mixed $objOrValue): void
     {
         if ($objOrValue instanceof self) {
             $exportedArray[$index] = $objOrValue->exportArray();
@@ -159,17 +146,11 @@ abstract class Supervisor implements IComparable
     /**
      * Get the shared style component for the currently active cell in currently active sheet.
      * Only used for style supervisor.
-     *
-     * @return mixed
      */
-    abstract public function getSharedComponent();
+    abstract public function getSharedComponent(): mixed;
 
     /**
      * Build style array from subcomponents.
-     *
-     * @param array $array
-     *
-     * @return array
      */
-    abstract public function getStyleArray($array);
+    abstract public function getStyleArray(array $array): array;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Theme.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Theme.php
new file mode 100644
index 0000000..adeef80
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Theme.php
@@ -0,0 +1,259 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet;
+
+class Theme
+{
+    private string $themeColorName = 'Office';
+
+    private string $themeFontName = 'Office';
+
+    public const COLOR_SCHEME_2013_PLUS_NAME = 'Office 2013+';
+    public const COLOR_SCHEME_2013_PLUS = [
+        'dk1' => '000000',
+        'lt1' => 'FFFFFF',
+        'dk2' => '44546A',
+        'lt2' => 'E7E6E6',
+        'accent1' => '4472C4',
+        'accent2' => 'ED7D31',
+        'accent3' => 'A5A5A5',
+        'accent4' => 'FFC000',
+        'accent5' => '5B9BD5',
+        'accent6' => '70AD47',
+        'hlink' => '0563C1',
+        'folHlink' => '954F72',
+    ];
+
+    public const COLOR_SCHEME_2007_2010_NAME = 'Office 2007-2010';
+    public const COLOR_SCHEME_2007_2010 = [
+        'dk1' => '000000',
+        'lt1' => 'FFFFFF',
+        'dk2' => '1F497D',
+        'lt2' => 'EEECE1',
+        'accent1' => '4F81BD',
+        'accent2' => 'C0504D',
+        'accent3' => '9BBB59',
+        'accent4' => '8064A2',
+        'accent5' => '4BACC6',
+        'accent6' => 'F79646',
+        'hlink' => '0000FF',
+        'folHlink' => '800080',
+    ];
+
+    /** @var string[] */
+    private array $themeColors = self::COLOR_SCHEME_2007_2010;
+
+    private string $majorFontLatin = 'Cambria';
+
+    private string $majorFontEastAsian = '';
+
+    private string $majorFontComplexScript = '';
+
+    private string $minorFontLatin = 'Calibri';
+
+    private string $minorFontEastAsian = '';
+
+    private string $minorFontComplexScript = '';
+
+    /**
+     * Map of Major (header) fonts to write.
+     *
+     * @var string[]
+     */
+    private array $majorFontSubstitutions = self::FONTS_TIMES_SUBSTITUTIONS;
+
+    /**
+     * Map of Minor (body) fonts to write.
+     *
+     * @var string[]
+     */
+    private array $minorFontSubstitutions = self::FONTS_ARIAL_SUBSTITUTIONS;
+
+    public const FONTS_TIMES_SUBSTITUTIONS = [
+        'Jpan' => 'MS Pゴシック',
+        'Hang' => '맑은 고딕',
+        'Hans' => '宋体',
+        'Hant' => '新細明體',
+        'Arab' => 'Times New Roman',
+        'Hebr' => 'Times New Roman',
+        'Thai' => 'Tahoma',
+        'Ethi' => 'Nyala',
+        'Beng' => 'Vrinda',
+        'Gujr' => 'Shruti',
+        'Khmr' => 'MoolBoran',
+        'Knda' => 'Tunga',
+        'Guru' => 'Raavi',
+        'Cans' => 'Euphemia',
+        'Cher' => 'Plantagenet Cherokee',
+        'Yiii' => 'Microsoft Yi Baiti',
+        'Tibt' => 'Microsoft Himalaya',
+        'Thaa' => 'MV Boli',
+        'Deva' => 'Mangal',
+        'Telu' => 'Gautami',
+        'Taml' => 'Latha',
+        'Syrc' => 'Estrangelo Edessa',
+        'Orya' => 'Kalinga',
+        'Mlym' => 'Kartika',
+        'Laoo' => 'DokChampa',
+        'Sinh' => 'Iskoola Pota',
+        'Mong' => 'Mongolian Baiti',
+        'Viet' => 'Times New Roman',
+        'Uigh' => 'Microsoft Uighur',
+        'Geor' => 'Sylfaen',
+    ];
+
+    public const FONTS_ARIAL_SUBSTITUTIONS = [
+        'Jpan' => 'MS Pゴシック',
+        'Hang' => '맑은 고딕',
+        'Hans' => '宋体',
+        'Hant' => '新細明體',
+        'Arab' => 'Arial',
+        'Hebr' => 'Arial',
+        'Thai' => 'Tahoma',
+        'Ethi' => 'Nyala',
+        'Beng' => 'Vrinda',
+        'Gujr' => 'Shruti',
+        'Khmr' => 'DaunPenh',
+        'Knda' => 'Tunga',
+        'Guru' => 'Raavi',
+        'Cans' => 'Euphemia',
+        'Cher' => 'Plantagenet Cherokee',
+        'Yiii' => 'Microsoft Yi Baiti',
+        'Tibt' => 'Microsoft Himalaya',
+        'Thaa' => 'MV Boli',
+        'Deva' => 'Mangal',
+        'Telu' => 'Gautami',
+        'Taml' => 'Latha',
+        'Syrc' => 'Estrangelo Edessa',
+        'Orya' => 'Kalinga',
+        'Mlym' => 'Kartika',
+        'Laoo' => 'DokChampa',
+        'Sinh' => 'Iskoola Pota',
+        'Mong' => 'Mongolian Baiti',
+        'Viet' => 'Arial',
+        'Uigh' => 'Microsoft Uighur',
+        'Geor' => 'Sylfaen',
+    ];
+
+    public function getThemeColors(): array
+    {
+        return $this->themeColors;
+    }
+
+    public function setThemeColor(string $key, string $value): self
+    {
+        $this->themeColors[$key] = $value;
+
+        return $this;
+    }
+
+    public function getThemeColorName(): string
+    {
+        return $this->themeColorName;
+    }
+
+    public function setThemeColorName(string $name, ?array $themeColors = null): self
+    {
+        $this->themeColorName = $name;
+        if ($name === self::COLOR_SCHEME_2007_2010_NAME) {
+            $themeColors = $themeColors ?? self::COLOR_SCHEME_2007_2010;
+        } elseif ($name === self::COLOR_SCHEME_2013_PLUS_NAME) {
+            $themeColors = $themeColors ?? self::COLOR_SCHEME_2013_PLUS;
+        }
+        if ($themeColors !== null) {
+            $this->themeColors = $themeColors;
+        }
+
+        return $this;
+    }
+
+    public function getMajorFontLatin(): string
+    {
+        return $this->majorFontLatin;
+    }
+
+    public function getMajorFontEastAsian(): string
+    {
+        return $this->majorFontEastAsian;
+    }
+
+    public function getMajorFontComplexScript(): string
+    {
+        return $this->majorFontComplexScript;
+    }
+
+    public function getMajorFontSubstitutions(): array
+    {
+        return $this->majorFontSubstitutions;
+    }
+
+    public function setMajorFontValues(?string $latin, ?string $eastAsian, ?string $complexScript, ?array $substitutions): self
+    {
+        if (!empty($latin)) {
+            $this->majorFontLatin = $latin;
+        }
+        if ($eastAsian !== null) {
+            $this->majorFontEastAsian = $eastAsian;
+        }
+        if ($complexScript !== null) {
+            $this->majorFontComplexScript = $complexScript;
+        }
+        if ($substitutions !== null) {
+            $this->majorFontSubstitutions = $substitutions;
+        }
+
+        return $this;
+    }
+
+    public function getMinorFontLatin(): string
+    {
+        return $this->minorFontLatin;
+    }
+
+    public function getMinorFontEastAsian(): string
+    {
+        return $this->minorFontEastAsian;
+    }
+
+    public function getMinorFontComplexScript(): string
+    {
+        return $this->minorFontComplexScript;
+    }
+
+    public function getMinorFontSubstitutions(): array
+    {
+        return $this->minorFontSubstitutions;
+    }
+
+    public function setMinorFontValues(?string $latin, ?string $eastAsian, ?string $complexScript, ?array $substitutions): self
+    {
+        if (!empty($latin)) {
+            $this->minorFontLatin = $latin;
+        }
+        if ($eastAsian !== null) {
+            $this->minorFontEastAsian = $eastAsian;
+        }
+        if ($complexScript !== null) {
+            $this->minorFontComplexScript = $complexScript;
+        }
+        if ($substitutions !== null) {
+            $this->minorFontSubstitutions = $substitutions;
+        }
+
+        return $this;
+    }
+
+    public function getThemeFontName(): string
+    {
+        return $this->themeFontName;
+    }
+
+    public function setThemeFontName(?string $name): self
+    {
+        if (!empty($name)) {
+            $this->themeFontName = $name;
+        }
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
index d604198..2c4a469 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter.php
@@ -8,36 +8,34 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
 use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
+use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
+use PhpOffice\PhpSpreadsheet\Cell\CellRange;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Exception;
 use PhpOffice\PhpSpreadsheet\Shared\Date;
 use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
+use Stringable;
 
-class AutoFilter
+class AutoFilter implements Stringable
 {
     /**
      * Autofilter Worksheet.
-     *
-     * @var null|Worksheet
      */
-    private $workSheet;
+    private ?Worksheet $workSheet;
 
     /**
      * Autofilter Range.
-     *
-     * @var string
      */
-    private $range = '';
+    private string $range;
 
     /**
      * Autofilter Column Ruleset.
      *
      * @var AutoFilter\Column[]
      */
-    private $columns = [];
+    private array $columns = [];
 
-    /** @var bool */
-    private $evaluated = false;
+    private bool $evaluated = false;
 
     public function getEvaluated(): bool
     {
@@ -52,27 +50,30 @@ class AutoFilter
     /**
      * Create a new AutoFilter.
      *
-     * @param AddressRange|array<int>|string $range
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
      *            A simple string containing a Cell range like 'A1:E10' is permitted
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or an AddressRange object.
      */
-    public function __construct($range = '', ?Worksheet $worksheet = null)
+    public function __construct(AddressRange|string|array $range = '', ?Worksheet $worksheet = null)
     {
         if ($range !== '') {
             [, $range] = Worksheet::extractSheetTitle(Validations::validateCellRange($range), true);
         }
 
-        $this->range = $range;
+        $this->range = $range ?? '';
         $this->workSheet = $worksheet;
     }
 
+    public function __destruct()
+    {
+        $this->workSheet = null;
+    }
+
     /**
      * Get AutoFilter Parent Worksheet.
-     *
-     * @return null|Worksheet
      */
-    public function getParent()
+    public function getParent(): null|Worksheet
     {
         return $this->workSheet;
     }
@@ -82,7 +83,7 @@ class AutoFilter
      *
      * @return $this
      */
-    public function setParent(?Worksheet $worksheet = null)
+    public function setParent(?Worksheet $worksheet = null): static
     {
         $this->evaluated = false;
         $this->workSheet = $worksheet;
@@ -92,10 +93,8 @@ class AutoFilter
 
     /**
      * Get AutoFilter Range.
-     *
-     * @return string
      */
-    public function getRange()
+    public function getRange(): string
     {
         return $this->range;
     }
@@ -103,12 +102,12 @@ class AutoFilter
     /**
      * Set AutoFilter Cell Range.
      *
-     * @param AddressRange|array<int>|string $range
+     * @param AddressRange<CellRange>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
      *            A simple string containing a Cell range like 'A1:E10' or a Cell address like 'A1' is permitted
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or an AddressRange object.
      */
-    public function setRange($range = ''): self
+    public function setRange(AddressRange|string|array $range = ''): self
     {
         $this->evaluated = false;
         // extract coordinate
@@ -160,7 +159,7 @@ class AutoFilter
      *
      * @return AutoFilter\Column[]
      */
-    public function getColumns()
+    public function getColumns(): array
     {
         return $this->columns;
     }
@@ -172,7 +171,7 @@ class AutoFilter
      *
      * @return int The column offset within the autofilter range
      */
-    public function testColumnInRange($column)
+    public function testColumnInRange(string $column): int
     {
         if (empty($this->range)) {
             throw new Exception('No autofilter range is defined.');
@@ -194,7 +193,7 @@ class AutoFilter
      *
      * @return int The offset of the specified column within the autofilter range
      */
-    public function getColumnOffset($column)
+    public function getColumnOffset(string $column): int
     {
         return $this->testColumnInRange($column);
     }
@@ -203,10 +202,8 @@ class AutoFilter
      * Get a specified AutoFilter Column.
      *
      * @param string $column Column name (e.g. A)
-     *
-     * @return AutoFilter\Column
      */
-    public function getColumn($column)
+    public function getColumn(string $column): AutoFilter\Column
     {
         $this->testColumnInRange($column);
 
@@ -221,10 +218,8 @@ class AutoFilter
      * Get a specified AutoFilter Column by it's offset.
      *
      * @param int $columnOffset Column offset within range (starting from 0)
-     *
-     * @return AutoFilter\Column
      */
-    public function getColumnByOffset($columnOffset)
+    public function getColumnByOffset(int $columnOffset): AutoFilter\Column
     {
         [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
         $pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $columnOffset);
@@ -240,12 +235,12 @@ class AutoFilter
      *
      * @return $this
      */
-    public function setColumn($columnObjectOrString)
+    public function setColumn(AutoFilter\Column|string $columnObjectOrString): static
     {
         $this->evaluated = false;
         if ((is_string($columnObjectOrString)) && (!empty($columnObjectOrString))) {
             $column = $columnObjectOrString;
-        } elseif (is_object($columnObjectOrString) && ($columnObjectOrString instanceof AutoFilter\Column)) {
+        } elseif ($columnObjectOrString instanceof AutoFilter\Column) {
             $column = $columnObjectOrString->getColumnIndex();
         } else {
             throw new Exception('Column is not within the autofilter range.');
@@ -270,7 +265,7 @@ class AutoFilter
      *
      * @return $this
      */
-    public function clearColumn($column)
+    public function clearColumn(string $column): static
     {
         $this->evaluated = false;
         $this->testColumnInRange($column);
@@ -294,7 +289,7 @@ class AutoFilter
      *
      * @return $this
      */
-    public function shiftColumn($fromColumn, $toColumn)
+    public function shiftColumn(string $fromColumn, string $toColumn): static
     {
         $this->evaluated = false;
         $fromColumn = strtoupper($fromColumn);
@@ -316,12 +311,9 @@ class AutoFilter
     /**
      * Test if cell value is in the defined set of values.
      *
-     * @param mixed $cellValue
-     * @param mixed[] $dataSet
-     *
-     * @return bool
+     * @param array{blanks: bool, filterValues: array<string,array<string,string>>} $dataSet
      */
-    private static function filterTestInSimpleDataSet($cellValue, $dataSet)
+    protected static function filterTestInSimpleDataSet(mixed $cellValue, array $dataSet): bool
     {
         $dataSetValues = $dataSet['filterValues'];
         $blanks = $dataSet['blanks'];
@@ -335,12 +327,9 @@ class AutoFilter
     /**
      * Test if cell value is in the defined set of Excel date values.
      *
-     * @param mixed $cellValue
-     * @param mixed[] $dataSet
-     *
-     * @return bool
+     * @param array{blanks: bool, filterValues: array<string,array<string,string>>} $dataSet
      */
-    private static function filterTestInDateGroupSet($cellValue, $dataSet)
+    protected static function filterTestInDateGroupSet(mixed $cellValue, array $dataSet): bool
     {
         $dateSet = $dataSet['filterValues'];
         $blanks = $dataSet['blanks'];
@@ -367,7 +356,7 @@ class AutoFilter
             }
             foreach ($dateSet as $dateValue) {
                 //    Use of substr to extract value at the appropriate group level
-                if (substr($dtVal, 0, strlen($dateValue)) == $dateValue) {
+                if (str_starts_with($dtVal, $dateValue)) {
                     return true;
                 }
             }
@@ -379,14 +368,11 @@ class AutoFilter
     /**
      * Test if cell value is within a set of values defined by a ruleset.
      *
-     * @param mixed $cellValue
      * @param mixed[] $ruleSet
-     *
-     * @return bool
      */
-    private static function filterTestInCustomDataSet($cellValue, $ruleSet)
+    protected static function filterTestInCustomDataSet(mixed $cellValue, array $ruleSet): bool
     {
-        /** @var array[] */
+        /** @var array[] $dataSet */
         $dataSet = $ruleSet['filterRules'];
         $join = $ruleSet['join'];
         $customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
@@ -399,11 +385,11 @@ class AutoFilter
         }
         $returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND);
         foreach ($dataSet as $rule) {
-            /** @var string */
+            /** @var string $ruleValue */
             $ruleValue = $rule['value'];
-            /** @var string */
+            /** @var string $ruleOperator */
             $ruleOperator = $rule['operator'];
-            /** @var string */
+            /** @var string $cellValueString */
             $cellValueString = $cellValue ?? '';
             $retVal = false;
 
@@ -437,20 +423,11 @@ class AutoFilter
                         break;
                 }
             } elseif ($ruleValue == '') {
-                switch ($ruleOperator) {
-                    case Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
-                        $retVal = (($cellValue == '') || ($cellValue === null));
-
-                        break;
-                    case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
-                        $retVal = (($cellValue != '') && ($cellValue !== null));
-
-                        break;
-                    default:
-                        $retVal = true;
-
-                        break;
-                }
+                $retVal = match ($ruleOperator) {
+                    Rule::AUTOFILTER_COLUMN_RULE_EQUAL => ($cellValue == '') || ($cellValue === null),
+                    Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL => ($cellValue != '') && ($cellValue !== null),
+                    default => true,
+                };
             } else {
                 //    String values are always tested for equality, factoring in for wildcards (hence a regexp test)
                 switch ($ruleOperator) {
@@ -504,12 +481,9 @@ class AutoFilter
     /**
      * Test if cell date value is matches a set of values defined by a set of months.
      *
-     * @param mixed $cellValue
      * @param mixed[] $monthSet
-     *
-     * @return bool
      */
-    private static function filterTestInPeriodDateSet($cellValue, $monthSet)
+    protected static function filterTestInPeriodDateSet(mixed $cellValue, array $monthSet): bool
     {
         //    Blank cells are always ignored, so return a FALSE
         if (($cellValue == '') || ($cellValue === null)) {
@@ -748,11 +722,9 @@ class AutoFilter
     /**
      * Convert a dynamic rule daterange to a custom filter range expression for ease of calculation.
      *
-     * @param string $dynamicRuleType
-     *
      * @return mixed[]
      */
-    private function dynamicFilterDateRange($dynamicRuleType, AutoFilter\Column &$filterColumn)
+    private function dynamicFilterDateRange(string $dynamicRuleType, AutoFilter\Column &$filterColumn): array
     {
         $ruleValues = [];
         $callBack = [__CLASS__, self::DATE_FUNCTIONS[$dynamicRuleType]]; // What if not found?
@@ -778,16 +750,8 @@ class AutoFilter
 
     /**
      * Apply the AutoFilter rules to the AutoFilter Range.
-     *
-     * @param string $columnID
-     * @param int $startRow
-     * @param int $endRow
-     * @param ?string $ruleType
-     * @param mixed $ruleValue
-     *
-     * @return mixed
      */
-    private function calculateTopTenValue($columnID, $startRow, $endRow, $ruleType, $ruleValue)
+    private function calculateTopTenValue(string $columnID, int $startRow, int $endRow, ?string $ruleType, mixed $ruleValue): mixed
     {
         $range = $columnID . $startRow . ':' . $columnID . $endRow;
         $retVal = null;
@@ -801,9 +765,13 @@ class AutoFilter
                 sort($dataValues);
             }
 
-            $slice = array_slice($dataValues, 0, $ruleValue);
-
-            $retVal = array_pop($slice);
+            if (is_numeric($ruleValue)) {
+                $ruleValue = (int) $ruleValue;
+            }
+            if ($ruleValue === null || is_int($ruleValue)) {
+                $slice = array_slice($dataValues, 0, $ruleValue);
+                $retVal = array_pop($slice);
+            }
         }
 
         return $retVal;
@@ -814,7 +782,7 @@ class AutoFilter
      *
      * @return $this
      */
-    public function showHideRows()
+    public function showHideRows(): static
     {
         if ($this->workSheet === null) {
             return $this;
@@ -848,7 +816,7 @@ class AutoFilter
                             'method' => 'filterTestInSimpleDataSet',
                             'arguments' => ['filterValues' => $ruleDataSet, 'blanks' => $blanks],
                         ];
-                    } else {
+                    } elseif ($ruleType !== null) {
                         //    Filter on date group values
                         $arguments = [
                             'date' => [],
@@ -861,38 +829,38 @@ class AutoFilter
                             }
                             $date = $time = '';
                             if (
-                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
-                                ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')
+                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]))
+                                && ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')
                             ) {
                                 $date .= sprintf('%04d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
                             }
                             if (
-                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
-                                ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')
+                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]))
+                                && ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')
                             ) {
                                 $date .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
                             }
                             if (
-                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
-                                ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')
+                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]))
+                                && ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')
                             ) {
                                 $date .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
                             }
                             if (
-                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
-                                ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')
+                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]))
+                                && ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')
                             ) {
                                 $time .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
                             }
                             if (
-                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
-                                ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')
+                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]))
+                                && ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')
                             ) {
                                 $time .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
                             }
                             if (
-                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
-                                ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')
+                                (isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]))
+                                && ($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')
                             ) {
                                 $time .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
                             }
@@ -941,14 +909,13 @@ class AutoFilter
                         //    We should only ever have one Dynamic Filter Rule anyway
                         $dynamicRuleType = $rule->getGrouping();
                         if (
-                            ($dynamicRuleType == Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
-                            ($dynamicRuleType == Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)
+                            ($dynamicRuleType == Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
+                            || ($dynamicRuleType == Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)
                         ) {
                             //    Number (Average) based
                             //    Calculate the average
                             $averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')';
-                            $spreadsheet = ($this->workSheet === null) ? null : $this->workSheet->getParent();
-                            $average = Calculation::getInstance($spreadsheet)->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
+                            $average = Calculation::getInstance($this->workSheet->getParent())->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
                             while (is_array($average)) {
                                 $average = array_pop($average);
                             }
@@ -1007,7 +974,7 @@ class AutoFilter
                         $ruleOperator = $rule->getOperator();
                     }
                     if (is_numeric($ruleValue) && $ruleOperator === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
-                        $ruleValue = floor((float) $ruleValue * ($dataRowCount / 100));
+                        $ruleValue = (int) floor((float) $ruleValue * ($dataRowCount / 100));
                     }
                     if (!is_array($ruleValue) && $ruleValue < 1) {
                         $ruleValue = 1;
@@ -1016,6 +983,7 @@ class AutoFilter
                         $ruleValue = 500;
                     }
 
+                    /** @var float|int|string */
                     $maxVal = $this->calculateTopTenValue($columnID, $rangeStart[1] + 1, (int) $rangeEnd[1], $toptenRuleType, $ruleValue);
 
                     $operator = ($toptenRuleType == Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
@@ -1040,16 +1008,22 @@ class AutoFilter
             foreach ($columnFilterTests as $columnID => $columnFilterTest) {
                 $cellValue = $this->workSheet->getCell($columnID . $row)->getCalculatedValue();
                 //    Execute the filter test
-                $result = // $result && // phpstan says $result is always true here
-                    // @phpstan-ignore-next-line
-                    call_user_func_array([self::class, $columnFilterTest['method']], [$cellValue, $columnFilterTest['arguments']]);
+                /** @var callable */
+                $temp = [self::class, $columnFilterTest['method']];
+                /** @var bool */
+                $result // $result && // phpstan says $result is always true here
+                    = call_user_func_array($temp, [$cellValue, $columnFilterTest['arguments']]);
                 //    If filter test has resulted in FALSE, exit the loop straightaway rather than running any more tests
                 if (!$result) {
                     break;
                 }
             }
             //    Set show/hide for the row based on the result of the autoFilter result
-            $this->workSheet->getRowDimension((int) $row)->setVisible($result);
+            //    If the RowDimension object has not been allocated yet and the row should be visible,
+            //    then we can avoid any operation since the rows are visible by default (saves a lot of memory)
+            if ($result === false || $this->workSheet->rowDimensionExists((int) $row)) {
+                $this->workSheet->getRowDimension((int) $row)->setVisible($result);
+            }
         }
         $this->evaluated = true;
 
@@ -1065,7 +1039,7 @@ class AutoFilter
         if ($startRow === $endRow && $this->workSheet !== null) {
             try {
                 $rowIterator = $this->workSheet->getRowIterator($startRow + 1);
-            } catch (Exception $e) {
+            } catch (Exception) {
                 // If there are no rows below $startRow
                 return $startRow;
             }
@@ -1111,7 +1085,7 @@ class AutoFilter
      * toString method replicates previous behavior by returning the range if object is
      * referenced as a property of its parent.
      */
-    public function __toString()
+    public function __toString(): string
     {
         return (string) $this->range;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
index 076292f..514cf10 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php
@@ -21,7 +21,7 @@ class Column
      *
      * @var string[]
      */
-    private static $filterTypes = [
+    private static array $filterTypes = [
         //    Currently we're not handling
         //        colorFilter
         //        extLst
@@ -41,60 +41,52 @@ class Column
      *
      * @var string[]
      */
-    private static $ruleJoins = [
+    private static array $ruleJoins = [
         self::AUTOFILTER_COLUMN_JOIN_AND,
         self::AUTOFILTER_COLUMN_JOIN_OR,
     ];
 
     /**
      * Autofilter.
-     *
-     * @var null|AutoFilter
      */
-    private $parent;
+    private ?AutoFilter $parent;
 
     /**
      * Autofilter Column Index.
-     *
-     * @var string
      */
-    private $columnIndex = '';
+    private string $columnIndex;
 
     /**
      * Autofilter Column Filter Type.
-     *
-     * @var string
      */
-    private $filterType = self::AUTOFILTER_FILTERTYPE_FILTER;
+    private string $filterType = self::AUTOFILTER_FILTERTYPE_FILTER;
 
     /**
      * Autofilter Multiple Rules And/Or.
-     *
-     * @var string
      */
-    private $join = self::AUTOFILTER_COLUMN_JOIN_OR;
+    private string $join = self::AUTOFILTER_COLUMN_JOIN_OR;
 
     /**
      * Autofilter Column Rules.
      *
      * @var Column\Rule[]
      */
-    private $ruleset = [];
+    private array $ruleset = [];
 
     /**
      * Autofilter Column Dynamic Attributes.
      *
-     * @var mixed[]
+     * @var (float|int|string)[]
      */
-    private $attributes = [];
+    private array $attributes = [];
 
     /**
      * Create a new Column.
      *
      * @param string $column Column (e.g. A)
-     * @param AutoFilter $parent Autofilter for this column
+     * @param ?AutoFilter $parent Autofilter for this column
      */
-    public function __construct($column, ?AutoFilter $parent = null)
+    public function __construct(string $column, ?AutoFilter $parent = null)
     {
         $this->columnIndex = $column;
         $this->parent = $parent;
@@ -109,10 +101,8 @@ class Column
 
     /**
      * Get AutoFilter column index as string eg: 'A'.
-     *
-     * @return string
      */
-    public function getColumnIndex()
+    public function getColumnIndex(): string
     {
         return $this->columnIndex;
     }
@@ -124,7 +114,7 @@ class Column
      *
      * @return $this
      */
-    public function setColumnIndex($column)
+    public function setColumnIndex(string $column): static
     {
         $this->setEvaluatedFalse();
         // Uppercase coordinate
@@ -140,10 +130,8 @@ class Column
 
     /**
      * Get this Column's AutoFilter Parent.
-     *
-     * @return null|AutoFilter
      */
-    public function getParent()
+    public function getParent(): ?AutoFilter
     {
         return $this->parent;
     }
@@ -153,7 +141,7 @@ class Column
      *
      * @return $this
      */
-    public function setParent(?AutoFilter $parent = null)
+    public function setParent(?AutoFilter $parent = null): static
     {
         $this->setEvaluatedFalse();
         $this->parent = $parent;
@@ -163,10 +151,8 @@ class Column
 
     /**
      * Get AutoFilter Type.
-     *
-     * @return string
      */
-    public function getFilterType()
+    public function getFilterType(): string
     {
         return $this->filterType;
     }
@@ -174,11 +160,9 @@ class Column
     /**
      * Set AutoFilter Type.
      *
-     * @param string $filterType
-     *
      * @return $this
      */
-    public function setFilterType($filterType)
+    public function setFilterType(string $filterType): static
     {
         $this->setEvaluatedFalse();
         if (!in_array($filterType, self::$filterTypes)) {
@@ -195,10 +179,8 @@ class Column
 
     /**
      * Get AutoFilter Multiple Rules And/Or Join.
-     *
-     * @return string
      */
-    public function getJoin()
+    public function getJoin(): string
     {
         return $this->join;
     }
@@ -210,7 +192,7 @@ class Column
      *
      * @return $this
      */
-    public function setJoin($join)
+    public function setJoin(string $join): static
     {
         $this->setEvaluatedFalse();
         // Lowercase And/Or
@@ -227,11 +209,11 @@ class Column
     /**
      * Set AutoFilter Attributes.
      *
-     * @param mixed[] $attributes
+     * @param (float|int|string)[] $attributes
      *
      * @return $this
      */
-    public function setAttributes($attributes)
+    public function setAttributes(array $attributes): static
     {
         $this->setEvaluatedFalse();
         $this->attributes = $attributes;
@@ -243,11 +225,11 @@ class Column
      * Set An AutoFilter Attribute.
      *
      * @param string $name Attribute Name
-     * @param int|string $value Attribute Value
+     * @param float|int|string $value Attribute Value
      *
      * @return $this
      */
-    public function setAttribute($name, $value)
+    public function setAttribute(string $name, $value): static
     {
         $this->setEvaluatedFalse();
         $this->attributes[$name] = $value;
@@ -258,9 +240,9 @@ class Column
     /**
      * Get AutoFilter Column Attributes.
      *
-     * @return int[]|string[]
+     * @return (float|int|string)[]
      */
-    public function getAttributes()
+    public function getAttributes(): array
     {
         return $this->attributes;
     }
@@ -269,10 +251,8 @@ class Column
      * Get specific AutoFilter Column Attribute.
      *
      * @param string $name Attribute Name
-     *
-     * @return null|int|string
      */
-    public function getAttribute($name)
+    public function getAttribute(string $name): null|float|int|string
     {
         if (isset($this->attributes[$name])) {
             return $this->attributes[$name];
@@ -291,7 +271,7 @@ class Column
      *
      * @return Column\Rule[]
      */
-    public function getRules()
+    public function getRules(): array
     {
         return $this->ruleset;
     }
@@ -300,10 +280,8 @@ class Column
      * Get a specified AutoFilter Column Rule.
      *
      * @param int $index Rule index in the ruleset array
-     *
-     * @return Column\Rule
      */
-    public function getRule($index)
+    public function getRule(int $index): Column\Rule
     {
         if (!isset($this->ruleset[$index])) {
             $this->ruleset[$index] = new Column\Rule($this);
@@ -314,10 +292,8 @@ class Column
 
     /**
      * Create a new AutoFilter Column Rule in the ruleset.
-     *
-     * @return Column\Rule
      */
-    public function createRule()
+    public function createRule(): Column\Rule
     {
         $this->setEvaluatedFalse();
         if ($this->filterType === self::AUTOFILTER_FILTERTYPE_CUSTOMFILTER && count($this->ruleset) >= 2) {
@@ -333,7 +309,7 @@ class Column
      *
      * @return $this
      */
-    public function addRule(Column\Rule $rule)
+    public function addRule(Column\Rule $rule): static
     {
         $this->setEvaluatedFalse();
         $rule->setParent($this);
@@ -350,7 +326,7 @@ class Column
      *
      * @return $this
      */
-    public function deleteRule($index)
+    public function deleteRule(int $index): static
     {
         $this->setEvaluatedFalse();
         if (isset($this->ruleset[$index])) {
@@ -369,7 +345,7 @@ class Column
      *
      * @return $this
      */
-    public function clearRules()
+    public function clearRules(): static
     {
         $this->setEvaluatedFalse();
         $this->ruleset = [];
@@ -384,6 +360,7 @@ class Column
     public function __clone()
     {
         $vars = get_object_vars($this);
+        /** @var AutoFilter\Column\Rule[] $value */
         foreach ($vars as $key => $value) {
             if ($key === 'parent') {
                 // Detach from autofilter parent
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
index 408dfb3..93768a1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
@@ -169,20 +169,15 @@ class Rule
     // Rule Operators (Date Special) which are translated to standard numeric operators with calculated values
     //    const AUTOFILTER_COLUMN_RULE_BEFORE                = 'lessThan';
     //    const AUTOFILTER_COLUMN_RULE_AFTER                = 'greaterThan';
-
     /**
      * Autofilter Column.
-     *
-     * @var ?Column
      */
-    private $parent;
+    private ?Column $parent;
 
     /**
      * Autofilter Rule Type.
-     *
-     * @var string
      */
-    private $ruleType = self::AUTOFILTER_RULETYPE_FILTER;
+    private string $ruleType = self::AUTOFILTER_RULETYPE_FILTER;
 
     /**
      * Autofilter Rule Value.
@@ -193,17 +188,13 @@ class Rule
 
     /**
      * Autofilter Rule Operator.
-     *
-     * @var string
      */
-    private $operator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
+    private string $operator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
 
     /**
      * DateTimeGrouping Group Value.
-     *
-     * @var string
      */
-    private $grouping = '';
+    private string $grouping = '';
 
     /**
      * Create a new Rule.
@@ -222,10 +213,8 @@ class Rule
 
     /**
      * Get AutoFilter Rule Type.
-     *
-     * @return string
      */
-    public function getRuleType()
+    public function getRuleType(): string
     {
         return $this->ruleType;
     }
@@ -237,7 +226,7 @@ class Rule
      *
      * @return $this
      */
-    public function setRuleType($ruleType)
+    public function setRuleType(string $ruleType): static
     {
         $this->setEvaluatedFalse();
         if (!in_array($ruleType, self::RULE_TYPES)) {
@@ -266,7 +255,7 @@ class Rule
      *
      * @return $this
      */
-    public function setValue($value)
+    public function setValue($value): static
     {
         $this->setEvaluatedFalse();
         if (is_array($value)) {
@@ -294,10 +283,8 @@ class Rule
 
     /**
      * Get AutoFilter Rule Operator.
-     *
-     * @return string
      */
-    public function getOperator()
+    public function getOperator(): string
     {
         return $this->operator;
     }
@@ -309,15 +296,15 @@ class Rule
      *
      * @return $this
      */
-    public function setOperator($operator)
+    public function setOperator(string $operator): static
     {
         $this->setEvaluatedFalse();
         if (empty($operator)) {
             $operator = self::AUTOFILTER_COLUMN_RULE_EQUAL;
         }
         if (
-            (!in_array($operator, self::OPERATORS)) &&
-            (!in_array($operator, self::TOP_TEN_VALUE))
+            (!in_array($operator, self::OPERATORS))
+            && (!in_array($operator, self::TOP_TEN_VALUE))
         ) {
             throw new PhpSpreadsheetException('Invalid operator for column AutoFilter Rule.');
         }
@@ -328,10 +315,8 @@ class Rule
 
     /**
      * Get AutoFilter Rule Grouping.
-     *
-     * @return string
      */
-    public function getGrouping()
+    public function getGrouping(): string
     {
         return $this->grouping;
     }
@@ -339,18 +324,16 @@ class Rule
     /**
      * Set AutoFilter Rule Grouping.
      *
-     * @param string $grouping
-     *
      * @return $this
      */
-    public function setGrouping($grouping)
+    public function setGrouping(string $grouping): static
     {
         $this->setEvaluatedFalse();
         if (
-            ($grouping !== null) &&
-            (!in_array($grouping, self::DATE_TIME_GROUPS)) &&
-            (!in_array($grouping, self::DYNAMIC_TYPES)) &&
-            (!in_array($grouping, self::TOP_TEN_TYPE))
+            ($grouping !== null)
+            && (!in_array($grouping, self::DATE_TIME_GROUPS))
+            && (!in_array($grouping, self::DYNAMIC_TYPES))
+            && (!in_array($grouping, self::TOP_TEN_TYPE))
         ) {
             throw new PhpSpreadsheetException('Invalid grouping for column AutoFilter Rule.');
         }
@@ -364,11 +347,10 @@ class Rule
      *
      * @param string $operator see self::AUTOFILTER_COLUMN_RULE_*
      * @param int|int[]|string|string[] $value
-     * @param string $grouping
      *
      * @return $this
      */
-    public function setRule($operator, $value, $grouping = null)
+    public function setRule(string $operator, $value, ?string $grouping = null): static
     {
         $this->setEvaluatedFalse();
         $this->setOperator($operator);
@@ -385,10 +367,8 @@ class Rule
 
     /**
      * Get this Rule's AutoFilter Column Parent.
-     *
-     * @return ?Column
      */
-    public function getParent()
+    public function getParent(): ?Column
     {
         return $this->parent;
     }
@@ -398,7 +378,7 @@ class Rule
      *
      * @return $this
      */
-    public function setParent(?Column $parent = null)
+    public function setParent(?Column $parent = null): static
     {
         $this->setEvaluatedFalse();
         $this->parent = $parent;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFit.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFit.php
new file mode 100644
index 0000000..46a0e9c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/AutoFit.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
+use PhpOffice\PhpSpreadsheet\Cell\CellRange;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class AutoFit
+{
+    protected Worksheet $worksheet;
+
+    public function __construct(Worksheet $worksheet)
+    {
+        $this->worksheet = $worksheet;
+    }
+
+    public function getAutoFilterIndentRanges(): array
+    {
+        $autoFilterIndentRanges = [];
+        $autoFilterIndentRanges[] = $this->getAutoFilterIndentRange($this->worksheet->getAutoFilter());
+
+        foreach ($this->worksheet->getTableCollection() as $table) {
+            /** @var Table $table */
+            if ($table->getShowHeaderRow() === true && $table->getAllowFilter() === true) {
+                $autoFilter = $table->getAutoFilter();
+                if ($autoFilter !== null) {
+                    $autoFilterIndentRanges[] = $this->getAutoFilterIndentRange($autoFilter);
+                }
+            }
+        }
+
+        return array_filter($autoFilterIndentRanges);
+    }
+
+    private function getAutoFilterIndentRange(AutoFilter $autoFilter): ?string
+    {
+        $autoFilterRange = $autoFilter->getRange();
+        $autoFilterIndentRange = null;
+
+        if (!empty($autoFilterRange)) {
+            $autoFilterRangeBoundaries = Coordinate::rangeBoundaries($autoFilterRange);
+            $autoFilterIndentRange = (string) new CellRange(
+                CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[0][0], $autoFilterRangeBoundaries[0][1]),
+                CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[1][0], $autoFilterRangeBoundaries[0][1])
+            );
+        }
+
+        return $autoFilterIndentRange;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
index 5001346..4c35438 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
@@ -5,6 +5,8 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet;
 use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 use PhpOffice\PhpSpreadsheet\IComparable;
+use PhpOffice\PhpSpreadsheet\Worksheet\Drawing\Shadow;
+use SimpleXMLElement;
 
 class BaseDrawing implements IComparable
 {
@@ -19,150 +21,115 @@ class BaseDrawing implements IComparable
 
     /**
      * The editAs attribute, used only with two cell anchor.
-     *
-     * @var string
      */
-    protected $editAs = '';
+    protected string $editAs = '';
 
     /**
      * Image counter.
-     *
-     * @var int
      */
-    private static $imageCounter = 0;
+    private static int $imageCounter = 0;
 
     /**
      * Image index.
-     *
-     * @var int
      */
-    private $imageIndex = 0;
+    private int $imageIndex;
 
     /**
      * Name.
-     *
-     * @var string
      */
-    protected $name = '';
+    protected string $name = '';
 
     /**
      * Description.
-     *
-     * @var string
      */
-    protected $description = '';
+    protected string $description = '';
 
     /**
      * Worksheet.
-     *
-     * @var null|Worksheet
      */
-    protected $worksheet;
+    protected ?Worksheet $worksheet = null;
 
     /**
      * Coordinates.
-     *
-     * @var string
      */
-    protected $coordinates = 'A1';
+    protected string $coordinates = 'A1';
 
     /**
      * Offset X.
-     *
-     * @var int
      */
-    protected $offsetX = 0;
+    protected int $offsetX = 0;
 
     /**
      * Offset Y.
-     *
-     * @var int
      */
-    protected $offsetY = 0;
+    protected int $offsetY = 0;
 
     /**
      * Coordinates2.
-     *
-     * @var string
      */
-    protected $coordinates2 = '';
+    protected string $coordinates2 = '';
 
     /**
      * Offset X2.
-     *
-     * @var int
      */
-    protected $offsetX2 = 0;
+    protected int $offsetX2 = 0;
 
     /**
      * Offset Y2.
-     *
-     * @var int
      */
-    protected $offsetY2 = 0;
+    protected int $offsetY2 = 0;
 
     /**
      * Width.
-     *
-     * @var int
      */
-    protected $width = 0;
+    protected int $width = 0;
 
     /**
      * Height.
-     *
-     * @var int
      */
-    protected $height = 0;
+    protected int $height = 0;
 
     /**
      * Pixel width of image. See $width for the size the Drawing will be in the sheet.
-     *
-     * @var int
      */
-    protected $imageWidth = 0;
+    protected int $imageWidth = 0;
 
     /**
      * Pixel width of image. See $height for the size the Drawing will be in the sheet.
-     *
-     * @var int
      */
-    protected $imageHeight = 0;
+    protected int $imageHeight = 0;
 
     /**
      * Proportional resize.
-     *
-     * @var bool
      */
-    protected $resizeProportional = true;
+    protected bool $resizeProportional = true;
 
     /**
      * Rotation.
-     *
-     * @var int
      */
-    protected $rotation = 0;
+    protected int $rotation = 0;
+
+    protected bool $flipVertical = false;
+
+    protected bool $flipHorizontal = false;
 
     /**
      * Shadow.
-     *
-     * @var Drawing\Shadow
      */
-    protected $shadow;
+    protected Shadow $shadow;
 
     /**
      * Image hyperlink.
-     *
-     * @var null|Hyperlink
      */
-    private $hyperlink;
+    private ?Hyperlink $hyperlink = null;
 
     /**
      * Image type.
-     *
-     * @var int
      */
-    protected $type = IMAGETYPE_UNKNOWN;
+    protected int $type = IMAGETYPE_UNKNOWN;
+
+    /** @var null|SimpleXMLElement|string[] */
+    protected $srcRect = [];
 
     /**
      * Create a new BaseDrawing.
@@ -177,6 +144,11 @@ class BaseDrawing implements IComparable
         $this->imageIndex = self::$imageCounter;
     }
 
+    public function __destruct()
+    {
+        $this->worksheet = null;
+    }
+
     public function getImageIndex(): int
     {
         return $this->imageIndex;
@@ -232,7 +204,7 @@ class BaseDrawing implements IComparable
 
                 while ($iterator->valid()) {
                     if ($iterator->current()->getHashCode() === $this->getHashCode()) {
-                        $this->worksheet->getDrawingCollection()->offsetUnset(/** @scrutinizer ignore-type */ $iterator->key());
+                        $this->worksheet->getDrawingCollection()->offsetUnset($iterator->key());
                         $this->worksheet = null;
 
                         break;
@@ -414,14 +386,14 @@ class BaseDrawing implements IComparable
         return $this;
     }
 
-    public function getShadow(): Drawing\Shadow
+    public function getShadow(): Shadow
     {
         return $this->shadow;
     }
 
-    public function setShadow(?Drawing\Shadow $shadow = null): self
+    public function setShadow(?Shadow $shadow = null): self
     {
-        $this->shadow = $shadow ?? new Drawing\Shadow();
+        $this->shadow = $shadow ?? new Shadow();
 
         return $this;
     }
@@ -431,23 +403,23 @@ class BaseDrawing implements IComparable
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->name .
-            $this->description .
-            (($this->worksheet === null) ? '' : $this->worksheet->getHashCode()) .
-            $this->coordinates .
-            $this->offsetX .
-            $this->offsetY .
-            $this->coordinates2 .
-            $this->offsetX2 .
-            $this->offsetY2 .
-            $this->width .
-            $this->height .
-            $this->rotation .
-            $this->shadow->getHashCode() .
-            __CLASS__
+            $this->name
+            . $this->description
+            . (($this->worksheet === null) ? '' : $this->worksheet->getHashCode())
+            . $this->coordinates
+            . $this->offsetX
+            . $this->offsetY
+            . $this->coordinates2
+            . $this->offsetX2
+            . $this->offsetY2
+            . $this->width
+            . $this->height
+            . $this->rotation
+            . $this->shadow->getHashCode()
+            . __CLASS__
         );
     }
 
@@ -532,4 +504,46 @@ class BaseDrawing implements IComparable
     {
         return in_array($this->editAs, self::VALID_EDIT_AS, true);
     }
+
+    /**
+     * @return null|SimpleXMLElement|string[]
+     */
+    public function getSrcRect()
+    {
+        return $this->srcRect;
+    }
+
+    /**
+     * @param null|SimpleXMLElement|string[] $srcRect
+     */
+    public function setSrcRect($srcRect): self
+    {
+        $this->srcRect = $srcRect;
+
+        return $this;
+    }
+
+    public function setFlipHorizontal(bool $flipHorizontal): self
+    {
+        $this->flipHorizontal = $flipHorizontal;
+
+        return $this;
+    }
+
+    public function getFlipHorizontal(): bool
+    {
+        return $this->flipHorizontal;
+    }
+
+    public function setFlipVertical(bool $flipVertical): self
+    {
+        $this->flipVertical = $flipVertical;
+
+        return $this;
+    }
+
+    public function getFlipVertical(): bool
+    {
+        return $this->flipVertical;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php
index b0d9d05..2522c33 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/CellIterator.php
@@ -17,34 +17,47 @@ abstract class CellIterator implements NativeIterator
 
     public const TREAT_EMPTY_STRING_AS_EMPTY_CELL = 2;
 
+    public const IF_NOT_EXISTS_RETURN_NULL = false;
+
+    public const IF_NOT_EXISTS_CREATE_NEW = true;
+
     /**
      * Worksheet to iterate.
-     *
-     * @var Worksheet
      */
-    protected $worksheet;
+    protected Worksheet $worksheet;
 
     /**
      * Cell Collection to iterate.
-     *
-     * @var Cells
      */
-    protected $cellCollection;
+    protected Cells $cellCollection;
 
     /**
      * Iterate only existing cells.
-     *
-     * @var bool
      */
-    protected $onlyExistingCells = false;
+    protected bool $onlyExistingCells = false;
+
+    /**
+     * If iterating all cells, and a cell doesn't exist, identifies whether a new cell should be created,
+     *    or if the iterator should return a null value.
+     */
+    protected bool $ifNotExists = self::IF_NOT_EXISTS_CREATE_NEW;
 
     /**
      * Destructor.
      */
     public function __destruct()
     {
-        // @phpstan-ignore-next-line
-        $this->worksheet = $this->cellCollection = null;
+        unset($this->worksheet, $this->cellCollection);
+    }
+
+    public function getIfNotExists(): bool
+    {
+        return $this->ifNotExists;
+    }
+
+    public function setIfNotExists(bool $ifNotExists = self::IF_NOT_EXISTS_CREATE_NEW): void
+    {
+        $this->ifNotExists = $ifNotExists;
     }
 
     /**
@@ -56,9 +69,9 @@ abstract class CellIterator implements NativeIterator
     }
 
     /**
-     * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
+     * Validate start/end values for 'IterateOnlyExistingCells' mode, and adjust if necessary.
      */
-    abstract protected function adjustForExistingOnlyRange();
+    abstract protected function adjustForExistingOnlyRange(): void;
 
     /**
      * Set the iterator to loop only existing cells.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
index 390a159..ef4ad2d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Column.php
@@ -4,26 +4,17 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet;
 
 class Column
 {
-    /**
-     * \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet.
-     *
-     * @var Worksheet
-     */
-    private $worksheet;
+    private Worksheet $worksheet;
 
     /**
      * Column index.
-     *
-     * @var string
      */
-    private $columnIndex;
+    private string $columnIndex;
 
     /**
      * Create a new column.
-     *
-     * @param string $columnIndex
      */
-    public function __construct(Worksheet $worksheet, $columnIndex = 'A')
+    public function __construct(Worksheet $worksheet, string $columnIndex = 'A')
     {
         // Set parent and column index
         $this->worksheet = $worksheet;
@@ -35,8 +26,7 @@ class Column
      */
     public function __destruct()
     {
-        // @phpstan-ignore-next-line
-        $this->worksheet = null;
+        unset($this->worksheet);
     }
 
     /**
@@ -51,13 +41,22 @@ class Column
      * Get cell iterator.
      *
      * @param int $startRow The row number at which to start iterating
-     * @param int $endRow Optionally, the row number at which to stop iterating
-     *
-     * @return ColumnCellIterator
+     * @param ?int $endRow Optionally, the row number at which to stop iterating
      */
-    public function getCellIterator($startRow = 1, $endRow = null)
+    public function getCellIterator(int $startRow = 1, ?int $endRow = null, bool $iterateOnlyExistingCells = false): ColumnCellIterator
     {
-        return new ColumnCellIterator($this->worksheet, $this->columnIndex, $startRow, $endRow);
+        return new ColumnCellIterator($this->worksheet, $this->columnIndex, $startRow, $endRow, $iterateOnlyExistingCells);
+    }
+
+    /**
+     * Get row iterator. Synonym for getCellIterator().
+     *
+     * @param int $startRow The row number at which to start iterating
+     * @param ?int $endRow Optionally, the row number at which to stop iterating
+     */
+    public function getRowIterator(int $startRow = 1, ?int $endRow = null, bool $iterateOnlyExistingCells = false): ColumnCellIterator
+    {
+        return $this->getCellIterator($startRow, $endRow, $iterateOnlyExistingCells);
     }
 
     /**
@@ -76,16 +75,17 @@ class Column
      *              Possible Flag Values are:
      *                  CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
      *                  CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
+     * @param int $startRow The row number at which to start checking if cells are empty
+     * @param ?int $endRow Optionally, the row number at which to stop checking if cells are empty
      */
-    public function isEmpty(int $definitionOfEmptyFlags = 0): bool
+    public function isEmpty(int $definitionOfEmptyFlags = 0, int $startRow = 1, ?int $endRow = null): bool
     {
         $nullValueCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
         $emptyStringCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
 
-        $cellIterator = $this->getCellIterator();
+        $cellIterator = $this->getCellIterator($startRow, $endRow);
         $cellIterator->setIterateOnlyExistingCells(true);
         foreach ($cellIterator as $cell) {
-            /** @scrutinizer ignore-call */
             $value = $cell->getValue();
             if ($value === null && $nullValueCellIsEmpty === true) {
                 continue;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
index a518f59..16a007a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php
@@ -13,31 +13,23 @@ class ColumnCellIterator extends CellIterator
 {
     /**
      * Current iterator position.
-     *
-     * @var int
      */
-    private $currentRow;
+    private int $currentRow;
 
     /**
      * Column index.
-     *
-     * @var int
      */
-    private $columnIndex;
+    private int $columnIndex;
 
     /**
      * Start position.
-     *
-     * @var int
      */
-    private $startRow = 1;
+    private int $startRow = 1;
 
     /**
      * End position.
-     *
-     * @var int
      */
-    private $endRow = 1;
+    private int $endRow = 1;
 
     /**
      * Create a new row iterator.
@@ -45,9 +37,9 @@ class ColumnCellIterator extends CellIterator
      * @param Worksheet $worksheet The worksheet to iterate over
      * @param string $columnIndex The column that we want to iterate
      * @param int $startRow The row number at which to start iterating
-     * @param int $endRow Optionally, the row number at which to stop iterating
+     * @param ?int $endRow Optionally, the row number at which to stop iterating
      */
-    public function __construct(Worksheet $worksheet, $columnIndex = 'A', $startRow = 1, $endRow = null)
+    public function __construct(Worksheet $worksheet, string $columnIndex = 'A', int $startRow = 1, ?int $endRow = null, bool $iterateOnlyExistingCells = false)
     {
         // Set subject
         $this->worksheet = $worksheet;
@@ -55,6 +47,7 @@ class ColumnCellIterator extends CellIterator
         $this->columnIndex = Coordinate::columnIndexFromString($columnIndex);
         $this->resetEnd($endRow);
         $this->resetStart($startRow);
+        $this->setIterateOnlyExistingCells($iterateOnlyExistingCells);
     }
 
     /**
@@ -64,7 +57,7 @@ class ColumnCellIterator extends CellIterator
      *
      * @return $this
      */
-    public function resetStart(int $startRow = 1)
+    public function resetStart(int $startRow = 1): static
     {
         $this->startRow = $startRow;
         $this->adjustForExistingOnlyRange();
@@ -76,11 +69,11 @@ class ColumnCellIterator extends CellIterator
     /**
      * (Re)Set the end row.
      *
-     * @param int $endRow The row number at which to stop iterating
+     * @param ?int $endRow The row number at which to stop iterating
      *
      * @return $this
      */
-    public function resetEnd($endRow = null)
+    public function resetEnd(?int $endRow = null): static
     {
         $this->endRow = $endRow ?: $this->worksheet->getHighestRow();
         $this->adjustForExistingOnlyRange();
@@ -95,11 +88,11 @@ class ColumnCellIterator extends CellIterator
      *
      * @return $this
      */
-    public function seek(int $row = 1)
+    public function seek(int $row = 1): static
     {
         if (
-            $this->onlyExistingCells &&
-            (!$this->cellCollection->has(Coordinate::stringFromColumnIndex($this->columnIndex) . $row))
+            $this->onlyExistingCells
+            && (!$this->cellCollection->has(Coordinate::stringFromColumnIndex($this->columnIndex) . $row))
         ) {
             throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
         }
@@ -128,7 +121,11 @@ class ColumnCellIterator extends CellIterator
 
         return $this->cellCollection->has($cellAddress)
             ? $this->cellCollection->get($cellAddress)
-            : $this->worksheet->createNewCell($cellAddress);
+            : (
+                $this->ifNotExists === self::IF_NOT_EXISTS_CREATE_NEW
+                ? $this->worksheet->createNewCell($cellAddress)
+                : null
+            );
     }
 
     /**
@@ -148,9 +145,9 @@ class ColumnCellIterator extends CellIterator
         do {
             ++$this->currentRow;
         } while (
-            ($this->onlyExistingCells) &&
-            ($this->currentRow <= $this->endRow) &&
-            (!$this->cellCollection->has($columnAddress . $this->currentRow))
+            ($this->onlyExistingCells)
+            && ($this->currentRow <= $this->endRow)
+            && (!$this->cellCollection->has($columnAddress . $this->currentRow))
         );
     }
 
@@ -163,9 +160,9 @@ class ColumnCellIterator extends CellIterator
         do {
             --$this->currentRow;
         } while (
-            ($this->onlyExistingCells) &&
-            ($this->currentRow >= $this->startRow) &&
-            (!$this->cellCollection->has($columnAddress . $this->currentRow))
+            ($this->onlyExistingCells)
+            && ($this->currentRow >= $this->startRow)
+            && (!$this->cellCollection->has($columnAddress . $this->currentRow))
         );
     }
 
@@ -185,14 +182,14 @@ class ColumnCellIterator extends CellIterator
         if ($this->onlyExistingCells) {
             $columnAddress = Coordinate::stringFromColumnIndex($this->columnIndex);
             while (
-                (!$this->cellCollection->has($columnAddress . $this->startRow)) &&
-                ($this->startRow <= $this->endRow)
+                (!$this->cellCollection->has($columnAddress . $this->startRow))
+                && ($this->startRow <= $this->endRow)
             ) {
                 ++$this->startRow;
             }
             while (
-                (!$this->cellCollection->has($columnAddress . $this->endRow)) &&
-                ($this->endRow >= $this->startRow)
+                (!$this->cellCollection->has($columnAddress . $this->endRow))
+                && ($this->endRow >= $this->startRow)
             ) {
                 --$this->endRow;
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
index b64ecec..8ac405b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnDimension.php
@@ -9,33 +9,27 @@ class ColumnDimension extends Dimension
 {
     /**
      * Column index.
-     *
-     * @var string
      */
-    private $columnIndex;
+    private ?string $columnIndex;
 
     /**
      * Column width.
      *
      * When this is set to a negative value, the column width should be ignored by IWriter
-     *
-     * @var float
      */
-    private $width = -1;
+    private float $width = -1;
 
     /**
      * Auto size?
-     *
-     * @var bool
      */
-    private $autoSize = false;
+    private bool $autoSize = false;
 
     /**
      * Create a new ColumnDimension.
      *
-     * @param string $index Character column index
+     * @param ?string $index Character column index
      */
-    public function __construct($index = 'A')
+    public function __construct(?string $index = 'A')
     {
         // Initialise values
         $this->columnIndex = $index;
@@ -47,7 +41,7 @@ class ColumnDimension extends Dimension
     /**
      * Get column index as string eg: 'A'.
      */
-    public function getColumnIndex(): string
+    public function getColumnIndex(): ?string
     {
         return $this->columnIndex;
     }
@@ -67,7 +61,7 @@ class ColumnDimension extends Dimension
      */
     public function getColumnNumeric(): int
     {
-        return Coordinate::columnIndexFromString($this->columnIndex);
+        return Coordinate::columnIndexFromString($this->columnIndex ?? '');
     }
 
     /**
@@ -106,7 +100,7 @@ class ColumnDimension extends Dimension
      *
      * @return $this
      */
-    public function setWidth(float $width, ?string $unitOfMeasure = null)
+    public function setWidth(float $width, ?string $unitOfMeasure = null): static
     {
         $this->width = ($unitOfMeasure === null || $width < 0)
             ? $width
@@ -128,7 +122,7 @@ class ColumnDimension extends Dimension
      *
      * @return $this
      */
-    public function setAutoSize(bool $autosizeEnabled)
+    public function setAutoSize(bool $autosizeEnabled): static
     {
         $this->autoSize = $autosizeEnabled;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
index fffef12..69939c3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ColumnIterator.php
@@ -14,40 +14,32 @@ class ColumnIterator implements NativeIterator
 {
     /**
      * Worksheet to iterate.
-     *
-     * @var Worksheet
      */
-    private $worksheet;
+    private Worksheet $worksheet;
 
     /**
      * Current iterator position.
-     *
-     * @var int
      */
-    private $currentColumnIndex = 1;
+    private int $currentColumnIndex = 1;
 
     /**
      * Start position.
-     *
-     * @var int
      */
-    private $startColumnIndex = 1;
+    private int $startColumnIndex = 1;
 
     /**
      * End position.
-     *
-     * @var int
      */
-    private $endColumnIndex = 1;
+    private int $endColumnIndex = 1;
 
     /**
      * Create a new column iterator.
      *
      * @param Worksheet $worksheet The worksheet to iterate over
      * @param string $startColumn The column address at which to start iterating
-     * @param string $endColumn Optionally, the column address at which to stop iterating
+     * @param ?string $endColumn Optionally, the column address at which to stop iterating
      */
-    public function __construct(Worksheet $worksheet, $startColumn = 'A', $endColumn = null)
+    public function __construct(Worksheet $worksheet, string $startColumn = 'A', ?string $endColumn = null)
     {
         // Set subject
         $this->worksheet = $worksheet;
@@ -60,8 +52,7 @@ class ColumnIterator implements NativeIterator
      */
     public function __destruct()
     {
-        // @phpstan-ignore-next-line
-        $this->worksheet = null;
+        unset($this->worksheet);
     }
 
     /**
@@ -71,7 +62,7 @@ class ColumnIterator implements NativeIterator
      *
      * @return $this
      */
-    public function resetStart(string $startColumn = 'A')
+    public function resetStart(string $startColumn = 'A'): static
     {
         $startColumnIndex = Coordinate::columnIndexFromString($startColumn);
         if ($startColumnIndex > Coordinate::columnIndexFromString($this->worksheet->getHighestColumn())) {
@@ -92,11 +83,11 @@ class ColumnIterator implements NativeIterator
     /**
      * (Re)Set the end column.
      *
-     * @param string $endColumn The column address at which to stop iterating
+     * @param ?string $endColumn The column address at which to stop iterating
      *
      * @return $this
      */
-    public function resetEnd($endColumn = null)
+    public function resetEnd(?string $endColumn = null): static
     {
         $endColumn = $endColumn ?: $this->worksheet->getHighestColumn();
         $this->endColumnIndex = Coordinate::columnIndexFromString($endColumn);
@@ -111,7 +102,7 @@ class ColumnIterator implements NativeIterator
      *
      * @return $this
      */
-    public function seek(string $column = 'A')
+    public function seek(string $column = 'A'): static
     {
         $column = Coordinate::columnIndexFromString($column);
         if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
index ba03b5b..7a88f8c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
@@ -8,38 +8,30 @@ abstract class Dimension
 {
     /**
      * Visible?
-     *
-     * @var bool
      */
-    private $visible = true;
+    private bool $visible = true;
 
     /**
      * Outline level.
-     *
-     * @var int
      */
-    private $outlineLevel = 0;
+    private int $outlineLevel = 0;
 
     /**
      * Collapsed.
-     *
-     * @var bool
      */
-    private $collapsed = false;
+    private bool $collapsed = false;
 
     /**
      * Index to cellXf. Null value means row has no explicit cellXf format.
-     *
-     * @var null|int
      */
-    private $xfIndex;
+    private ?int $xfIndex;
 
     /**
      * Create a new Dimension.
      *
-     * @param int $initialValue Numeric row index
+     * @param ?int $initialValue Numeric row index
      */
-    public function __construct($initialValue = null)
+    public function __construct(?int $initialValue = null)
     {
         // set dimension as unformatted by default
         $this->xfIndex = $initialValue;
@@ -112,8 +104,6 @@ abstract class Dimension
 
     /**
      * Get index to cellXf.
-     *
-     * @return int
      */
     public function getXfIndex(): ?int
     {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
index 7d95753..be02779 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing.php
@@ -16,17 +16,13 @@ class Drawing extends BaseDrawing
 
     /**
      * Path.
-     *
-     * @var string
      */
-    private $path;
+    private string $path;
 
     /**
      * Whether or not we are dealing with a URL.
-     *
-     * @var bool
      */
-    private $isUrl;
+    private bool $isUrl;
 
     /**
      * Create a new Drawing.
@@ -43,10 +39,8 @@ class Drawing extends BaseDrawing
 
     /**
      * Get Filename.
-     *
-     * @return string
      */
-    public function getFilename()
+    public function getFilename(): string
     {
         return basename($this->path);
     }
@@ -61,10 +55,8 @@ class Drawing extends BaseDrawing
 
     /**
      * Get Extension.
-     *
-     * @return string
      */
-    public function getExtension()
+    public function getExtension(): string
     {
         $exploded = explode('.', basename($this->path));
 
@@ -73,10 +65,8 @@ class Drawing extends BaseDrawing
 
     /**
      * Get full filepath to store drawing in zip archive.
-     *
-     * @return string
      */
-    public function getMediaFilename()
+    public function getMediaFilename(): string
     {
         if (!array_key_exists($this->type, self::IMAGE_TYPES_CONVERTION_MAP)) {
             throw new PhpSpreadsheetException('Unsupported image type in comment background. Supported types: PNG, JPEG, BMP, GIF.');
@@ -87,10 +77,8 @@ class Drawing extends BaseDrawing
 
     /**
      * Get Path.
-     *
-     * @return string
      */
-    public function getPath()
+    public function getPath(): string
     {
         return $this->path;
     }
@@ -100,11 +88,11 @@ class Drawing extends BaseDrawing
      *
      * @param string $path File path
      * @param bool $verifyFile Verify file
-     * @param ZipArchive $zip Zip archive instance
+     * @param ?ZipArchive $zip Zip archive instance
      *
      * @return $this
      */
-    public function setPath($path, $verifyFile = true, $zip = null)
+    public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null): static
     {
         if ($verifyFile && preg_match('~^data:image/[a-z]+;base64,~', $path) !== 1) {
             // Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
@@ -165,12 +153,12 @@ class Drawing extends BaseDrawing
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->path .
-            parent::getHashCode() .
-            __CLASS__
+            $this->path
+            . parent::getHashCode()
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
index a461a51..e002d5f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Drawing/Shadow.php
@@ -19,56 +19,42 @@ class Shadow implements IComparable
 
     /**
      * Visible.
-     *
-     * @var bool
      */
-    private $visible;
+    private bool $visible;
 
     /**
      * Blur radius.
      *
      * Defaults to 6
-     *
-     * @var int
      */
-    private $blurRadius;
+    private int $blurRadius;
 
     /**
      * Shadow distance.
      *
      * Defaults to 2
-     *
-     * @var int
      */
-    private $distance;
+    private int $distance;
 
     /**
      * Shadow direction (in degrees).
-     *
-     * @var int
      */
-    private $direction;
+    private int $direction;
 
     /**
      * Shadow alignment.
-     *
-     * @var string
      */
-    private $alignment;
+    private string $alignment;
 
     /**
      * Color.
-     *
-     * @var Color
      */
-    private $color;
+    private Color $color;
 
     /**
      * Alpha.
-     *
-     * @var int
      */
-    private $alpha;
+    private int $alpha;
 
     /**
      * Create a new Shadow.
@@ -87,10 +73,8 @@ class Shadow implements IComparable
 
     /**
      * Get Visible.
-     *
-     * @return bool
      */
-    public function getVisible()
+    public function getVisible(): bool
     {
         return $this->visible;
     }
@@ -98,11 +82,9 @@ class Shadow implements IComparable
     /**
      * Set Visible.
      *
-     * @param bool $visible
-     *
      * @return $this
      */
-    public function setVisible($visible)
+    public function setVisible(bool $visible): static
     {
         $this->visible = $visible;
 
@@ -111,10 +93,8 @@ class Shadow implements IComparable
 
     /**
      * Get Blur radius.
-     *
-     * @return int
      */
-    public function getBlurRadius()
+    public function getBlurRadius(): int
     {
         return $this->blurRadius;
     }
@@ -122,11 +102,9 @@ class Shadow implements IComparable
     /**
      * Set Blur radius.
      *
-     * @param int $blurRadius
-     *
      * @return $this
      */
-    public function setBlurRadius($blurRadius)
+    public function setBlurRadius(int $blurRadius): static
     {
         $this->blurRadius = $blurRadius;
 
@@ -135,10 +113,8 @@ class Shadow implements IComparable
 
     /**
      * Get Shadow distance.
-     *
-     * @return int
      */
-    public function getDistance()
+    public function getDistance(): int
     {
         return $this->distance;
     }
@@ -146,11 +122,9 @@ class Shadow implements IComparable
     /**
      * Set Shadow distance.
      *
-     * @param int $distance
-     *
      * @return $this
      */
-    public function setDistance($distance)
+    public function setDistance(int $distance): static
     {
         $this->distance = $distance;
 
@@ -159,10 +133,8 @@ class Shadow implements IComparable
 
     /**
      * Get Shadow direction (in degrees).
-     *
-     * @return int
      */
-    public function getDirection()
+    public function getDirection(): int
     {
         return $this->direction;
     }
@@ -170,11 +142,9 @@ class Shadow implements IComparable
     /**
      * Set Shadow direction (in degrees).
      *
-     * @param int $direction
-     *
      * @return $this
      */
-    public function setDirection($direction)
+    public function setDirection(int $direction): static
     {
         $this->direction = $direction;
 
@@ -183,10 +153,8 @@ class Shadow implements IComparable
 
     /**
      * Get Shadow alignment.
-     *
-     * @return string
      */
-    public function getAlignment()
+    public function getAlignment(): string
     {
         return $this->alignment;
     }
@@ -194,11 +162,9 @@ class Shadow implements IComparable
     /**
      * Set Shadow alignment.
      *
-     * @param string $alignment
-     *
      * @return $this
      */
-    public function setAlignment($alignment)
+    public function setAlignment(string $alignment): static
     {
         $this->alignment = $alignment;
 
@@ -207,10 +173,8 @@ class Shadow implements IComparable
 
     /**
      * Get Color.
-     *
-     * @return Color
      */
-    public function getColor()
+    public function getColor(): Color
     {
         return $this->color;
     }
@@ -220,7 +184,7 @@ class Shadow implements IComparable
      *
      * @return $this
      */
-    public function setColor(?Color $color = null)
+    public function setColor(Color $color): static
     {
         $this->color = $color;
 
@@ -229,10 +193,8 @@ class Shadow implements IComparable
 
     /**
      * Get Alpha.
-     *
-     * @return int
      */
-    public function getAlpha()
+    public function getAlpha(): int
     {
         return $this->alpha;
     }
@@ -240,11 +202,9 @@ class Shadow implements IComparable
     /**
      * Set Alpha.
      *
-     * @param int $alpha
-     *
      * @return $this
      */
-    public function setAlpha($alpha)
+    public function setAlpha(int $alpha): static
     {
         $this->alpha = $alpha;
 
@@ -256,17 +216,17 @@ class Shadow implements IComparable
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            ($this->visible ? 't' : 'f') .
-            $this->blurRadius .
-            $this->distance .
-            $this->direction .
-            $this->alignment .
-            $this->color->getHashCode() .
-            $this->alpha .
-            __CLASS__
+            ($this->visible ? 't' : 'f')
+            . $this->blurRadius
+            . $this->distance
+            . $this->direction
+            . $this->alignment
+            . $this->color->getHashCode()
+            . $this->alpha
+            . __CLASS__
         );
     }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
index c3504d8..68716c0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooter.php
@@ -75,80 +75,60 @@ class HeaderFooter
 
     /**
      * OddHeader.
-     *
-     * @var string
      */
-    private $oddHeader = '';
+    private string $oddHeader = '';
 
     /**
      * OddFooter.
-     *
-     * @var string
      */
-    private $oddFooter = '';
+    private string $oddFooter = '';
 
     /**
      * EvenHeader.
-     *
-     * @var string
      */
-    private $evenHeader = '';
+    private string $evenHeader = '';
 
     /**
      * EvenFooter.
-     *
-     * @var string
      */
-    private $evenFooter = '';
+    private string $evenFooter = '';
 
     /**
      * FirstHeader.
-     *
-     * @var string
      */
-    private $firstHeader = '';
+    private string $firstHeader = '';
 
     /**
      * FirstFooter.
-     *
-     * @var string
      */
-    private $firstFooter = '';
+    private string $firstFooter = '';
 
     /**
      * Different header for Odd/Even, defaults to false.
-     *
-     * @var bool
      */
-    private $differentOddEven = false;
+    private bool $differentOddEven = false;
 
     /**
      * Different header for first page, defaults to false.
-     *
-     * @var bool
      */
-    private $differentFirst = false;
+    private bool $differentFirst = false;
 
     /**
      * Scale with document, defaults to true.
-     *
-     * @var bool
      */
-    private $scaleWithDocument = true;
+    private bool $scaleWithDocument = true;
 
     /**
      * Align with margins, defaults to true.
-     *
-     * @var bool
      */
-    private $alignWithMargins = true;
+    private bool $alignWithMargins = true;
 
     /**
      * Header/footer images.
      *
      * @var HeaderFooterDrawing[]
      */
-    private $headerFooterImages = [];
+    private array $headerFooterImages = [];
 
     /**
      * Create a new HeaderFooter.
@@ -159,10 +139,8 @@ class HeaderFooter
 
     /**
      * Get OddHeader.
-     *
-     * @return string
      */
-    public function getOddHeader()
+    public function getOddHeader(): string
     {
         return $this->oddHeader;
     }
@@ -170,11 +148,9 @@ class HeaderFooter
     /**
      * Set OddHeader.
      *
-     * @param string $oddHeader
-     *
      * @return $this
      */
-    public function setOddHeader($oddHeader)
+    public function setOddHeader(string $oddHeader): static
     {
         $this->oddHeader = $oddHeader;
 
@@ -183,10 +159,8 @@ class HeaderFooter
 
     /**
      * Get OddFooter.
-     *
-     * @return string
      */
-    public function getOddFooter()
+    public function getOddFooter(): string
     {
         return $this->oddFooter;
     }
@@ -194,11 +168,9 @@ class HeaderFooter
     /**
      * Set OddFooter.
      *
-     * @param string $oddFooter
-     *
      * @return $this
      */
-    public function setOddFooter($oddFooter)
+    public function setOddFooter(string $oddFooter): static
     {
         $this->oddFooter = $oddFooter;
 
@@ -207,10 +179,8 @@ class HeaderFooter
 
     /**
      * Get EvenHeader.
-     *
-     * @return string
      */
-    public function getEvenHeader()
+    public function getEvenHeader(): string
     {
         return $this->evenHeader;
     }
@@ -218,11 +188,9 @@ class HeaderFooter
     /**
      * Set EvenHeader.
      *
-     * @param string $eventHeader
-     *
      * @return $this
      */
-    public function setEvenHeader($eventHeader)
+    public function setEvenHeader(string $eventHeader): static
     {
         $this->evenHeader = $eventHeader;
 
@@ -231,10 +199,8 @@ class HeaderFooter
 
     /**
      * Get EvenFooter.
-     *
-     * @return string
      */
-    public function getEvenFooter()
+    public function getEvenFooter(): string
     {
         return $this->evenFooter;
     }
@@ -242,11 +208,9 @@ class HeaderFooter
     /**
      * Set EvenFooter.
      *
-     * @param string $evenFooter
-     *
      * @return $this
      */
-    public function setEvenFooter($evenFooter)
+    public function setEvenFooter(string $evenFooter): static
     {
         $this->evenFooter = $evenFooter;
 
@@ -255,10 +219,8 @@ class HeaderFooter
 
     /**
      * Get FirstHeader.
-     *
-     * @return string
      */
-    public function getFirstHeader()
+    public function getFirstHeader(): string
     {
         return $this->firstHeader;
     }
@@ -266,11 +228,9 @@ class HeaderFooter
     /**
      * Set FirstHeader.
      *
-     * @param string $firstHeader
-     *
      * @return $this
      */
-    public function setFirstHeader($firstHeader)
+    public function setFirstHeader(string $firstHeader): static
     {
         $this->firstHeader = $firstHeader;
 
@@ -279,10 +239,8 @@ class HeaderFooter
 
     /**
      * Get FirstFooter.
-     *
-     * @return string
      */
-    public function getFirstFooter()
+    public function getFirstFooter(): string
     {
         return $this->firstFooter;
     }
@@ -290,11 +248,9 @@ class HeaderFooter
     /**
      * Set FirstFooter.
      *
-     * @param string $firstFooter
-     *
      * @return $this
      */
-    public function setFirstFooter($firstFooter)
+    public function setFirstFooter(string $firstFooter): static
     {
         $this->firstFooter = $firstFooter;
 
@@ -303,10 +259,8 @@ class HeaderFooter
 
     /**
      * Get DifferentOddEven.
-     *
-     * @return bool
      */
-    public function getDifferentOddEven()
+    public function getDifferentOddEven(): bool
     {
         return $this->differentOddEven;
     }
@@ -314,11 +268,9 @@ class HeaderFooter
     /**
      * Set DifferentOddEven.
      *
-     * @param bool $differentOddEvent
-     *
      * @return $this
      */
-    public function setDifferentOddEven($differentOddEvent)
+    public function setDifferentOddEven(bool $differentOddEvent): static
     {
         $this->differentOddEven = $differentOddEvent;
 
@@ -327,10 +279,8 @@ class HeaderFooter
 
     /**
      * Get DifferentFirst.
-     *
-     * @return bool
      */
-    public function getDifferentFirst()
+    public function getDifferentFirst(): bool
     {
         return $this->differentFirst;
     }
@@ -338,11 +288,9 @@ class HeaderFooter
     /**
      * Set DifferentFirst.
      *
-     * @param bool $differentFirst
-     *
      * @return $this
      */
-    public function setDifferentFirst($differentFirst)
+    public function setDifferentFirst(bool $differentFirst): static
     {
         $this->differentFirst = $differentFirst;
 
@@ -351,10 +299,8 @@ class HeaderFooter
 
     /**
      * Get ScaleWithDocument.
-     *
-     * @return bool
      */
-    public function getScaleWithDocument()
+    public function getScaleWithDocument(): bool
     {
         return $this->scaleWithDocument;
     }
@@ -362,11 +308,9 @@ class HeaderFooter
     /**
      * Set ScaleWithDocument.
      *
-     * @param bool $scaleWithDocument
-     *
      * @return $this
      */
-    public function setScaleWithDocument($scaleWithDocument)
+    public function setScaleWithDocument(bool $scaleWithDocument): static
     {
         $this->scaleWithDocument = $scaleWithDocument;
 
@@ -375,10 +319,8 @@ class HeaderFooter
 
     /**
      * Get AlignWithMargins.
-     *
-     * @return bool
      */
-    public function getAlignWithMargins()
+    public function getAlignWithMargins(): bool
     {
         return $this->alignWithMargins;
     }
@@ -386,11 +328,9 @@ class HeaderFooter
     /**
      * Set AlignWithMargins.
      *
-     * @param bool $alignWithMargins
-     *
      * @return $this
      */
-    public function setAlignWithMargins($alignWithMargins)
+    public function setAlignWithMargins(bool $alignWithMargins): static
     {
         $this->alignWithMargins = $alignWithMargins;
 
@@ -400,11 +340,9 @@ class HeaderFooter
     /**
      * Add header/footer image.
      *
-     * @param string $location
-     *
      * @return $this
      */
-    public function addImage(HeaderFooterDrawing $image, $location = self::IMAGE_HEADER_LEFT)
+    public function addImage(HeaderFooterDrawing $image, string $location = self::IMAGE_HEADER_LEFT): static
     {
         $this->headerFooterImages[$location] = $image;
 
@@ -414,11 +352,9 @@ class HeaderFooter
     /**
      * Remove header/footer image.
      *
-     * @param string $location
-     *
      * @return $this
      */
-    public function removeImage($location = self::IMAGE_HEADER_LEFT)
+    public function removeImage(string $location = self::IMAGE_HEADER_LEFT): static
     {
         if (isset($this->headerFooterImages[$location])) {
             unset($this->headerFooterImages[$location]);
@@ -434,7 +370,7 @@ class HeaderFooter
      *
      * @return $this
      */
-    public function setImages(array $images)
+    public function setImages(array $images): static
     {
         $this->headerFooterImages = $images;
 
@@ -446,7 +382,7 @@ class HeaderFooter
      *
      * @return HeaderFooterDrawing[]
      */
-    public function getImages()
+    public function getImages(): array
     {
         // Sort array
         $images = [];
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
index b42c732..6dddb79 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/HeaderFooterDrawing.php
@@ -9,16 +9,16 @@ class HeaderFooterDrawing extends Drawing
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->getPath() .
-            $this->name .
-            $this->offsetX .
-            $this->offsetY .
-            $this->width .
-            $this->height .
-            __CLASS__
+            $this->getPath()
+            . $this->name
+            . $this->offsetX
+            . $this->offsetY
+            . $this->width
+            . $this->height
+            . __CLASS__
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php
index dc08204..9223718 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Iterator.php
@@ -11,17 +11,13 @@ class Iterator implements \Iterator
 {
     /**
      * Spreadsheet to iterate.
-     *
-     * @var Spreadsheet
      */
-    private $subject;
+    private Spreadsheet $subject;
 
     /**
      * Current iterator position.
-     *
-     * @var int
      */
-    private $position = 0;
+    private int $position = 0;
 
     /**
      * Create a new worksheet iterator.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
index c1c4160..e15a798 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
@@ -28,34 +28,23 @@ class MemoryDrawing extends BaseDrawing
 
     /**
      * Image resource.
-     *
-     * @var null|GdImage|resource
      */
-    private $imageResource;
+    private null|GdImage $imageResource = null;
 
     /**
      * Rendering function.
-     *
-     * @var string
      */
-    private $renderingFunction;
+    private string $renderingFunction;
 
     /**
      * Mime type.
-     *
-     * @var string
      */
-    private $mimeType;
+    private string $mimeType;
 
     /**
      * Unique name.
-     *
-     * @var string
      */
-    private $uniqueName;
-
-    /** @var null|resource */
-    private $alwaysNull;
+    private string $uniqueName;
 
     /**
      * Create a new MemoryDrawing.
@@ -66,7 +55,6 @@ class MemoryDrawing extends BaseDrawing
         $this->renderingFunction = self::RENDERING_DEFAULT;
         $this->mimeType = self::MIMETYPE_DEFAULT;
         $this->uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
-        $this->alwaysNull = null;
 
         // Initialize parent
         parent::__construct();
@@ -75,10 +63,10 @@ class MemoryDrawing extends BaseDrawing
     public function __destruct()
     {
         if ($this->imageResource) {
-            $rslt = @imagedestroy($this->imageResource);
-            // "Fix" for Scrutinizer
-            $this->imageResource = $rslt ? null : $this->alwaysNull;
+            @imagedestroy($this->imageResource);
+            $this->imageResource = null;
         }
+        $this->worksheet = null;
     }
 
     public function __clone()
@@ -113,10 +101,8 @@ class MemoryDrawing extends BaseDrawing
             // If the image has transparency...
             $transparent = imagecolortransparent($this->imageResource);
             if ($transparent >= 0) {
+                // Starting with Php8.0, next function throws rather than return false
                 $rgb = imagecolorsforindex($this->imageResource, $transparent);
-                if (empty($rgb)) {
-                    throw new Exception('Could not get image colors');
-                }
 
                 imagesavealpha($clone, true);
                 $color = imagecolorallocatealpha($clone, $rgb['red'], $rgb['green'], $rgb['blue'], $rgb['alpha']);
@@ -162,6 +148,9 @@ class MemoryDrawing extends BaseDrawing
         }
 
         $mimeType = self::identifyMimeType($imageString);
+        if (imageistruecolor($gdImage) || imagecolortransparent($gdImage) >= 0) {
+            imagesavealpha($gdImage, true);
+        }
         $renderingFunction = self::identifyRenderingFunction($mimeType);
 
         $drawing = new self();
@@ -174,16 +163,12 @@ class MemoryDrawing extends BaseDrawing
 
     private static function identifyRenderingFunction(string $mimeType): string
     {
-        switch ($mimeType) {
-            case self::MIMETYPE_PNG:
-                return self::RENDERING_PNG;
-            case self::MIMETYPE_JPEG:
-                return self::RENDERING_JPEG;
-            case self::MIMETYPE_GIF:
-                return self::RENDERING_GIF;
-        }
-
-        return self::RENDERING_DEFAULT;
+        return match ($mimeType) {
+            self::MIMETYPE_PNG => self::RENDERING_PNG,
+            self::MIMETYPE_JPEG => self::RENDERING_JPEG,
+            self::MIMETYPE_GIF => self::RENDERING_GIF,
+            default => self::RENDERING_DEFAULT,
+        };
     }
 
     /**
@@ -230,7 +215,7 @@ class MemoryDrawing extends BaseDrawing
         if (function_exists('getimagesize')) {
             $imageSize = @getimagesize($temporaryFileName);
             if (is_array($imageSize)) {
-                $mimeType = $imageSize['mime'] ?? null;
+                $mimeType = $imageSize['mime'];
 
                 return self::supportedMimeTypes($mimeType);
             }
@@ -250,10 +235,8 @@ class MemoryDrawing extends BaseDrawing
 
     /**
      * Get image resource.
-     *
-     * @return null|GdImage|resource
      */
-    public function getImageResource()
+    public function getImageResource(): ?GdImage
     {
         return $this->imageResource;
     }
@@ -261,11 +244,9 @@ class MemoryDrawing extends BaseDrawing
     /**
      * Set image resource.
      *
-     * @param GdImage|resource $value
-     *
      * @return $this
      */
-    public function setImageResource($value)
+    public function setImageResource(?GdImage $value): static
     {
         $this->imageResource = $value;
 
@@ -280,10 +261,8 @@ class MemoryDrawing extends BaseDrawing
 
     /**
      * Get rendering function.
-     *
-     * @return string
      */
-    public function getRenderingFunction()
+    public function getRenderingFunction(): string
     {
         return $this->renderingFunction;
     }
@@ -295,7 +274,7 @@ class MemoryDrawing extends BaseDrawing
      *
      * @return $this
      */
-    public function setRenderingFunction($value)
+    public function setRenderingFunction(string $value): static
     {
         $this->renderingFunction = $value;
 
@@ -304,10 +283,8 @@ class MemoryDrawing extends BaseDrawing
 
     /**
      * Get mime type.
-     *
-     * @return string
      */
-    public function getMimeType()
+    public function getMimeType(): string
     {
         return $this->mimeType;
     }
@@ -319,7 +296,7 @@ class MemoryDrawing extends BaseDrawing
      *
      * @return $this
      */
-    public function setMimeType($value)
+    public function setMimeType(string $value): static
     {
         $this->mimeType = $value;
 
@@ -343,14 +320,14 @@ class MemoryDrawing extends BaseDrawing
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         return md5(
-            $this->renderingFunction .
-            $this->mimeType .
-            $this->uniqueName .
-            parent::getHashCode() .
-            __CLASS__
+            $this->renderingFunction
+            . $this->mimeType
+            . $this->uniqueName
+            . parent::getHashCode()
+            . __CLASS__
         );
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageBreak.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageBreak.php
new file mode 100644
index 0000000..05a6074
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageBreak.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+use PhpOffice\PhpSpreadsheet\Calculation\Functions;
+use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+
+class PageBreak
+{
+    private int $breakType;
+
+    private string $coordinate;
+
+    private int $maxColOrRow;
+
+    /**
+     * @param array{0: int, 1: int}|CellAddress|string $coordinate
+     */
+    public function __construct(int $breakType, CellAddress|string|array $coordinate, int $maxColOrRow = -1)
+    {
+        $coordinate = Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate));
+        $this->breakType = $breakType;
+        $this->coordinate = $coordinate;
+        $this->maxColOrRow = $maxColOrRow;
+    }
+
+    public function getBreakType(): int
+    {
+        return $this->breakType;
+    }
+
+    public function getCoordinate(): string
+    {
+        return $this->coordinate;
+    }
+
+    public function getMaxColOrRow(): int
+    {
+        return $this->maxColOrRow;
+    }
+
+    public function getColumnInt(): int
+    {
+        return Coordinate::indexesFromString($this->coordinate)[0];
+    }
+
+    public function getRow(): int
+    {
+        return Coordinate::indexesFromString($this->coordinate)[1];
+    }
+
+    public function getColumnString(): string
+    {
+        return Coordinate::indexesFromString($this->coordinate)[2];
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
index 34e1145..1b3767a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageMargins.php
@@ -6,45 +6,33 @@ class PageMargins
 {
     /**
      * Left.
-     *
-     * @var float
      */
-    private $left = 0.7;
+    private float $left = 0.7;
 
     /**
      * Right.
-     *
-     * @var float
      */
-    private $right = 0.7;
+    private float $right = 0.7;
 
     /**
      * Top.
-     *
-     * @var float
      */
-    private $top = 0.75;
+    private float $top = 0.75;
 
     /**
      * Bottom.
-     *
-     * @var float
      */
-    private $bottom = 0.75;
+    private float $bottom = 0.75;
 
     /**
      * Header.
-     *
-     * @var float
      */
-    private $header = 0.3;
+    private float $header = 0.3;
 
     /**
      * Footer.
-     *
-     * @var float
      */
-    private $footer = 0.3;
+    private float $footer = 0.3;
 
     /**
      * Create a new PageMargins.
@@ -55,10 +43,8 @@ class PageMargins
 
     /**
      * Get Left.
-     *
-     * @return float
      */
-    public function getLeft()
+    public function getLeft(): float
     {
         return $this->left;
     }
@@ -66,11 +52,9 @@ class PageMargins
     /**
      * Set Left.
      *
-     * @param float $left
-     *
      * @return $this
      */
-    public function setLeft($left)
+    public function setLeft(float $left): static
     {
         $this->left = $left;
 
@@ -79,10 +63,8 @@ class PageMargins
 
     /**
      * Get Right.
-     *
-     * @return float
      */
-    public function getRight()
+    public function getRight(): float
     {
         return $this->right;
     }
@@ -90,11 +72,9 @@ class PageMargins
     /**
      * Set Right.
      *
-     * @param float $right
-     *
      * @return $this
      */
-    public function setRight($right)
+    public function setRight(float $right): static
     {
         $this->right = $right;
 
@@ -103,10 +83,8 @@ class PageMargins
 
     /**
      * Get Top.
-     *
-     * @return float
      */
-    public function getTop()
+    public function getTop(): float
     {
         return $this->top;
     }
@@ -114,11 +92,9 @@ class PageMargins
     /**
      * Set Top.
      *
-     * @param float $top
-     *
      * @return $this
      */
-    public function setTop($top)
+    public function setTop(float $top): static
     {
         $this->top = $top;
 
@@ -127,10 +103,8 @@ class PageMargins
 
     /**
      * Get Bottom.
-     *
-     * @return float
      */
-    public function getBottom()
+    public function getBottom(): float
     {
         return $this->bottom;
     }
@@ -138,11 +112,9 @@ class PageMargins
     /**
      * Set Bottom.
      *
-     * @param float $bottom
-     *
      * @return $this
      */
-    public function setBottom($bottom)
+    public function setBottom(float $bottom): static
     {
         $this->bottom = $bottom;
 
@@ -151,10 +123,8 @@ class PageMargins
 
     /**
      * Get Header.
-     *
-     * @return float
      */
-    public function getHeader()
+    public function getHeader(): float
     {
         return $this->header;
     }
@@ -162,11 +132,9 @@ class PageMargins
     /**
      * Set Header.
      *
-     * @param float $header
-     *
      * @return $this
      */
-    public function setHeader($header)
+    public function setHeader(float $header): static
     {
         $this->header = $header;
 
@@ -175,10 +143,8 @@ class PageMargins
 
     /**
      * Get Footer.
-     *
-     * @return float
      */
-    public function getFooter()
+    public function getFooter(): float
     {
         return $this->footer;
     }
@@ -186,32 +152,15 @@ class PageMargins
     /**
      * Set Footer.
      *
-     * @param float $footer
-     *
      * @return $this
      */
-    public function setFooter($footer)
+    public function setFooter(float $footer): static
     {
         $this->footer = $footer;
 
         return $this;
     }
 
-    /**
-     * Implement PHP __clone to create a deep clone, not just a shallow copy.
-     */
-    public function __clone()
-    {
-        $vars = get_object_vars($this);
-        foreach ($vars as $key => $value) {
-            if (is_object($value)) {
-                $this->$key = clone $value;
-            } else {
-                $this->$key = $value;
-            }
-        }
-    }
-
     public static function fromCentimeters(float $value): float
     {
         return $value / 2.54;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
index 93030db..f8c2dd1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/PageSetup.php
@@ -161,110 +161,85 @@ class PageSetup
 
     /**
      * Paper size default.
-     *
-     * @var int
      */
-    private static $paperSizeDefault = self::PAPERSIZE_LETTER;
+    private static int $paperSizeDefault = self::PAPERSIZE_LETTER;
 
     /**
      * Paper size.
-     *
-     * @var ?int
      */
-    private $paperSize;
+    private ?int $paperSize = null;
 
     /**
      * Orientation default.
-     *
-     * @var string
      */
-    private static $orientationDefault = self::ORIENTATION_DEFAULT;
+    private static string $orientationDefault = self::ORIENTATION_DEFAULT;
 
     /**
      * Orientation.
-     *
-     * @var string
      */
-    private $orientation;
+    private string $orientation;
 
     /**
      * Scale (Print Scale).
      *
      * Print scaling. Valid values range from 10 to 400
      * This setting is overridden when fitToWidth and/or fitToHeight are in use
-     *
-     * @var null|int
      */
-    private $scale = 100;
+    private ?int $scale = 100;
 
     /**
      * Fit To Page
      * Whether scale or fitToWith / fitToHeight applies.
-     *
-     * @var bool
      */
-    private $fitToPage = false;
+    private bool $fitToPage = false;
 
     /**
      * Fit To Height
      * Number of vertical pages to fit on.
-     *
-     * @var null|int
      */
-    private $fitToHeight = 1;
+    private ?int $fitToHeight = 1;
 
     /**
      * Fit To Width
      * Number of horizontal pages to fit on.
-     *
-     * @var null|int
      */
-    private $fitToWidth = 1;
+    private ?int $fitToWidth = 1;
 
     /**
      * Columns to repeat at left.
      *
      * @var array Containing start column and end column, empty array if option unset
      */
-    private $columnsToRepeatAtLeft = ['', ''];
+    private array $columnsToRepeatAtLeft = ['', ''];
 
     /**
      * Rows to repeat at top.
      *
      * @var array Containing start row number and end row number, empty array if option unset
      */
-    private $rowsToRepeatAtTop = [0, 0];
+    private array $rowsToRepeatAtTop = [0, 0];
 
     /**
      * Center page horizontally.
-     *
-     * @var bool
      */
-    private $horizontalCentered = false;
+    private bool $horizontalCentered = false;
 
     /**
      * Center page vertically.
-     *
-     * @var bool
      */
-    private $verticalCentered = false;
+    private bool $verticalCentered = false;
 
     /**
      * Print area.
-     *
-     * @var null|string
      */
-    private $printArea;
+    private ?string $printArea = null;
 
     /**
      * First page number.
-     *
-     * @var ?int
      */
-    private $firstPageNumber;
+    private ?int $firstPageNumber = null;
 
-    /** @var string */
-    private $pageOrder = self::PAGEORDER_DOWN_THEN_OVER;
+    private string $pageOrder = self::PAGEORDER_DOWN_THEN_OVER;
 
     /**
      * Create a new PageSetup.
@@ -276,10 +251,8 @@ class PageSetup
 
     /**
      * Get Paper Size.
-     *
-     * @return int
      */
-    public function getPaperSize()
+    public function getPaperSize(): int
     {
         return $this->paperSize ?? self::$paperSizeDefault;
     }
@@ -291,7 +264,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function setPaperSize($paperSize)
+    public function setPaperSize(int $paperSize): static
     {
         $this->paperSize = $paperSize;
 
@@ -316,10 +289,8 @@ class PageSetup
 
     /**
      * Get Orientation.
-     *
-     * @return string
      */
-    public function getOrientation()
+    public function getOrientation(): string
     {
         return $this->orientation;
     }
@@ -331,7 +302,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function setOrientation($orientation)
+    public function setOrientation(string $orientation): static
     {
         if ($orientation === self::ORIENTATION_LANDSCAPE || $orientation === self::ORIENTATION_PORTRAIT || $orientation === self::ORIENTATION_DEFAULT) {
             $this->orientation = $orientation;
@@ -354,10 +325,8 @@ class PageSetup
 
     /**
      * Get Scale.
-     *
-     * @return null|int
      */
-    public function getScale()
+    public function getScale(): ?int
     {
         return $this->scale;
     }
@@ -367,12 +336,11 @@ class PageSetup
      * Print scaling. Valid values range from 10 to 400
      * This setting is overridden when fitToWidth and/or fitToHeight are in use.
      *
-     * @param null|int $scale
      * @param bool $update Update fitToPage so scaling applies rather than fitToHeight / fitToWidth
      *
      * @return $this
      */
-    public function setScale($scale, $update = true)
+    public function setScale(?int $scale, bool $update = true): static
     {
         // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
         // but it is apparently still able to handle any scale >= 0, where 0 results in 100
@@ -390,10 +358,8 @@ class PageSetup
 
     /**
      * Get Fit To Page.
-     *
-     * @return bool
      */
-    public function getFitToPage()
+    public function getFitToPage(): bool
     {
         return $this->fitToPage;
     }
@@ -401,11 +367,9 @@ class PageSetup
     /**
      * Set Fit To Page.
      *
-     * @param bool $fitToPage
-     *
      * @return $this
      */
-    public function setFitToPage($fitToPage)
+    public function setFitToPage(bool $fitToPage): static
     {
         $this->fitToPage = $fitToPage;
 
@@ -414,10 +378,8 @@ class PageSetup
 
     /**
      * Get Fit To Height.
-     *
-     * @return null|int
      */
-    public function getFitToHeight()
+    public function getFitToHeight(): ?int
     {
         return $this->fitToHeight;
     }
@@ -425,12 +387,11 @@ class PageSetup
     /**
      * Set Fit To Height.
      *
-     * @param null|int $fitToHeight
      * @param bool $update Update fitToPage so it applies rather than scaling
      *
      * @return $this
      */
-    public function setFitToHeight($fitToHeight, $update = true)
+    public function setFitToHeight(?int $fitToHeight, bool $update = true): static
     {
         $this->fitToHeight = $fitToHeight;
         if ($update) {
@@ -442,10 +403,8 @@ class PageSetup
 
     /**
      * Get Fit To Width.
-     *
-     * @return null|int
      */
-    public function getFitToWidth()
+    public function getFitToWidth(): ?int
     {
         return $this->fitToWidth;
     }
@@ -453,12 +412,11 @@ class PageSetup
     /**
      * Set Fit To Width.
      *
-     * @param null|int $value
      * @param bool $update Update fitToPage so it applies rather than scaling
      *
      * @return $this
      */
-    public function setFitToWidth($value, $update = true)
+    public function setFitToWidth(?int $value, bool $update = true): static
     {
         $this->fitToWidth = $value;
         if ($update) {
@@ -470,10 +428,8 @@ class PageSetup
 
     /**
      * Is Columns to repeat at left set?
-     *
-     * @return bool
      */
-    public function isColumnsToRepeatAtLeftSet()
+    public function isColumnsToRepeatAtLeftSet(): bool
     {
         if (!empty($this->columnsToRepeatAtLeft)) {
             if ($this->columnsToRepeatAtLeft[0] != '' && $this->columnsToRepeatAtLeft[1] != '') {
@@ -489,7 +445,7 @@ class PageSetup
      *
      * @return array Containing start column and end column, empty array if option unset
      */
-    public function getColumnsToRepeatAtLeft()
+    public function getColumnsToRepeatAtLeft(): array
     {
         return $this->columnsToRepeatAtLeft;
     }
@@ -501,7 +457,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function setColumnsToRepeatAtLeft(array $columnsToRepeatAtLeft)
+    public function setColumnsToRepeatAtLeft(array $columnsToRepeatAtLeft): static
     {
         $this->columnsToRepeatAtLeft = $columnsToRepeatAtLeft;
 
@@ -516,7 +472,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function setColumnsToRepeatAtLeftByStartAndEnd($start, $end)
+    public function setColumnsToRepeatAtLeftByStartAndEnd(string $start, string $end): static
     {
         $this->columnsToRepeatAtLeft = [$start, $end];
 
@@ -525,10 +481,8 @@ class PageSetup
 
     /**
      * Is Rows to repeat at top set?
-     *
-     * @return bool
      */
-    public function isRowsToRepeatAtTopSet()
+    public function isRowsToRepeatAtTopSet(): bool
     {
         if (!empty($this->rowsToRepeatAtTop)) {
             if ($this->rowsToRepeatAtTop[0] != 0 && $this->rowsToRepeatAtTop[1] != 0) {
@@ -544,7 +498,7 @@ class PageSetup
      *
      * @return array Containing start column and end column, empty array if option unset
      */
-    public function getRowsToRepeatAtTop()
+    public function getRowsToRepeatAtTop(): array
     {
         return $this->rowsToRepeatAtTop;
     }
@@ -556,7 +510,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function setRowsToRepeatAtTop(array $rowsToRepeatAtTop)
+    public function setRowsToRepeatAtTop(array $rowsToRepeatAtTop): static
     {
         $this->rowsToRepeatAtTop = $rowsToRepeatAtTop;
 
@@ -571,7 +525,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function setRowsToRepeatAtTopByStartAndEnd($start, $end)
+    public function setRowsToRepeatAtTopByStartAndEnd(int $start, int $end): static
     {
         $this->rowsToRepeatAtTop = [$start, $end];
 
@@ -580,10 +534,8 @@ class PageSetup
 
     /**
      * Get center page horizontally.
-     *
-     * @return bool
      */
-    public function getHorizontalCentered()
+    public function getHorizontalCentered(): bool
     {
         return $this->horizontalCentered;
     }
@@ -591,11 +543,9 @@ class PageSetup
     /**
      * Set center page horizontally.
      *
-     * @param bool $value
-     *
      * @return $this
      */
-    public function setHorizontalCentered($value)
+    public function setHorizontalCentered(bool $value): static
     {
         $this->horizontalCentered = $value;
 
@@ -604,10 +554,8 @@ class PageSetup
 
     /**
      * Get center page vertically.
-     *
-     * @return bool
      */
-    public function getVerticalCentered()
+    public function getVerticalCentered(): bool
     {
         return $this->verticalCentered;
     }
@@ -615,11 +563,9 @@ class PageSetup
     /**
      * Set center page vertically.
      *
-     * @param bool $value
-     *
      * @return $this
      */
-    public function setVerticalCentered($value)
+    public function setVerticalCentered(bool $value): static
     {
         $this->verticalCentered = $value;
 
@@ -633,13 +579,11 @@ class PageSetup
      *                            Default behaviour, or a index value of 0, will return all ranges as a comma-separated string
      *                            Otherwise, the specific range identified by the value of $index will be returned
      *                            Print areas are numbered from 1
-     *
-     * @return string
      */
-    public function getPrintArea($index = 0)
+    public function getPrintArea(int $index = 0): string
     {
         if ($index == 0) {
-            return $this->printArea;
+            return (string) $this->printArea;
         }
         $printAreas = explode(',', (string) $this->printArea);
         if (isset($printAreas[$index - 1])) {
@@ -656,10 +600,8 @@ class PageSetup
      *                            Default behaviour, or an index value of 0, will identify whether any print range is set
      *                            Otherwise, existence of the range identified by the value of $index will be returned
      *                            Print areas are numbered from 1
-     *
-     * @return bool
      */
-    public function isPrintAreaSet($index = 0)
+    public function isPrintAreaSet(int $index = 0): bool
     {
         if ($index == 0) {
             return $this->printArea !== null;
@@ -679,7 +621,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function clearPrintArea($index = 0)
+    public function clearPrintArea(int $index = 0): static
     {
         if ($index == 0) {
             $this->printArea = null;
@@ -697,7 +639,6 @@ class PageSetup
     /**
      * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'.
      *
-     * @param string $value
      * @param int $index Identifier for a specific print area range allowing several ranges to be set
      *                            When the method is "O"verwrite, then a positive integer index will overwrite that indexed
      *                                entry in the print areas list; a negative index value will identify which entry to
@@ -714,13 +655,13 @@ class PageSetup
      *
      * @return $this
      */
-    public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
+    public function setPrintArea(string $value, int $index = 0, string $method = self::SETPRINTRANGE_OVERWRITE): static
     {
-        if (strpos($value, '!') !== false) {
+        if (str_contains($value, '!')) {
             throw new PhpSpreadsheetException('Cell coordinate must not specify a worksheet.');
-        } elseif (strpos($value, ':') === false) {
+        } elseif (!str_contains($value, ':')) {
             throw new PhpSpreadsheetException('Cell coordinate must be a range of cells.');
-        } elseif (strpos($value, '$') !== false) {
+        } elseif (str_contains($value, '$')) {
             throw new PhpSpreadsheetException('Cell coordinate must not be absolute.');
         }
         $value = strtoupper($value);
@@ -766,7 +707,6 @@ class PageSetup
     /**
      * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas.
      *
-     * @param string $value
      * @param int $index Identifier for a specific print area range allowing several ranges to be set
      *                            A positive index will insert after that indexed entry in the print areas list, while a
      *                                negative index will insert before the indexed entry.
@@ -776,7 +716,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function addPrintArea($value, $index = -1)
+    public function addPrintArea(string $value, int $index = -1): static
     {
         return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
     }
@@ -804,7 +744,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
+    public function setPrintAreaByColumnAndRow(int $column1, int $row1, int $column2, int $row2, int $index = 0, string $method = self::SETPRINTRANGE_OVERWRITE): static
     {
         return $this->setPrintArea(
             Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
@@ -829,7 +769,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
+    public function addPrintAreaByColumnAndRow(int $column1, int $row1, int $column2, int $row2, int $index = -1): static
     {
         return $this->setPrintArea(
             Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
@@ -840,10 +780,8 @@ class PageSetup
 
     /**
      * Get first page number.
-     *
-     * @return ?int
      */
-    public function getFirstPageNumber()
+    public function getFirstPageNumber(): ?int
     {
         return $this->firstPageNumber;
     }
@@ -851,11 +789,9 @@ class PageSetup
     /**
      * Set first page number.
      *
-     * @param ?int $value
-     *
      * @return $this
      */
-    public function setFirstPageNumber($value)
+    public function setFirstPageNumber(?int $value): static
     {
         $this->firstPageNumber = $value;
 
@@ -867,7 +803,7 @@ class PageSetup
      *
      * @return $this
      */
-    public function resetFirstPageNumber()
+    public function resetFirstPageNumber(): static
     {
         return $this->setFirstPageNumber(null);
     }
@@ -885,19 +821,4 @@ class PageSetup
 
         return $this;
     }
-
-    /**
-     * Implement PHP __clone to create a deep clone, not just a shallow copy.
-     */
-    public function __clone()
-    {
-        $vars = get_object_vars($this);
-        foreach ($vars as $key => $value) {
-            if (is_object($value)) {
-                $this->$key = clone $value;
-            } else {
-                $this->$key = $value;
-            }
-        }
-    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Pane.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Pane.php
new file mode 100644
index 0000000..cbf523d
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Pane.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class Pane
+{
+    private string $sqref;
+
+    private string $activeCell;
+
+    private string $position;
+
+    public function __construct(string $position, string $sqref = '', string $activeCell = '')
+    {
+        $this->sqref = $sqref;
+        $this->activeCell = $activeCell;
+        $this->position = $position;
+    }
+
+    public function getPosition(): string
+    {
+        return $this->position;
+    }
+
+    public function getSqref(): string
+    {
+        return $this->sqref;
+    }
+
+    public function setSqref(string $sqref): self
+    {
+        $this->sqref = $sqref;
+
+        return $this;
+    }
+
+    public function getActiveCell(): string
+    {
+        return $this->activeCell;
+    }
+
+    public function setActiveCell(string $activeCell): self
+    {
+        $this->activeCell = $activeCell;
+
+        return $this;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ProtectedRange.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ProtectedRange.php
new file mode 100644
index 0000000..bd41976
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/ProtectedRange.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Worksheet;
+
+class ProtectedRange
+{
+    private string $name = '';
+
+    private string $password = '';
+
+    private string $sqref;
+
+    private string $securityDescriptor = '';
+
+    /**
+     * No setters aside from constructor.
+     */
+    public function __construct(string $sqref, string $password = '', string $name = '', string $securityDescriptor = '')
+    {
+        $this->sqref = $sqref;
+        $this->name = $name;
+        $this->password = $password;
+        $this->securityDescriptor = $securityDescriptor;
+    }
+
+    public function getSqref(): string
+    {
+        return $this->sqref;
+    }
+
+    public function getName(): string
+    {
+        return $this->name ?: ('p' . md5($this->sqref));
+    }
+
+    public function getPassword(): string
+    {
+        return $this->password;
+    }
+
+    public function getSecurityDescriptor(): string
+    {
+        return $this->securityDescriptor;
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
index 063a5cc..520994d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Protection.php
@@ -19,143 +19,103 @@ class Protection
 
     /**
      * Autofilters are locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $autoFilter;
+    private ?bool $autoFilter = null;
 
     /**
      * Deleting columns is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $deleteColumns;
+    private ?bool $deleteColumns = null;
 
     /**
      * Deleting rows is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $deleteRows;
+    private ?bool $deleteRows = null;
 
     /**
      * Formatting cells is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $formatCells;
+    private ?bool $formatCells = null;
 
     /**
      * Formatting columns is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $formatColumns;
+    private ?bool $formatColumns = null;
 
     /**
      * Formatting rows is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $formatRows;
+    private ?bool $formatRows = null;
 
     /**
      * Inserting columns is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $insertColumns;
+    private ?bool $insertColumns = null;
 
     /**
      * Inserting hyperlinks is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $insertHyperlinks;
+    private ?bool $insertHyperlinks = null;
 
     /**
      * Inserting rows is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $insertRows;
+    private ?bool $insertRows = null;
 
     /**
      * Objects are locked when sheet is protected, default false.
-     *
-     * @var ?bool
      */
-    private $objects;
+    private ?bool $objects = null;
 
     /**
      * Pivot tables are locked when the sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $pivotTables;
+    private ?bool $pivotTables = null;
 
     /**
      * Scenarios are locked when sheet is protected, default false.
-     *
-     * @var ?bool
      */
-    private $scenarios;
+    private ?bool $scenarios = null;
 
     /**
      * Selection of locked cells is locked when sheet is protected, default false.
-     *
-     * @var ?bool
      */
-    private $selectLockedCells;
+    private ?bool $selectLockedCells = null;
 
     /**
      * Selection of unlocked cells is locked when sheet is protected, default false.
-     *
-     * @var ?bool
      */
-    private $selectUnlockedCells;
+    private ?bool $selectUnlockedCells = null;
 
     /**
      * Sheet is locked when sheet is protected, default false.
-     *
-     * @var ?bool
      */
-    private $sheet;
+    private ?bool $sheet = null;
 
     /**
      * Sorting is locked when sheet is protected, default true.
-     *
-     * @var ?bool
      */
-    private $sort;
+    private ?bool $sort = null;
 
     /**
      * Hashed password.
-     *
-     * @var string
      */
-    private $password = '';
+    private string $password = '';
 
     /**
      * Algorithm name.
-     *
-     * @var string
      */
-    private $algorithm = '';
+    private string $algorithm = '';
 
     /**
      * Salt value.
-     *
-     * @var string
      */
-    private $salt = '';
+    private string $salt = '';
 
     /**
      * Spin count.
-     *
-     * @var int
      */
-    private $spinCount = 10000;
+    private int $spinCount = 10000;
 
     /**
      * Create a new Protection.
@@ -170,23 +130,23 @@ class Protection
     public function isProtectionEnabled(): bool
     {
         return
-            $this->password !== '' ||
-            isset($this->sheet) ||
-            isset($this->objects) ||
-            isset($this->scenarios) ||
-            isset($this->formatCells) ||
-            isset($this->formatColumns) ||
-            isset($this->formatRows) ||
-            isset($this->insertColumns) ||
-            isset($this->insertRows) ||
-            isset($this->insertHyperlinks) ||
-            isset($this->deleteColumns) ||
-            isset($this->deleteRows) ||
-            isset($this->selectLockedCells) ||
-            isset($this->sort) ||
-            isset($this->autoFilter) ||
-            isset($this->pivotTables) ||
-            isset($this->selectUnlockedCells);
+            $this->password !== ''
+            || isset($this->sheet)
+            || isset($this->objects)
+            || isset($this->scenarios)
+            || isset($this->formatCells)
+            || isset($this->formatColumns)
+            || isset($this->formatRows)
+            || isset($this->insertColumns)
+            || isset($this->insertRows)
+            || isset($this->insertHyperlinks)
+            || isset($this->deleteColumns)
+            || isset($this->deleteRows)
+            || isset($this->selectLockedCells)
+            || isset($this->sort)
+            || isset($this->autoFilter)
+            || isset($this->pivotTables)
+            || isset($this->selectUnlockedCells);
     }
 
     public function getSheet(): ?bool
@@ -383,10 +343,8 @@ class Protection
 
     /**
      * Get hashed password.
-     *
-     * @return string
      */
-    public function getPassword()
+    public function getPassword(): string
     {
         return $this->password;
     }
@@ -394,12 +352,11 @@ class Protection
     /**
      * Set Password.
      *
-     * @param string $password
      * @param bool $alreadyHashed If the password has already been hashed, set this to true
      *
      * @return $this
      */
-    public function setPassword($password, $alreadyHashed = false)
+    public function setPassword(string $password, bool $alreadyHashed = false): static
     {
         if (!$alreadyHashed) {
             $salt = $this->generateSalt();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php
index 223a07c..49d13da 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Row.php
@@ -4,26 +4,17 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet;
 
 class Row
 {
-    /**
-     * \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet.
-     *
-     * @var Worksheet
-     */
-    private $worksheet;
+    private Worksheet $worksheet;
 
     /**
      * Row index.
-     *
-     * @var int
      */
-    private $rowIndex = 0;
+    private int $rowIndex;
 
     /**
      * Create a new row.
-     *
-     * @param int $rowIndex
      */
-    public function __construct(Worksheet $worksheet, $rowIndex = 1)
+    public function __construct(Worksheet $worksheet, int $rowIndex = 1)
     {
         // Set parent and row index
         $this->worksheet = $worksheet;
@@ -35,7 +26,7 @@ class Row
      */
     public function __destruct()
     {
-        $this->worksheet = null; // @phpstan-ignore-line
+        unset($this->worksheet);
     }
 
     /**
@@ -50,13 +41,22 @@ class Row
      * Get cell iterator.
      *
      * @param string $startColumn The column address at which to start iterating
-     * @param string $endColumn Optionally, the column address at which to stop iterating
-     *
-     * @return RowCellIterator
+     * @param ?string $endColumn Optionally, the column address at which to stop iterating
      */
-    public function getCellIterator($startColumn = 'A', $endColumn = null)
+    public function getCellIterator(string $startColumn = 'A', ?string $endColumn = null, bool $iterateOnlyExistingCells = false): RowCellIterator
     {
-        return new RowCellIterator($this->worksheet, $this->rowIndex, $startColumn, $endColumn);
+        return new RowCellIterator($this->worksheet, $this->rowIndex, $startColumn, $endColumn, $iterateOnlyExistingCells);
+    }
+
+    /**
+     * Get column iterator. Synonym for getCellIterator().
+     *
+     * @param string $startColumn The column address at which to start iterating
+     * @param ?string $endColumn Optionally, the column address at which to stop iterating
+     */
+    public function getColumnIterator(string $startColumn = 'A', ?string $endColumn = null, bool $iterateOnlyExistingCells = false): RowCellIterator
+    {
+        return $this->getCellIterator($startColumn, $endColumn, $iterateOnlyExistingCells);
     }
 
     /**
@@ -75,16 +75,17 @@ class Row
      *              Possible Flag Values are:
      *                  CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
      *                  CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
+     * @param string $startColumn The column address at which to start checking if cells are empty
+     * @param ?string $endColumn Optionally, the column address at which to stop checking if cells are empty
      */
-    public function isEmpty(int $definitionOfEmptyFlags = 0): bool
+    public function isEmpty(int $definitionOfEmptyFlags = 0, string $startColumn = 'A', ?string $endColumn = null): bool
     {
         $nullValueCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
         $emptyStringCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
 
-        $cellIterator = $this->getCellIterator();
+        $cellIterator = $this->getCellIterator($startColumn, $endColumn);
         $cellIterator->setIterateOnlyExistingCells(true);
         foreach ($cellIterator as $cell) {
-            /** @scrutinizer ignore-call */
             $value = $cell->getValue();
             if ($value === null && $nullValueCellIsEmpty === true) {
                 continue;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
index 5af0507..4d4901c 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
@@ -13,31 +13,23 @@ class RowCellIterator extends CellIterator
 {
     /**
      * Current iterator position.
-     *
-     * @var int
      */
-    private $currentColumnIndex;
+    private int $currentColumnIndex;
 
     /**
      * Row index.
-     *
-     * @var int
      */
-    private $rowIndex = 1;
+    private int $rowIndex;
 
     /**
      * Start position.
-     *
-     * @var int
      */
-    private $startColumnIndex = 1;
+    private int $startColumnIndex = 1;
 
     /**
      * End position.
-     *
-     * @var int
      */
-    private $endColumnIndex = 1;
+    private int $endColumnIndex = 1;
 
     /**
      * Create a new column iterator.
@@ -45,9 +37,9 @@ class RowCellIterator extends CellIterator
      * @param Worksheet $worksheet The worksheet to iterate over
      * @param int $rowIndex The row that we want to iterate
      * @param string $startColumn The column address at which to start iterating
-     * @param string $endColumn Optionally, the column address at which to stop iterating
+     * @param ?string $endColumn Optionally, the column address at which to stop iterating
      */
-    public function __construct(Worksheet $worksheet, $rowIndex = 1, $startColumn = 'A', $endColumn = null)
+    public function __construct(Worksheet $worksheet, int $rowIndex = 1, string $startColumn = 'A', ?string $endColumn = null, bool $iterateOnlyExistingCells = false)
     {
         // Set subject and row index
         $this->worksheet = $worksheet;
@@ -55,6 +47,7 @@ class RowCellIterator extends CellIterator
         $this->rowIndex = $rowIndex;
         $this->resetEnd($endColumn);
         $this->resetStart($startColumn);
+        $this->setIterateOnlyExistingCells($iterateOnlyExistingCells);
     }
 
     /**
@@ -64,7 +57,7 @@ class RowCellIterator extends CellIterator
      *
      * @return $this
      */
-    public function resetStart(string $startColumn = 'A')
+    public function resetStart(string $startColumn = 'A'): static
     {
         $this->startColumnIndex = Coordinate::columnIndexFromString($startColumn);
         $this->adjustForExistingOnlyRange();
@@ -76,11 +69,11 @@ class RowCellIterator extends CellIterator
     /**
      * (Re)Set the end column.
      *
-     * @param string $endColumn The column address at which to stop iterating
+     * @param ?string $endColumn The column address at which to stop iterating
      *
      * @return $this
      */
-    public function resetEnd($endColumn = null)
+    public function resetEnd(?string $endColumn = null): static
     {
         $endColumn = $endColumn ?: $this->worksheet->getHighestColumn();
         $this->endColumnIndex = Coordinate::columnIndexFromString($endColumn);
@@ -96,7 +89,7 @@ class RowCellIterator extends CellIterator
      *
      * @return $this
      */
-    public function seek(string $column = 'A')
+    public function seek(string $column = 'A'): static
     {
         $columnId = Coordinate::columnIndexFromString($column);
         if ($this->onlyExistingCells && !($this->cellCollection->has($column . $this->rowIndex))) {
@@ -127,7 +120,11 @@ class RowCellIterator extends CellIterator
 
         return $this->cellCollection->has($cellAddress)
             ? $this->cellCollection->get($cellAddress)
-            : $this->worksheet->createNewCell($cellAddress);
+            : (
+                $this->ifNotExists === self::IF_NOT_EXISTS_CREATE_NEW
+                ? $this->worksheet->createNewCell($cellAddress)
+                : null
+            );
     }
 
     /**
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
index acaafac..e94a63b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowDimension.php
@@ -8,33 +8,27 @@ class RowDimension extends Dimension
 {
     /**
      * Row index.
-     *
-     * @var int
      */
-    private $rowIndex;
+    private ?int $rowIndex;
 
     /**
      * Row height (in pt).
      *
      * When this is set to a negative value, the row height should be ignored by IWriter
-     *
-     * @var float
      */
-    private $height = -1;
+    private float $height = -1;
 
     /**
      * ZeroHeight for Row?
-     *
-     * @var bool
      */
-    private $zeroHeight = false;
+    private bool $zeroHeight = false;
 
     /**
      * Create a new RowDimension.
      *
-     * @param int $index Numeric row index
+     * @param ?int $index Numeric row index
      */
-    public function __construct($index = 0)
+    public function __construct(?int $index = 0)
     {
         // Initialise values
         $this->rowIndex = $index;
@@ -46,7 +40,7 @@ class RowDimension extends Dimension
     /**
      * Get Row Index.
      */
-    public function getRowIndex(): int
+    public function getRowIndex(): ?int
     {
         return $this->rowIndex;
     }
@@ -56,7 +50,7 @@ class RowDimension extends Dimension
      *
      * @return $this
      */
-    public function setRowIndex(int $index)
+    public function setRowIndex(int $index): static
     {
         $this->rowIndex = $index;
 
@@ -68,10 +62,8 @@ class RowDimension extends Dimension
      * By default, this will be in points; but this method also accepts an optional unit of measure
      *    argument, and will convert the value from points to the specified UoM.
      *    A value of -1 tells Excel to display this column in its default height.
-     *
-     * @return float
      */
-    public function getRowHeight(?string $unitOfMeasure = null)
+    public function getRowHeight(?string $unitOfMeasure = null): float
     {
         return ($unitOfMeasure === null || $this->height < 0)
             ? $this->height
@@ -87,7 +79,7 @@ class RowDimension extends Dimension
      *
      * @return $this
      */
-    public function setRowHeight($height, ?string $unitOfMeasure = null)
+    public function setRowHeight(float $height, ?string $unitOfMeasure = null): static
     {
         $this->height = ($unitOfMeasure === null || $height < 0)
             ? $height
@@ -109,7 +101,7 @@ class RowDimension extends Dimension
      *
      * @return $this
      */
-    public function setZeroHeight(bool $zeroHeight)
+    public function setZeroHeight(bool $zeroHeight): static
     {
         $this->zeroHeight = $zeroHeight;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
index 84f376c..9d42ee9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowIterator.php
@@ -12,40 +12,32 @@ class RowIterator implements NativeIterator
 {
     /**
      * Worksheet to iterate.
-     *
-     * @var Worksheet
      */
-    private $subject;
+    private Worksheet $subject;
 
     /**
      * Current iterator position.
-     *
-     * @var int
      */
-    private $position = 1;
+    private int $position = 1;
 
     /**
      * Start position.
-     *
-     * @var int
      */
-    private $startRow = 1;
+    private int $startRow = 1;
 
     /**
      * End position.
-     *
-     * @var int
      */
-    private $endRow = 1;
+    private int $endRow = 1;
 
     /**
      * Create a new row iterator.
      *
      * @param Worksheet $subject The worksheet to iterate over
      * @param int $startRow The row number at which to start iterating
-     * @param int $endRow Optionally, the row number at which to stop iterating
+     * @param ?int $endRow Optionally, the row number at which to stop iterating
      */
-    public function __construct(Worksheet $subject, $startRow = 1, $endRow = null)
+    public function __construct(Worksheet $subject, int $startRow = 1, ?int $endRow = null)
     {
         // Set subject
         $this->subject = $subject;
@@ -53,6 +45,11 @@ class RowIterator implements NativeIterator
         $this->resetStart($startRow);
     }
 
+    public function __destruct()
+    {
+        unset($this->subject);
+    }
+
     /**
      * (Re)Set the start row and the current row pointer.
      *
@@ -60,7 +57,7 @@ class RowIterator implements NativeIterator
      *
      * @return $this
      */
-    public function resetStart(int $startRow = 1)
+    public function resetStart(int $startRow = 1): static
     {
         if ($startRow > $this->subject->getHighestRow()) {
             throw new PhpSpreadsheetException(
@@ -80,11 +77,11 @@ class RowIterator implements NativeIterator
     /**
      * (Re)Set the end row.
      *
-     * @param int $endRow The row number at which to stop iterating
+     * @param ?int $endRow The row number at which to stop iterating
      *
      * @return $this
      */
-    public function resetEnd($endRow = null)
+    public function resetEnd(?int $endRow = null): static
     {
         $this->endRow = $endRow ?: $this->subject->getHighestRow();
 
@@ -98,7 +95,7 @@ class RowIterator implements NativeIterator
      *
      * @return $this
      */
-    public function seek(int $row = 1)
+    public function seek(int $row = 1): static
     {
         if (($row < $this->startRow) || ($row > $this->endRow)) {
             throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
index 13464c9..dca2bf5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/SheetView.php
@@ -21,38 +21,44 @@ class SheetView
      * ZoomScale.
      *
      * Valid values range from 10 to 400.
-     *
-     * @var ?int
      */
-    private $zoomScale = 100;
+    private ?int $zoomScale = 100;
 
     /**
      * ZoomScaleNormal.
      *
      * Valid values range from 10 to 400.
-     *
-     * @var ?int
      */
-    private $zoomScaleNormal = 100;
+    private ?int $zoomScaleNormal = 100;
+
+    /**
+     * ZoomScalePageLayoutView.
+     *
+     * Valid values range from 10 to 400.
+     */
+    private int $zoomScalePageLayoutView = 100;
+
+    /**
+     * ZoomScaleSheetLayoutView.
+     *
+     * Valid values range from 10 to 400.
+     */
+    private int $zoomScaleSheetLayoutView = 100;
 
     /**
      * ShowZeros.
      *
      * If true, "null" values from a calculation will be shown as "0". This is the default Excel behaviour and can be changed
      * with the advanced worksheet option "Show a zero in cells that have zero value"
-     *
-     * @var bool
      */
-    private $showZeros = true;
+    private bool $showZeros = true;
 
     /**
      * View.
      *
      * Valid values range from 10 to 400.
-     *
-     * @var string
      */
-    private $sheetviewType = self::SHEETVIEW_NORMAL;
+    private string $sheetviewType = self::SHEETVIEW_NORMAL;
 
     /**
      * Create a new SheetView.
@@ -63,10 +69,8 @@ class SheetView
 
     /**
      * Get ZoomScale.
-     *
-     * @return ?int
      */
-    public function getZoomScale()
+    public function getZoomScale(): ?int
     {
         return $this->zoomScale;
     }
@@ -75,11 +79,9 @@ class SheetView
      * Set ZoomScale.
      * Valid values range from 10 to 400.
      *
-     * @param ?int $zoomScale
-     *
      * @return $this
      */
-    public function setZoomScale($zoomScale)
+    public function setZoomScale(?int $zoomScale): static
     {
         // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
         // but it is apparently still able to handle any scale >= 1
@@ -94,10 +96,8 @@ class SheetView
 
     /**
      * Get ZoomScaleNormal.
-     *
-     * @return ?int
      */
-    public function getZoomScaleNormal()
+    public function getZoomScaleNormal(): ?int
     {
         return $this->zoomScaleNormal;
     }
@@ -106,11 +106,9 @@ class SheetView
      * Set ZoomScale.
      * Valid values range from 10 to 400.
      *
-     * @param ?int $zoomScaleNormal
-     *
      * @return $this
      */
-    public function setZoomScaleNormal($zoomScaleNormal)
+    public function setZoomScaleNormal(?int $zoomScaleNormal): static
     {
         if ($zoomScaleNormal === null || $zoomScaleNormal >= 1) {
             $this->zoomScaleNormal = $zoomScaleNormal;
@@ -121,30 +119,55 @@ class SheetView
         return $this;
     }
 
+    public function getZoomScalePageLayoutView(): int
+    {
+        return $this->zoomScalePageLayoutView;
+    }
+
+    public function setZoomScalePageLayoutView(int $zoomScalePageLayoutView): static
+    {
+        if ($zoomScalePageLayoutView >= 1) {
+            $this->zoomScalePageLayoutView = $zoomScalePageLayoutView;
+        } else {
+            throw new PhpSpreadsheetException('Scale must be greater than or equal to 1.');
+        }
+
+        return $this;
+    }
+
+    public function getZoomScaleSheetLayoutView(): int
+    {
+        return $this->zoomScaleSheetLayoutView;
+    }
+
+    public function setZoomScaleSheetLayoutView(int $zoomScaleSheetLayoutView): static
+    {
+        if ($zoomScaleSheetLayoutView >= 1) {
+            $this->zoomScaleSheetLayoutView = $zoomScaleSheetLayoutView;
+        } else {
+            throw new PhpSpreadsheetException('Scale must be greater than or equal to 1.');
+        }
+
+        return $this;
+    }
+
     /**
      * Set ShowZeroes setting.
-     *
-     * @param bool $showZeros
      */
-    public function setShowZeros($showZeros): void
+    public function setShowZeros(bool $showZeros): void
     {
         $this->showZeros = $showZeros;
     }
 
-    /**
-     * @return bool
-     */
-    public function getShowZeros()
+    public function getShowZeros(): bool
     {
         return $this->showZeros;
     }
 
     /**
      * Get View.
-     *
-     * @return string
      */
-    public function getView()
+    public function getView(): string
     {
         return $this->sheetviewType;
     }
@@ -157,11 +180,9 @@ class SheetView
      *        'pageLayout'        self::SHEETVIEW_PAGE_LAYOUT
      *        'pageBreakPreview'  self::SHEETVIEW_PAGE_BREAK_PREVIEW
      *
-     * @param ?string $sheetViewType
-     *
      * @return $this
      */
-    public function setView($sheetViewType)
+    public function setView(?string $sheetViewType): static
     {
         // MS Excel 2007 allows setting the view to 'normal', 'pageLayout' or 'pageBreakPreview' via the user interface
         if ($sheetViewType === null) {
@@ -175,19 +196,4 @@ class SheetView
 
         return $this;
     }
-
-    /**
-     * Implement PHP __clone to create a deep clone, not just a shallow copy.
-     */
-    public function __clone()
-    {
-        $vars = get_object_vars($this);
-        foreach ($vars as $key => $value) {
-            if (is_object($value)) {
-                $this->$key = clone $value;
-            } else {
-                $this->$key = $value;
-            }
-        }
-    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table.php
index 4c25259..234014a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table.php
@@ -3,88 +3,74 @@
 namespace PhpOffice\PhpSpreadsheet\Worksheet;
 
 use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
+use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle;
+use Stringable;
 
-class Table
+class Table implements Stringable
 {
     /**
      * Table Name.
-     *
-     * @var string
      */
-    private $name;
+    private string $name;
 
     /**
      * Show Header Row.
-     *
-     * @var bool
      */
-    private $showHeaderRow = true;
+    private bool $showHeaderRow = true;
 
     /**
      * Show Totals Row.
-     *
-     * @var bool
      */
-    private $showTotalsRow = false;
+    private bool $showTotalsRow = false;
 
     /**
      * Table Range.
-     *
-     * @var string
      */
-    private $range = '';
+    private string $range = '';
 
     /**
      * Table Worksheet.
-     *
-     * @var null|Worksheet
      */
-    private $workSheet;
+    private ?Worksheet $workSheet = null;
 
     /**
      * Table allow filter.
-     *
-     * @var bool
      */
-    private $allowFilter = true;
+    private bool $allowFilter = true;
 
     /**
      * Table Column.
      *
      * @var Table\Column[]
      */
-    private $columns = [];
+    private array $columns = [];
 
     /**
      * Table Style.
-     *
-     * @var TableStyle
      */
-    private $style;
+    private TableStyle $style;
 
     /**
      * Table AutoFilter.
-     *
-     * @var AutoFilter
      */
-    private $autoFilter;
+    private AutoFilter $autoFilter;
 
     /**
      * Create a new Table.
      *
-     * @param AddressRange|array<int>|string $range
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
      *            A simple string containing a Cell range like 'A1:E10' is permitted
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or an AddressRange object.
      * @param string $name (e.g. Table1)
      */
-    public function __construct($range = '', string $name = '')
+    public function __construct(AddressRange|string|array $range = '', string $name = '')
     {
         $this->style = new TableStyle();
         $this->autoFilter = new AutoFilter($range);
@@ -92,6 +78,14 @@ class Table
         $this->setName($name);
     }
 
+    /**
+     * Code to execute when this table is unset().
+     */
+    public function __destruct()
+    {
+        $this->workSheet = null;
+    }
+
     /**
      * Get Table name.
      */
@@ -118,8 +112,8 @@ class Table
             }
             // Check for A1 or R1C1 cell reference notation
             if (
-                preg_match(Coordinate::A1_COORDINATE_REGEX, $name) ||
-                preg_match('/^R\[?\-?[0-9]*\]?C\[?\-?[0-9]*\]?$/i', $name)
+                preg_match(Coordinate::A1_COORDINATE_REGEX, $name)
+                || preg_match('/^R\[?\-?[0-9]*\]?C\[?\-?[0-9]*\]?$/i', $name)
             ) {
                 throw new PhpSpreadsheetException('The table name can\'t be the same as a cell reference');
             }
@@ -148,7 +142,7 @@ class Table
         $tableName = StringHelper::strToLower($name);
 
         if ($worksheet !== null && StringHelper::strToLower($this->name) !== $name) {
-            $spreadsheet = $worksheet->getParent();
+            $spreadsheet = $worksheet->getParentOrThrow();
 
             foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
                 foreach ($sheet->getTableCollection() as $table) {
@@ -162,7 +156,7 @@ class Table
 
     private function updateStructuredReferences(string $name): void
     {
-        if ($this->workSheet === null || $this->name === null || $this->name === '') {
+        if (!$this->workSheet || !$this->name) {
             return;
         }
 
@@ -170,7 +164,7 @@ class Table
         if (StringHelper::strToLower($this->name) !== StringHelper::strToLower($name)) {
             // We need to check all formula cells that might contain fully-qualified Structured References
             //    that refer to this table, and update those formulae to reference the new table name
-            $spreadsheet = $this->workSheet->getParent();
+            $spreadsheet = $this->workSheet->getParentOrThrow();
             foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
                 $this->updateStructuredReferencesInCells($sheet, $name);
             }
@@ -180,12 +174,12 @@ class Table
 
     private function updateStructuredReferencesInCells(Worksheet $worksheet, string $newName): void
     {
-        $pattern = '/' . preg_quote($this->name) . '\[/mui';
+        $pattern = '/' . preg_quote($this->name, '/') . '\[/mui';
 
         foreach ($worksheet->getCoordinates(false) as $coordinate) {
             $cell = $worksheet->getCell($coordinate);
             if ($cell->getDataType() === DataType::TYPE_FORMULA) {
-                $formula = $cell->getValue();
+                $formula = $cell->getValueString();
                 if (preg_match($pattern, $formula) === 1) {
                     $formula = preg_replace($pattern, "{$newName}[", $formula);
                     $cell->setValueExplicit($formula, DataType::TYPE_FORMULA);
@@ -196,13 +190,13 @@ class Table
 
     private function updateStructuredReferencesInNamedFormulae(Spreadsheet $spreadsheet, string $newName): void
     {
-        $pattern = '/' . preg_quote($this->name) . '\[/mui';
+        $pattern = '/' . preg_quote($this->name, '/') . '\[/mui';
 
         foreach ($spreadsheet->getNamedFormulae() as $namedFormula) {
             $formula = $namedFormula->getValue();
             if (preg_match($pattern, $formula) === 1) {
-                $formula = preg_replace($pattern, "{$newName}[", $formula);
-                $namedFormula->setValue($formula); // @phpstan-ignore-line
+                $formula = preg_replace($pattern, "{$newName}[", $formula) ?? '';
+                $namedFormula->setValue($formula);
             }
         }
     }
@@ -274,12 +268,12 @@ class Table
     /**
      * Set Table Cell Range.
      *
-     * @param AddressRange|array<int>|string $range
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
      *            A simple string containing a Cell range like 'A1:E10' is permitted
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or an AddressRange object.
      */
-    public function setRange($range = ''): self
+    public function setRange(AddressRange|string|array $range = ''): self
     {
         // extract coordinate
         if ($range !== '') {
@@ -293,13 +287,13 @@ class Table
             return $this;
         }
 
-        if (strpos($range, ':') === false) {
+        if (!str_contains($range, ':')) {
             throw new PhpSpreadsheetException('Table must be set on a range of cells.');
         }
 
         [$width, $height] = Coordinate::rangeDimension($range);
-        if ($width < 1 || $height < 2) {
-            throw new PhpSpreadsheetException('The table range must be at least 1 column and 2 rows');
+        if ($width < 1 || $height < 1) {
+            throw new PhpSpreadsheetException('The table range must be at least 1 column and row');
         }
 
         $this->range = $range;
@@ -347,7 +341,7 @@ class Table
     public function setWorksheet(?Worksheet $worksheet = null): self
     {
         if ($this->name !== '' && $worksheet !== null) {
-            $spreadsheet = $worksheet->getParent();
+            $spreadsheet = $worksheet->getParentOrThrow();
             $tableName = StringHelper::strToUpper($this->name);
 
             foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
@@ -404,7 +398,7 @@ class Table
      *
      * @return int The offset of the specified column within the table range
      */
-    public function getColumnOffset($column): int
+    public function getColumnOffset(string $column): int
     {
         return $this->isColumnInRange($column);
     }
@@ -414,7 +408,7 @@ class Table
      *
      * @param string $column Column name (e.g. A)
      */
-    public function getColumn($column): Table\Column
+    public function getColumn(string $column): Table\Column
     {
         $this->isColumnInRange($column);
 
@@ -430,7 +424,7 @@ class Table
      *
      * @param int $columnOffset Column offset within range (starting from 0)
      */
-    public function getColumnByOffset($columnOffset): Table\Column
+    public function getColumnByOffset(int $columnOffset): Table\Column
     {
         [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
         $pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $columnOffset);
@@ -444,7 +438,7 @@ class Table
      * @param string|Table\Column $columnObjectOrString
      *            A simple string containing a Column ID like 'A' is permitted
      */
-    public function setColumn($columnObjectOrString): self
+    public function setColumn(string|Table\Column $columnObjectOrString): self
     {
         if ((is_string($columnObjectOrString)) && (!empty($columnObjectOrString))) {
             $column = $columnObjectOrString;
@@ -471,7 +465,7 @@ class Table
      *
      * @param string $column Column name (e.g. A)
      */
-    public function clearColumn($column): self
+    public function clearColumn(string $column): self
     {
         $this->isColumnInRange($column);
 
@@ -492,7 +486,7 @@ class Table
      * @param string $fromColumn Column name (e.g. A)
      * @param string $toColumn Column name (e.g. B)
      */
-    public function shiftColumn($fromColumn, $toColumn): self
+    public function shiftColumn(string $fromColumn, string $toColumn): self
     {
         $fromColumn = strtoupper($fromColumn);
         $toColumn = strtoupper($toColumn);
@@ -513,7 +507,7 @@ class Table
     /**
      * Get table Style.
      */
-    public function getStyle(): Table\TableStyle
+    public function getStyle(): TableStyle
     {
         return $this->style;
     }
@@ -578,7 +572,7 @@ class Table
      * toString method replicates previous behavior by returning the range if object is
      * referenced as a property of its worksheet.
      */
-    public function __toString()
+    public function __toString(): string
     {
         return (string) $this->range;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/Column.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/Column.php
index 9fe19ec..257e3e3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/Column.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/Column.php
@@ -12,60 +12,46 @@ class Column
 {
     /**
      * Table Column Index.
-     *
-     * @var string
      */
-    private $columnIndex = '';
+    private string $columnIndex;
 
     /**
      * Show Filter Button.
-     *
-     * @var bool
      */
-    private $showFilterButton = true;
+    private bool $showFilterButton = true;
 
     /**
      * Total Row Label.
-     *
-     * @var string
      */
-    private $totalsRowLabel;
+    private ?string $totalsRowLabel = null;
 
     /**
      * Total Row Function.
-     *
-     * @var string
      */
-    private $totalsRowFunction;
+    private ?string $totalsRowFunction = null;
 
     /**
      * Total Row Formula.
-     *
-     * @var string
      */
-    private $totalsRowFormula;
+    private ?string $totalsRowFormula = null;
 
     /**
      * Column Formula.
-     *
-     * @var string
      */
-    private $columnFormula;
+    private ?string $columnFormula = null;
 
     /**
      * Table.
-     *
-     * @var null|Table
      */
-    private $table;
+    private ?Table $table;
 
     /**
      * Create a new Column.
      *
      * @param string $column Column (e.g. A)
-     * @param Table $table Table for this column
+     * @param ?Table $table Table for this column
      */
-    public function __construct($column, ?Table $table = null)
+    public function __construct(string $column, ?Table $table = null)
     {
         $this->columnIndex = $column;
         $this->table = $table;
@@ -84,7 +70,7 @@ class Column
      *
      * @param string $column Column (e.g. A)
      */
-    public function setColumnIndex($column): self
+    public function setColumnIndex(string $column): self
     {
         // Uppercase coordinate
         $column = strtoupper($column);
@@ -205,9 +191,9 @@ class Column
         return $this;
     }
 
-    public static function updateStructuredReferences(?Worksheet $workSheet, ?string $oldTitle, string $newTitle): void
+    public static function updateStructuredReferences(?Worksheet $workSheet, ?string $oldTitle, ?string $newTitle): void
     {
-        if ($workSheet === null || $oldTitle === null || $oldTitle === '') {
+        if ($workSheet === null || $oldTitle === null || $oldTitle === '' || $newTitle === null) {
             return;
         }
 
@@ -215,7 +201,7 @@ class Column
         if (StringHelper::strToLower($oldTitle) !== StringHelper::strToLower($newTitle)) {
             // We need to check all formula cells that might contain Structured References that refer
             //    to this column, and update those formulae to reference the new column text
-            $spreadsheet = $workSheet->getParent();
+            $spreadsheet = $workSheet->getParentOrThrow();
             foreach ($spreadsheet->getWorksheetIterator() as $sheet) {
                 self::updateStructuredReferencesInCells($sheet, $oldTitle, $newTitle);
             }
@@ -225,12 +211,12 @@ class Column
 
     private static function updateStructuredReferencesInCells(Worksheet $worksheet, string $oldTitle, string $newTitle): void
     {
-        $pattern = '/\[(@?)' . preg_quote($oldTitle) . '\]/mui';
+        $pattern = '/\[(@?)' . preg_quote($oldTitle, '/') . '\]/mui';
 
         foreach ($worksheet->getCoordinates(false) as $coordinate) {
             $cell = $worksheet->getCell($coordinate);
             if ($cell->getDataType() === DataType::TYPE_FORMULA) {
-                $formula = $cell->getValue();
+                $formula = $cell->getValueString();
                 if (preg_match($pattern, $formula) === 1) {
                     $formula = preg_replace($pattern, "[$1{$newTitle}]", $formula);
                     $cell->setValueExplicit($formula, DataType::TYPE_FORMULA);
@@ -241,13 +227,13 @@ class Column
 
     private static function updateStructuredReferencesInNamedFormulae(Spreadsheet $spreadsheet, string $oldTitle, string $newTitle): void
     {
-        $pattern = '/\[(@?)' . preg_quote($oldTitle) . '\]/mui';
+        $pattern = '/\[(@?)' . preg_quote($oldTitle, '/') . '\]/mui';
 
         foreach ($spreadsheet->getNamedFormulae() as $namedFormula) {
             $formula = $namedFormula->getValue();
             if (preg_match($pattern, $formula) === 1) {
-                $formula = preg_replace($pattern, "[$1{$newTitle}]", $formula);
-                $namedFormula->setValue($formula); // @phpstan-ignore-line
+                $formula = preg_replace($pattern, "[$1{$newTitle}]", $formula) ?? '';
+                $namedFormula->setValue($formula);
             }
         }
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php
index 78643c7..8115302 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Table/TableStyle.php
@@ -70,45 +70,33 @@ class TableStyle
 
     /**
      * Theme.
-     *
-     * @var string
      */
-    private $theme;
+    private string $theme;
 
     /**
      * Show First Column.
-     *
-     * @var bool
      */
-    private $showFirstColumn = false;
+    private bool $showFirstColumn = false;
 
     /**
      * Show Last Column.
-     *
-     * @var bool
      */
-    private $showLastColumn = false;
+    private bool $showLastColumn = false;
 
     /**
      * Show Row Stripes.
-     *
-     * @var bool
      */
-    private $showRowStripes = false;
+    private bool $showRowStripes = false;
 
     /**
      * Show Column Stripes.
-     *
-     * @var bool
      */
-    private $showColumnStripes = false;
+    private bool $showColumnStripes = false;
 
     /**
      * Table.
-     *
-     * @var null|Table
      */
-    private $table;
+    private ?Table $table = null;
 
     /**
      * Create a new Table Style.
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Validations.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Validations.php
index 5e5cdf7..99b5eed 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Validations.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Validations.php
@@ -12,10 +12,10 @@ class Validations
     /**
      * Validate a cell address.
      *
-     * @param null|array<int>|CellAddress|string $cellAddress Coordinate of the cell as a string, eg: 'C5';
+     * @param null|array{0: int, 1: int}|CellAddress|string $cellAddress Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      */
-    public static function validateCellAddress($cellAddress): string
+    public static function validateCellAddress(null|CellAddress|string|array $cellAddress): string
     {
         if (is_string($cellAddress)) {
             [$worksheet, $address] = Worksheet::extractSheetTitle($cellAddress, true);
@@ -36,11 +36,11 @@ class Validations
     /**
      * Validate a cell address or cell range.
      *
-     * @param AddressRange|array<int>|CellAddress|int|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
      *               or as an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 12]),
      *               or as a CellAddress or AddressRange object.
      */
-    public static function validateCellOrCellRange($cellRange): string
+    public static function validateCellOrCellRange(AddressRange|CellAddress|int|string|array $cellRange): string
     {
         if (is_string($cellRange) || is_numeric($cellRange)) {
             // Convert a single column reference like 'A' to 'A:A',
@@ -53,14 +53,17 @@ class Validations
         return self::validateCellRange($cellRange);
     }
 
+    private const SETMAXROW = '${1}1:${2}' . AddressRange::MAX_ROW;
+    private const SETMAXCOL = 'A${1}:' . AddressRange::MAX_COLUMN . '${2}';
+
     /**
      * Validate a cell range.
      *
-     * @param AddressRange|array<int>|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
      *               or as an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 12]),
      *               or as an AddressRange object.
      */
-    public static function validateCellRange($cellRange): string
+    public static function validateCellRange(AddressRange|string|array $cellRange): string
     {
         if (is_string($cellRange)) {
             [$worksheet, $addressRange] = Worksheet::extractSheetTitle($cellRange, true);
@@ -69,8 +72,8 @@ class Validations
             //      or Row ranges like '1:3' to 'A1:XFD3'
             $addressRange = (string) preg_replace(
                 ['/^([A-Z]+):([A-Z]+)$/i', '/^(\\d+):(\\d+)$/'],
-                ['${1}1:${2}1048576', 'A${1}:XFD${2}'],
-                $addressRange
+                [self::SETMAXROW, self::SETMAXCOL],
+                $addressRange ?? ''
             );
 
             return empty($worksheet) ? strtoupper($addressRange) : $worksheet . '!' . strtoupper($addressRange);
@@ -78,15 +81,15 @@ class Validations
 
         if (is_array($cellRange)) {
             switch (count($cellRange)) {
-                case 2:
-                    $from = [$cellRange[0], $cellRange[1]];
-                    $to = [$cellRange[0], $cellRange[1]];
-
-                    break;
                 case 4:
                     $from = [$cellRange[0], $cellRange[1]];
                     $to = [$cellRange[2], $cellRange[3]];
 
+                    break;
+                case 2:
+                    $from = [$cellRange[0], $cellRange[1]];
+                    $to = [$cellRange[0], $cellRange[1]];
+
                     break;
                 default:
                     throw new SpreadsheetException('CellRange array length must be 2 or 4');
@@ -103,7 +106,7 @@ class Validations
         $coordinate = strtoupper($coordinate);
         // Eliminate leading equal sign
         $testCoordinate = (string) preg_replace('/^=/', '', $coordinate);
-        $defined = $worksheet->getParent()->getDefinedName($testCoordinate, $worksheet);
+        $defined = $worksheet->getParentOrThrow()->getDefinedName($testCoordinate, $worksheet);
         if ($defined !== null) {
             if ($defined->getWorksheet() === $worksheet && !$defined->isFormula()) {
                 $coordinate = (string) preg_replace('/^=/', '', $defined->getValue());
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
index d71bc38..39355b3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
@@ -3,16 +3,17 @@
 namespace PhpOffice\PhpSpreadsheet\Worksheet;
 
 use ArrayObject;
+use Generator;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Calculation\Functions;
 use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
-use PhpOffice\PhpSpreadsheet\Cell\CellRange;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
 use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
+use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
 use PhpOffice\PhpSpreadsheet\Chart\Chart;
 use PhpOffice\PhpSpreadsheet\Collection\Cells;
 use PhpOffice\PhpSpreadsheet\Collection\CellsFactory;
@@ -24,9 +25,11 @@ use PhpOffice\PhpSpreadsheet\ReferenceHelper;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Shared;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
 use PhpOffice\PhpSpreadsheet\Style\Color;
 use PhpOffice\PhpSpreadsheet\Style\Conditional;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
+use PhpOffice\PhpSpreadsheet\Style\Protection as StyleProtection;
 use PhpOffice\PhpSpreadsheet\Style\Style;
 
 class Worksheet implements IComparable
@@ -35,6 +38,8 @@ class Worksheet implements IComparable
     public const BREAK_NONE = 0;
     public const BREAK_ROW = 1;
     public const BREAK_COLUMN = 2;
+    // Maximum column for row break
+    public const BREAK_ROW_MAX_COLUMN = 16383;
 
     // Sheet state
     public const SHEETSTATE_VISIBLE = 'visible';
@@ -56,304 +61,269 @@ class Worksheet implements IComparable
 
     /**
      * Invalid characters in sheet title.
-     *
-     * @var array
      */
-    private static $invalidCharacters = ['*', ':', '/', '\\', '?', '[', ']'];
+    private static array $invalidCharacters = ['*', ':', '/', '\\', '?', '[', ']'];
 
     /**
      * Parent spreadsheet.
-     *
-     * @var Spreadsheet
      */
-    private $parent;
+    private ?Spreadsheet $parent = null;
 
     /**
      * Collection of cells.
-     *
-     * @var Cells
      */
-    private $cellCollection;
+    private Cells $cellCollection;
+
+    private bool $cellCollectionInitialized = true;
 
     /**
      * Collection of row dimensions.
      *
      * @var RowDimension[]
      */
-    private $rowDimensions = [];
+    private array $rowDimensions = [];
 
     /**
      * Default row dimension.
-     *
-     * @var RowDimension
      */
-    private $defaultRowDimension;
+    private RowDimension $defaultRowDimension;
 
     /**
      * Collection of column dimensions.
      *
      * @var ColumnDimension[]
      */
-    private $columnDimensions = [];
+    private array $columnDimensions = [];
 
     /**
      * Default column dimension.
-     *
-     * @var ColumnDimension
      */
-    private $defaultColumnDimension;
+    private ColumnDimension $defaultColumnDimension;
 
     /**
      * Collection of drawings.
      *
      * @var ArrayObject<int, BaseDrawing>
      */
-    private $drawingCollection;
+    private ArrayObject $drawingCollection;
 
     /**
      * Collection of Chart objects.
      *
      * @var ArrayObject<int, Chart>
      */
-    private $chartCollection;
+    private ArrayObject $chartCollection;
 
     /**
      * Collection of Table objects.
      *
      * @var ArrayObject<int, Table>
      */
-    private $tableCollection;
+    private ArrayObject $tableCollection;
 
     /**
      * Worksheet title.
-     *
-     * @var string
      */
-    private $title;
+    private string $title = '';
 
     /**
      * Sheet state.
-     *
-     * @var string
      */
-    private $sheetState;
+    private string $sheetState;
 
     /**
      * Page setup.
-     *
-     * @var PageSetup
      */
-    private $pageSetup;
+    private PageSetup $pageSetup;
 
     /**
      * Page margins.
-     *
-     * @var PageMargins
      */
-    private $pageMargins;
+    private PageMargins $pageMargins;
 
     /**
      * Page header/footer.
-     *
-     * @var HeaderFooter
      */
-    private $headerFooter;
+    private HeaderFooter $headerFooter;
 
     /**
      * Sheet view.
-     *
-     * @var SheetView
      */
-    private $sheetView;
+    private SheetView $sheetView;
 
     /**
      * Protection.
-     *
-     * @var Protection
      */
-    private $protection;
+    private Protection $protection;
 
     /**
      * Collection of styles.
      *
      * @var Style[]
      */
-    private $styles = [];
+    private array $styles = [];
 
     /**
      * Conditional styles. Indexed by cell coordinate, e.g. 'A1'.
-     *
-     * @var array
      */
-    private $conditionalStylesCollection = [];
+    private array $conditionalStylesCollection = [];
 
     /**
-     * Collection of breaks.
+     * Collection of row breaks.
      *
-     * @var int[]
+     * @var PageBreak[]
      */
-    private $breaks = [];
+    private array $rowBreaks = [];
+
+    /**
+     * Collection of column breaks.
+     *
+     * @var PageBreak[]
+     */
+    private array $columnBreaks = [];
 
     /**
      * Collection of merged cell ranges.
      *
      * @var string[]
      */
-    private $mergeCells = [];
+    private array $mergeCells = [];
 
     /**
      * Collection of protected cell ranges.
      *
-     * @var string[]
+     * @var ProtectedRange[]
      */
-    private $protectedCells = [];
+    private array $protectedCells = [];
 
     /**
      * Autofilter Range and selection.
-     *
-     * @var AutoFilter
      */
-    private $autoFilter;
+    private AutoFilter $autoFilter;
 
     /**
      * Freeze pane.
-     *
-     * @var null|string
      */
-    private $freezePane;
+    private ?string $freezePane = null;
 
     /**
      * Default position of the right bottom pane.
-     *
-     * @var null|string
      */
-    private $topLeftCell;
+    private ?string $topLeftCell = null;
+
+    private string $paneTopLeftCell = '';
+
+    private string $activePane = '';
+
+    private int $xSplit = 0;
+
+    private int $ySplit = 0;
+
+    private string $paneState = '';
+
+    /**
+     * Properties of the 4 panes.
+     *
+     * @var (null|Pane)[]
+     */
+    private array $panes = [
+        'bottomRight' => null,
+        'bottomLeft' => null,
+        'topRight' => null,
+        'topLeft' => null,
+    ];
 
     /**
      * Show gridlines?
-     *
-     * @var bool
      */
-    private $showGridlines = true;
+    private bool $showGridlines = true;
 
     /**
      * Print gridlines?
-     *
-     * @var bool
      */
-    private $printGridlines = false;
+    private bool $printGridlines = false;
 
     /**
      * Show row and column headers?
-     *
-     * @var bool
      */
-    private $showRowColHeaders = true;
+    private bool $showRowColHeaders = true;
 
     /**
      * Show summary below? (Row/Column outline).
-     *
-     * @var bool
      */
-    private $showSummaryBelow = true;
+    private bool $showSummaryBelow = true;
 
     /**
      * Show summary right? (Row/Column outline).
-     *
-     * @var bool
      */
-    private $showSummaryRight = true;
+    private bool $showSummaryRight = true;
 
     /**
      * Collection of comments.
      *
      * @var Comment[]
      */
-    private $comments = [];
+    private array $comments = [];
 
     /**
      * Active cell. (Only one!).
-     *
-     * @var string
      */
-    private $activeCell = 'A1';
+    private string $activeCell = 'A1';
 
     /**
      * Selected cells.
-     *
-     * @var string
      */
-    private $selectedCells = 'A1';
+    private string $selectedCells = 'A1';
 
     /**
      * Cached highest column.
-     *
-     * @var int
      */
-    private $cachedHighestColumn = 1;
+    private int $cachedHighestColumn = 1;
 
     /**
      * Cached highest row.
-     *
-     * @var int
      */
-    private $cachedHighestRow = 1;
+    private int $cachedHighestRow = 1;
 
     /**
      * Right-to-left?
-     *
-     * @var bool
      */
-    private $rightToLeft = false;
+    private bool $rightToLeft = false;
 
     /**
      * Hyperlinks. Indexed by cell coordinate, e.g. 'A1'.
-     *
-     * @var array
      */
-    private $hyperlinkCollection = [];
+    private array $hyperlinkCollection = [];
 
     /**
      * Data validation objects. Indexed by cell coordinate, e.g. 'A1'.
-     *
-     * @var array
      */
-    private $dataValidationCollection = [];
+    private array $dataValidationCollection = [];
 
     /**
      * Tab color.
-     *
-     * @var null|Color
      */
-    private $tabColor;
+    private ?Color $tabColor = null;
 
     /**
      * Dirty flag.
-     *
-     * @var bool
      */
-    private $dirty = true;
+    private bool $dirty = true;
 
     /**
      * Hash.
-     *
-     * @var string
      */
-    private $hash;
+    private string $hash;
 
     /**
      * CodeName.
-     *
-     * @var string
      */
-    private $codeName;
+    private ?string $codeName = null;
 
     /**
      * Create a new worksheet.
-     *
-     * @param string $title
      */
-    public function __construct(?Spreadsheet $parent = null, $title = 'Worksheet')
+    public function __construct(?Spreadsheet $parent = null, string $title = 'Worksheet')
     {
         // Set parent and title
         $this->parent = $parent;
@@ -393,13 +363,12 @@ class Worksheet implements IComparable
      */
     public function disconnectCells(): void
     {
-        if ($this->cellCollection !== null) {
+        if ($this->cellCollectionInitialized) {
             $this->cellCollection->unsetWorksheetCells();
-            // @phpstan-ignore-next-line
-            $this->cellCollection = null;
+            unset($this->cellCollection);
+            $this->cellCollectionInitialized = false;
         }
         //    detach ourself from the workbook, so that it can then delete this worksheet successfully
-        // @phpstan-ignore-next-line
         $this->parent = null;
     }
 
@@ -411,25 +380,21 @@ class Worksheet implements IComparable
         Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
 
         $this->disconnectCells();
-        $this->rowDimensions = [];
+        unset($this->rowDimensions, $this->columnDimensions, $this->tableCollection, $this->drawingCollection, $this->chartCollection, $this->autoFilter);
     }
 
     /**
      * Return the cell collection.
-     *
-     * @return Cells
      */
-    public function getCellCollection()
+    public function getCellCollection(): Cells
     {
         return $this->cellCollection;
     }
 
     /**
      * Get array of invalid characters for sheet title.
-     *
-     * @return array
      */
-    public static function getInvalidCharacters()
+    public static function getInvalidCharacters(): array
     {
         return self::$invalidCharacters;
     }
@@ -441,7 +406,7 @@ class Worksheet implements IComparable
      *
      * @return string The valid string
      */
-    private static function checkSheetCodeName($sheetCodeName)
+    private static function checkSheetCodeName(string $sheetCodeName): string
     {
         $charCount = Shared\StringHelper::countCharacters($sheetCodeName);
         if ($charCount == 0) {
@@ -449,9 +414,9 @@ class Worksheet implements IComparable
         }
         // Some of the printable ASCII characters are invalid:  * : / \ ? [ ] and  first and last characters cannot be a "'"
         if (
-            (str_replace(self::$invalidCharacters, '', $sheetCodeName) !== $sheetCodeName) ||
-            (Shared\StringHelper::substring($sheetCodeName, -1, 1) == '\'') ||
-            (Shared\StringHelper::substring($sheetCodeName, 0, 1) == '\'')
+            (str_replace(self::$invalidCharacters, '', $sheetCodeName) !== $sheetCodeName)
+            || (Shared\StringHelper::substring($sheetCodeName, -1, 1) == '\'')
+            || (Shared\StringHelper::substring($sheetCodeName, 0, 1) == '\'')
         ) {
             throw new Exception('Invalid character found in sheet code name');
         }
@@ -471,7 +436,7 @@ class Worksheet implements IComparable
      *
      * @return string The valid string
      */
-    private static function checkSheetTitle($sheetTitle)
+    private static function checkSheetTitle(string $sheetTitle): string
     {
         // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
         if (str_replace(self::$invalidCharacters, '', $sheetTitle) !== $sheetTitle) {
@@ -493,9 +458,9 @@ class Worksheet implements IComparable
      *
      * @return string[]
      */
-    public function getCoordinates($sorted = true)
+    public function getCoordinates(bool $sorted = true): array
     {
-        if ($this->cellCollection == null) {
+        if ($this->cellCollectionInitialized === false) {
             return [];
         }
 
@@ -511,17 +476,15 @@ class Worksheet implements IComparable
      *
      * @return RowDimension[]
      */
-    public function getRowDimensions()
+    public function getRowDimensions(): array
     {
         return $this->rowDimensions;
     }
 
     /**
      * Get default row dimension.
-     *
-     * @return RowDimension
      */
-    public function getDefaultRowDimension()
+    public function getDefaultRowDimension(): RowDimension
     {
         return $this->defaultRowDimension;
     }
@@ -531,17 +494,24 @@ class Worksheet implements IComparable
      *
      * @return ColumnDimension[]
      */
-    public function getColumnDimensions()
+    public function getColumnDimensions(): array
     {
+        /** @var callable $callable */
+        $callable = [self::class, 'columnDimensionCompare'];
+        uasort($this->columnDimensions, $callable);
+
         return $this->columnDimensions;
     }
 
+    private static function columnDimensionCompare(ColumnDimension $a, ColumnDimension $b): int
+    {
+        return $a->getColumnNumeric() - $b->getColumnNumeric();
+    }
+
     /**
      * Get default column dimension.
-     *
-     * @return ColumnDimension
      */
-    public function getDefaultColumnDimension()
+    public function getDefaultColumnDimension(): ColumnDimension
     {
         return $this->defaultColumnDimension;
     }
@@ -551,7 +521,7 @@ class Worksheet implements IComparable
      *
      * @return ArrayObject<int, BaseDrawing>
      */
-    public function getDrawingCollection()
+    public function getDrawingCollection(): ArrayObject
     {
         return $this->drawingCollection;
     }
@@ -561,28 +531,15 @@ class Worksheet implements IComparable
      *
      * @return ArrayObject<int, Chart>
      */
-    public function getChartCollection()
+    public function getChartCollection(): ArrayObject
     {
         return $this->chartCollection;
     }
 
-    /**
-     * Add chart.
-     *
-     * @param null|int $chartIndex Index where chart should go (0,1,..., or null for last)
-     *
-     * @return Chart
-     */
-    public function addChart(Chart $chart, $chartIndex = null)
+    public function addChart(Chart $chart): Chart
     {
         $chart->setWorksheet($this);
-        if ($chartIndex === null) {
-            $this->chartCollection[] = $chart;
-        } else {
-            // Insert the chart at the requested index
-            // @phpstan-ignore-next-line
-            array_splice(/** @scrutinizer ignore-type */ $this->chartCollection, $chartIndex, 0, [$chart]);
-        }
+        $this->chartCollection[] = $chart;
 
         return $chart;
     }
@@ -592,7 +549,7 @@ class Worksheet implements IComparable
      *
      * @return int The number of charts
      */
-    public function getChartCount()
+    public function getChartCount(): int
     {
         return count($this->chartCollection);
     }
@@ -604,7 +561,7 @@ class Worksheet implements IComparable
      *
      * @return Chart|false
      */
-    public function getChartByIndex($index)
+    public function getChartByIndex(?string $index)
     {
         $chartCount = count($this->chartCollection);
         if ($chartCount == 0) {
@@ -625,7 +582,7 @@ class Worksheet implements IComparable
      *
      * @return string[] The names of charts
      */
-    public function getChartNames()
+    public function getChartNames(): array
     {
         $chartNames = [];
         foreach ($this->chartCollection as $chart) {
@@ -642,27 +599,33 @@ class Worksheet implements IComparable
      *
      * @return Chart|false
      */
-    public function getChartByName($chartName)
+    public function getChartByName(string $chartName)
     {
-        $chartCount = count($this->chartCollection);
-        if ($chartCount == 0) {
-            return false;
-        }
         foreach ($this->chartCollection as $index => $chart) {
             if ($chart->getName() == $chartName) {
-                return $this->chartCollection[$index];
+                return $chart;
             }
         }
 
         return false;
     }
 
+    public function getChartByNameOrThrow(string $chartName): Chart
+    {
+        $chart = $this->getChartByName($chartName);
+        if ($chart !== false) {
+            return $chart;
+        }
+
+        throw new Exception("Sheet does not have a chart named $chartName.");
+    }
+
     /**
      * Refresh column dimensions.
      *
      * @return $this
      */
-    public function refreshColumnDimensions()
+    public function refreshColumnDimensions(): static
     {
         $newColumnDimensions = [];
         foreach ($this->getColumnDimensions() as $objColumnDimension) {
@@ -679,7 +642,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function refreshRowDimensions()
+    public function refreshRowDimensions(): static
     {
         $newRowDimensions = [];
         foreach ($this->getRowDimensions() as $objRowDimension) {
@@ -696,7 +659,7 @@ class Worksheet implements IComparable
      *
      * @return string String containing the dimension of this worksheet
      */
-    public function calculateWorksheetDimension()
+    public function calculateWorksheetDimension(): string
     {
         // Return
         return 'A1:' . $this->getHighestColumn() . $this->getHighestRow();
@@ -707,7 +670,7 @@ class Worksheet implements IComparable
      *
      * @return string String containing the dimension of this worksheet that actually contain data
      */
-    public function calculateWorksheetDataDimension()
+    public function calculateWorksheetDataDimension(): string
     {
         // Return
         return 'A1:' . $this->getHighestDataColumn() . $this->getHighestDataRow();
@@ -718,7 +681,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function calculateColumnWidths()
+    public function calculateColumnWidths(): static
     {
         // initialize $autoSizes array
         $autoSizes = [];
@@ -730,6 +693,7 @@ class Worksheet implements IComparable
 
         // There is only something to do if there are some auto-size columns
         if (!empty($autoSizes)) {
+            $holdActivePane = $this->activePane;
             // build list of cells references that participate in a merge
             $isMergeCell = [];
             foreach ($this->getMergeCells() as $cells) {
@@ -738,14 +702,7 @@ class Worksheet implements IComparable
                 }
             }
 
-            $autoFilterRange = $autoFilterFirstRowRange = $this->autoFilter->getRange();
-            if (!empty($autoFilterRange)) {
-                $autoFilterRangeBoundaries = Coordinate::rangeBoundaries($autoFilterRange);
-                $autoFilterFirstRowRange = (string) new CellRange(
-                    CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[0][0], $autoFilterRangeBoundaries[0][1]),
-                    CellAddress::fromColumnAndRow($autoFilterRangeBoundaries[1][0], $autoFilterRangeBoundaries[0][1])
-                );
-            }
+            $autoFilterIndentRanges = (new AutoFit($this))->getAutoFilterIndentRanges();
 
             // loop through all cells in the worksheet
             foreach ($this->getCoordinates(false) as $coordinate) {
@@ -760,7 +717,7 @@ class Worksheet implements IComparable
 
                     //The only exception is if it's a merge range value cell of a 'vertical' range (1 column wide)
                     if ($isMerged && $cell->isMergeRangeValueCell()) {
-                        $range = $cell->getMergeRange();
+                        $range = (string) $cell->getMergeRange();
                         $rangeBoundaries = Coordinate::rangeDimension($range);
                         if ($rangeBoundaries[0] === 1) {
                             $isMergedButProceed = true;
@@ -772,31 +729,41 @@ class Worksheet implements IComparable
                         // Determine if we need to make an adjustment for the first row in an AutoFilter range that
                         //    has a column filter dropdown
                         $filterAdjustment = false;
-                        if (!empty($autoFilterRange) && $cell->isInRange($autoFilterFirstRowRange)) {
-                            $filterAdjustment = true;
+                        if (!empty($autoFilterIndentRanges)) {
+                            foreach ($autoFilterIndentRanges as $autoFilterFirstRowRange) {
+                                if ($cell->isInRange($autoFilterFirstRowRange)) {
+                                    $filterAdjustment = true;
+
+                                    break;
+                                }
+                            }
                         }
 
                         $indentAdjustment = $cell->getStyle()->getAlignment()->getIndent();
+                        $indentAdjustment += (int) ($cell->getStyle()->getAlignment()->getHorizontal() === Alignment::HORIZONTAL_CENTER);
 
                         // Calculated value
                         // To formatted string
                         $cellValue = NumberFormat::toFormattedString(
-                            $cell->getCalculatedValue(),
-                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())
-                                ->getNumberFormat()->getFormatCode()
+                            $cell->getCalculatedValueString(),
+                            (string) $this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())
+                                ->getNumberFormat()->getFormatCode(true)
                         );
 
                         if ($cellValue !== null && $cellValue !== '') {
                             $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
-                                (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
-                                (float) Shared\Font::calculateColumnWidth(
-                                    $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
-                                    $cellValue,
-                                    $this->getParent()->getCellXfByIndex($cell->getXfIndex())
-                                        ->getAlignment()->getTextRotation(),
-                                    $this->getParent()->getDefaultStyle()->getFont(),
-                                    $filterAdjustment,
-                                    $indentAdjustment
+                                $autoSizes[$this->cellCollection->getCurrentColumn()],
+                                round(
+                                    Shared\Font::calculateColumnWidth(
+                                        $this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())->getFont(),
+                                        $cellValue,
+                                        (int) $this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())
+                                            ->getAlignment()->getTextRotation(),
+                                        $this->getParentOrThrow()->getDefaultStyle()->getFont(),
+                                        $filterAdjustment,
+                                        $indentAdjustment
+                                    ),
+                                    3
                                 )
                             );
                         }
@@ -811,27 +778,38 @@ class Worksheet implements IComparable
                 }
                 $this->getColumnDimension($columnIndex)->setWidth($width);
             }
+            $this->activePane = $holdActivePane;
         }
 
         return $this;
     }
 
     /**
-     * Get parent.
-     *
-     * @return Spreadsheet
+     * Get parent or null.
      */
-    public function getParent()
+    public function getParent(): ?Spreadsheet
     {
         return $this->parent;
     }
 
+    /**
+     * Get parent, throw exception if null.
+     */
+    public function getParentOrThrow(): Spreadsheet
+    {
+        if ($this->parent !== null) {
+            return $this->parent;
+        }
+
+        throw new Exception('Sheet does not have a parent.');
+    }
+
     /**
      * Re-bind parent.
      *
      * @return $this
      */
-    public function rebindParent(Spreadsheet $parent)
+    public function rebindParent(Spreadsheet $parent): static
     {
         if ($this->parent !== null) {
             $definedNames = $this->parent->getDefinedNames();
@@ -850,10 +828,8 @@ class Worksheet implements IComparable
 
     /**
      * Get title.
-     *
-     * @return string
      */
-    public function getTitle()
+    public function getTitle(): string
     {
         return $this->title;
     }
@@ -872,7 +848,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setTitle($title, $updateFormulaCellReferences = true, $validate = true)
+    public function setTitle(string $title, bool $updateFormulaCellReferences = true, bool $validate = true): static
     {
         // Is this a 'rename' or not?
         if ($this->getTitle() == $title) {
@@ -935,7 +911,7 @@ class Worksheet implements IComparable
      *
      * @return string Sheet state (visible, hidden, veryHidden)
      */
-    public function getSheetState()
+    public function getSheetState(): string
     {
         return $this->sheetState;
     }
@@ -947,7 +923,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setSheetState($value)
+    public function setSheetState(string $value): static
     {
         $this->sheetState = $value;
 
@@ -956,10 +932,8 @@ class Worksheet implements IComparable
 
     /**
      * Get page setup.
-     *
-     * @return PageSetup
      */
-    public function getPageSetup()
+    public function getPageSetup(): PageSetup
     {
         return $this->pageSetup;
     }
@@ -969,7 +943,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setPageSetup(PageSetup $pageSetup)
+    public function setPageSetup(PageSetup $pageSetup): static
     {
         $this->pageSetup = $pageSetup;
 
@@ -978,10 +952,8 @@ class Worksheet implements IComparable
 
     /**
      * Get page margins.
-     *
-     * @return PageMargins
      */
-    public function getPageMargins()
+    public function getPageMargins(): PageMargins
     {
         return $this->pageMargins;
     }
@@ -991,7 +963,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setPageMargins(PageMargins $pageMargins)
+    public function setPageMargins(PageMargins $pageMargins): static
     {
         $this->pageMargins = $pageMargins;
 
@@ -1000,10 +972,8 @@ class Worksheet implements IComparable
 
     /**
      * Get page header/footer.
-     *
-     * @return HeaderFooter
      */
-    public function getHeaderFooter()
+    public function getHeaderFooter(): HeaderFooter
     {
         return $this->headerFooter;
     }
@@ -1013,7 +983,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setHeaderFooter(HeaderFooter $headerFooter)
+    public function setHeaderFooter(HeaderFooter $headerFooter): static
     {
         $this->headerFooter = $headerFooter;
 
@@ -1022,10 +992,8 @@ class Worksheet implements IComparable
 
     /**
      * Get sheet view.
-     *
-     * @return SheetView
      */
-    public function getSheetView()
+    public function getSheetView(): SheetView
     {
         return $this->sheetView;
     }
@@ -1035,7 +1003,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setSheetView(SheetView $sheetView)
+    public function setSheetView(SheetView $sheetView): static
     {
         $this->sheetView = $sheetView;
 
@@ -1044,10 +1012,8 @@ class Worksheet implements IComparable
 
     /**
      * Get Protection.
-     *
-     * @return Protection
      */
-    public function getProtection()
+    public function getProtection(): Protection
     {
         return $this->protection;
     }
@@ -1057,7 +1023,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setProtection(Protection $protection)
+    public function setProtection(Protection $protection): static
     {
         $this->protection = $protection;
         $this->dirty = true;
@@ -1073,7 +1039,7 @@ class Worksheet implements IComparable
      *
      * @return string Highest column name
      */
-    public function getHighestColumn($row = null)
+    public function getHighestColumn($row = null): string
     {
         if ($row === null) {
             return Coordinate::stringFromColumnIndex($this->cachedHighestColumn);
@@ -1090,7 +1056,7 @@ class Worksheet implements IComparable
      *
      * @return string Highest column name that contains data
      */
-    public function getHighestDataColumn($row = null)
+    public function getHighestDataColumn($row = null): string
     {
         return $this->cellCollection->getHighestColumn($row);
     }
@@ -1103,7 +1069,7 @@ class Worksheet implements IComparable
      *
      * @return int Highest row number
      */
-    public function getHighestRow($column = null)
+    public function getHighestRow(?string $column = null): int
     {
         if ($column === null) {
             return $this->cachedHighestRow;
@@ -1120,7 +1086,7 @@ class Worksheet implements IComparable
      *
      * @return int Highest row number that contains data
      */
-    public function getHighestDataRow($column = null)
+    public function getHighestDataRow(?string $column = null): int
     {
         return $this->cellCollection->getHighestRow($column);
     }
@@ -1130,7 +1096,7 @@ class Worksheet implements IComparable
      *
      * @return array Highest column name and highest row number
      */
-    public function getHighestRowAndColumn()
+    public function getHighestRowAndColumn(): array
     {
         return $this->cellCollection->getHighestRowAndColumn();
     }
@@ -1138,37 +1104,17 @@ class Worksheet implements IComparable
     /**
      * Set a cell value.
      *
-     * @param array<int>|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param array{0: int, 1: int}|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      * @param mixed $value Value for the cell
+     * @param null|IValueBinder $binder Value Binder to override the currently set Value Binder
      *
      * @return $this
      */
-    public function setCellValue($coordinate, $value)
+    public function setCellValue(CellAddress|string|array $coordinate, mixed $value, ?IValueBinder $binder = null): static
     {
         $cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate));
-        $this->getCell($cellAddress)->setValue($value);
-
-        return $this;
-    }
-
-    /**
-     * Set a cell value by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the setCellValue() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::setCellValue()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     * @param mixed $value Value of the cell
-     *
-     * @return $this
-     */
-    public function setCellValueByColumnAndRow($columnIndex, $row, $value)
-    {
-        $this->getCell(Coordinate::stringFromColumnIndex($columnIndex) . $row)->setValue($value);
+        $this->getCell($cellAddress)->setValue($value, $binder);
 
         return $this;
     }
@@ -1176,7 +1122,7 @@ class Worksheet implements IComparable
     /**
      * Set a cell value.
      *
-     * @param array<int>|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param array{0: int, 1: int}|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      * @param mixed $value Value of the cell
      * @param string $dataType Explicit data type, see DataType::TYPE_*
@@ -1190,7 +1136,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setCellValueExplicit($coordinate, $value, $dataType)
+    public function setCellValueExplicit(CellAddress|string|array $coordinate, mixed $value, string $dataType): static
     {
         $cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate));
         $this->getCell($cellAddress)->setValueExplicit($value, $dataType);
@@ -1198,39 +1144,10 @@ class Worksheet implements IComparable
         return $this;
     }
 
-    /**
-     * Set a cell value by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the setCellValueExplicit() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::setCellValueExplicit()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     * @param mixed $value Value of the cell
-     * @param string $dataType Explicit data type, see DataType::TYPE_*
-     *        Note that PhpSpreadsheet does not validate that the value and datatype are consistent, in using this
-     *             method, then it is your responsibility as an end-user developer to validate that the value and
-     *             the datatype match.
-     *       If you do mismatch value and datatpe, then the value you enter may be changed to match the datatype
-     *          that you specify.
-     *
-     * @see DataType
-     *
-     * @return $this
-     */
-    public function setCellValueExplicitByColumnAndRow($columnIndex, $row, $value, $dataType)
-    {
-        $this->getCell(Coordinate::stringFromColumnIndex($columnIndex) . $row)->setValueExplicit($value, $dataType);
-
-        return $this;
-    }
-
     /**
      * Get cell at a specific coordinate.
      *
-     * @param array<int>|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param array{0: int, 1: int}|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      *
      * @return Cell Cell that was found or created
@@ -1240,7 +1157,7 @@ class Worksheet implements IComparable
      *              the "active" cell, and any previous assignment becomes a disconnected reference because
      *              the active cell has changed.
      */
-    public function getCell($coordinate): Cell
+    public function getCell(CellAddress|string|array $coordinate): Cell
     {
         $cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate));
 
@@ -1254,7 +1171,7 @@ class Worksheet implements IComparable
 
         /** @var Worksheet $sheet */
         [$sheet, $finalCoordinate] = $this->getWorksheetAndCoordinate($cellAddress);
-        $cell = $sheet->cellCollection->get($finalCoordinate);
+        $cell = $sheet->getCellCollection()->get($finalCoordinate);
 
         return $cell ?? $sheet->createNewCell($finalCoordinate);
     }
@@ -1271,18 +1188,18 @@ class Worksheet implements IComparable
         $finalCoordinate = null;
 
         // Worksheet reference?
-        if (strpos($coordinate, '!') !== false) {
+        if (str_contains($coordinate, '!')) {
             $worksheetReference = self::extractSheetTitle($coordinate, true);
 
-            $sheet = $this->parent->getSheetByName($worksheetReference[0]);
+            $sheet = $this->getParentOrThrow()->getSheetByName($worksheetReference[0]);
             $finalCoordinate = strtoupper($worksheetReference[1]);
 
             if ($sheet === null) {
                 throw new Exception('Sheet not found for name: ' . $worksheetReference[0]);
             }
         } elseif (
-            !preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $coordinate) &&
-            preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/iu', $coordinate)
+            !preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $coordinate)
+            && preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/iu', $coordinate)
         ) {
             // Named range?
             $namedRange = $this->validateNamedRange($coordinate, true);
@@ -1305,7 +1222,7 @@ class Worksheet implements IComparable
 
         if (Coordinate::coordinateIsRange($finalCoordinate)) {
             throw new Exception('Cell coordinate string can not be a range of cells.');
-        } elseif (strpos($finalCoordinate, '$') !== false) {
+        } elseif (str_contains($finalCoordinate, '$')) {
             throw new Exception('Cell coordinate must not be absolute.');
         }
 
@@ -1319,7 +1236,7 @@ class Worksheet implements IComparable
      *
      * @return null|Cell Cell that was found or null
      */
-    private function getCellOrNull($coordinate): ?Cell
+    private function getCellOrNull(string $coordinate): ?Cell
     {
         // Check cell collection
         if ($this->cellCollection->has($coordinate)) {
@@ -1329,29 +1246,6 @@ class Worksheet implements IComparable
         return null;
     }
 
-    /**
-     * Get cell at a specific coordinate by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the getCell() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::getCell()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     *
-     * @return Cell Cell that was found/created or null
-     *              WARNING: Because the cell collection can be cached to reduce memory, it only allows one
-     *              "active" cell at a time in memory. If you assign that cell to a variable, then select
-     *              another cell using getCell() or any of its variants, the newly selected cell becomes
-     *              the "active" cell, and any previous assignment becomes a disconnected reference because
-     *              the active cell has changed.
-     */
-    public function getCellByColumnAndRow($columnIndex, $row): Cell
-    {
-        return $this->getCell(Coordinate::stringFromColumnIndex($columnIndex) . $row);
-    }
-
     /**
      * Create a new cell at the specified coordinate.
      *
@@ -1364,7 +1258,7 @@ class Worksheet implements IComparable
      *              the "active" cell, and any previous assignment becomes a disconnected reference because
      *              the active cell has changed.
      */
-    public function createNewCell($coordinate): Cell
+    public function createNewCell(string $coordinate): Cell
     {
         [$column, $row, $columnString] = Coordinate::indexesFromString($coordinate);
         $cell = new Cell(null, DataType::TYPE_NULL, $this);
@@ -1383,13 +1277,16 @@ class Worksheet implements IComparable
         $rowDimension = $this->rowDimensions[$row] ?? null;
         $columnDimension = $this->columnDimensions[$columnString] ?? null;
 
+        $xfSet = false;
         if ($rowDimension !== null) {
             $rowXf = (int) $rowDimension->getXfIndex();
             if ($rowXf > 0) {
                 // then there is a row dimension with explicit style, assign it to the cell
                 $cell->setXfIndex($rowXf);
+                $xfSet = true;
             }
-        } elseif ($columnDimension !== null) {
+        }
+        if (!$xfSet && $columnDimension !== null) {
             $colXf = (int) $columnDimension->getXfIndex();
             if ($colXf > 0) {
                 // then there is a column dimension, assign it to the cell
@@ -1403,32 +1300,15 @@ class Worksheet implements IComparable
     /**
      * Does the cell at a specific coordinate exist?
      *
-     * @param array<int>|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param array{0: int, 1: int}|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      */
-    public function cellExists($coordinate): bool
+    public function cellExists(CellAddress|string|array $coordinate): bool
     {
         $cellAddress = Validations::validateCellAddress($coordinate);
-        /** @var Worksheet $sheet */
         [$sheet, $finalCoordinate] = $this->getWorksheetAndCoordinate($cellAddress);
 
-        return $sheet->cellCollection->has($finalCoordinate);
-    }
-
-    /**
-     * Cell at a specific coordinate by using numeric cell coordinates exists?
-     *
-     * @deprecated 1.23.0
-     *      Use the cellExists() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::cellExists()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     */
-    public function cellExistsByColumnAndRow($columnIndex, $row): bool
-    {
-        return $this->cellExists(Coordinate::stringFromColumnIndex($columnIndex) . $row);
+        return $sheet->getCellCollection()->has($finalCoordinate);
     }
 
     /**
@@ -1453,6 +1333,11 @@ class Worksheet implements IComparable
         return isset($this->rowDimensions[$row]);
     }
 
+    public function columnDimensionExists(string $column): bool
+    {
+        return isset($this->columnDimensions[$column]);
+    }
+
     /**
      * Get column dimension at a specific column.
      *
@@ -1491,7 +1376,7 @@ class Worksheet implements IComparable
      *
      * @return Style[]
      */
-    public function getStyles()
+    public function getStyles(): array
     {
         return $this->styles;
     }
@@ -1499,52 +1384,22 @@ class Worksheet implements IComparable
     /**
      * Get style for cell.
      *
-     * @param AddressRange|array<int>|CellAddress|int|string $cellCoordinate
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $cellCoordinate
      *              A simple string containing a cell address like 'A1' or a cell range like 'A1:E10'
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or a CellAddress or AddressRange object.
      */
-    public function getStyle($cellCoordinate): Style
+    public function getStyle(AddressRange|CellAddress|int|string|array $cellCoordinate): Style
     {
         $cellCoordinate = Validations::validateCellOrCellRange($cellCoordinate);
 
         // set this sheet as active
-        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
+        $this->getParentOrThrow()->setActiveSheetIndex($this->getParentOrThrow()->getIndex($this));
 
         // set cell coordinate as active
         $this->setSelectedCells($cellCoordinate);
 
-        return $this->parent->getCellXfSupervisor();
-    }
-
-    /**
-     * Get style for cell by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the getStyle() method with a cell address range such as 'C5:F8' instead;,
-     *          or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
-     *          or an AddressRange object.
-     * @see Worksheet::getStyle()
-     *
-     * @param int $columnIndex1 Numeric column coordinate of the cell
-     * @param int $row1 Numeric row coordinate of the cell
-     * @param null|int $columnIndex2 Numeric column coordinate of the range cell
-     * @param null|int $row2 Numeric row coordinate of the range cell
-     *
-     * @return Style
-     */
-    public function getStyleByColumnAndRow($columnIndex1, $row1, $columnIndex2 = null, $row2 = null)
-    {
-        if ($columnIndex2 !== null && $row2 !== null) {
-            $cellRange = new CellRange(
-                CellAddress::fromColumnAndRow($columnIndex1, $row1),
-                CellAddress::fromColumnAndRow($columnIndex2, $row2)
-            );
-
-            return $this->getStyle($cellRange);
-        }
-
-        return $this->getStyle(CellAddress::fromColumnAndRow($columnIndex1, $row1));
+        return $this->getParentOrThrow()->getCellXfSupervisor();
     }
 
     /**
@@ -1561,14 +1416,17 @@ class Worksheet implements IComparable
     public function getConditionalStyles(string $coordinate): array
     {
         $coordinate = strtoupper($coordinate);
-        if (strpos($coordinate, ':') !== false) {
+        if (str_contains($coordinate, ':')) {
             return $this->conditionalStylesCollection[$coordinate] ?? [];
         }
 
         $cell = $this->getCell($coordinate);
         foreach (array_keys($this->conditionalStylesCollection) as $conditionalRange) {
-            if ($cell->isInRange($conditionalRange)) {
-                return $this->conditionalStylesCollection[$conditionalRange];
+            $cellBlocks = explode(',', Coordinate::resolveUnionAndIntersection($conditionalRange));
+            foreach ($cellBlocks as $cellBlock) {
+                if ($cell->isInRange($cellBlock)) {
+                    return $this->conditionalStylesCollection[$conditionalRange];
+                }
             }
         }
 
@@ -1580,8 +1438,11 @@ class Worksheet implements IComparable
         $coordinate = strtoupper($coordinate);
         $cell = $this->getCell($coordinate);
         foreach (array_keys($this->conditionalStylesCollection) as $conditionalRange) {
-            if ($cell->isInRange($conditionalRange)) {
-                return $conditionalRange;
+            $cellBlocks = explode(',', Coordinate::resolveUnionAndIntersection($conditionalRange));
+            foreach ($cellBlocks as $cellBlock) {
+                if ($cell->isInRange($cellBlock)) {
+                    return $conditionalRange;
+                }
             }
         }
 
@@ -1597,10 +1458,10 @@ class Worksheet implements IComparable
      *          If a range of cells is specified, then true will only be returned if the range matches the entire
      *               range of the conditional.
      */
-    public function conditionalStylesExists($coordinate): bool
+    public function conditionalStylesExists(string $coordinate): bool
     {
         $coordinate = strtoupper($coordinate);
-        if (strpos($coordinate, ':') !== false) {
+        if (str_contains($coordinate, ':')) {
             return isset($this->conditionalStylesCollection[$coordinate]);
         }
 
@@ -1621,7 +1482,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function removeConditionalStyles($coordinate)
+    public function removeConditionalStyles(string $coordinate): static
     {
         unset($this->conditionalStylesCollection[strtoupper($coordinate)]);
 
@@ -1630,10 +1491,8 @@ class Worksheet implements IComparable
 
     /**
      * Get collection of conditional styles.
-     *
-     * @return array
      */
-    public function getConditionalStylesCollection()
+    public function getConditionalStylesCollection(): array
     {
         return $this->conditionalStylesCollection;
     }
@@ -1646,7 +1505,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setConditionalStyles($coordinate, $styles)
+    public function setConditionalStyles(string $coordinate, array $styles): static
     {
         $this->conditionalStylesCollection[strtoupper($coordinate)] = $styles;
 
@@ -1663,11 +1522,11 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function duplicateStyle(Style $style, $range)
+    public function duplicateStyle(Style $style, string $range): static
     {
         // Add the style to the workbook if necessary
-        $workbook = $this->parent;
-        if ($existingStyle = $this->parent->getCellXfByHashCode($style->getHashCode())) {
+        $workbook = $this->getParentOrThrow();
+        if ($existingStyle = $workbook->getCellXfByHashCode($style->getHashCode())) {
             // there is already such cell Xf in our collection
             $xfIndex = $existingStyle->getIndex();
         } else {
@@ -1706,7 +1565,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function duplicateConditionalStyle(array $styles, $range = '')
+    public function duplicateConditionalStyle(array $styles, string $range = ''): static
     {
         foreach ($styles as $cellStyle) {
             if (!($cellStyle instanceof Conditional)) {
@@ -1737,60 +1596,99 @@ class Worksheet implements IComparable
     /**
      * Set break on a cell.
      *
-     * @param array<int>|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param array{0: int, 1: int}|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      * @param int $break Break type (type of Worksheet::BREAK_*)
      *
      * @return $this
      */
-    public function setBreak($coordinate, $break)
+    public function setBreak(CellAddress|string|array $coordinate, int $break, int $max = -1): static
     {
         $cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate));
 
         if ($break === self::BREAK_NONE) {
-            if (isset($this->breaks[$cellAddress])) {
-                unset($this->breaks[$cellAddress]);
-            }
-        } else {
-            $this->breaks[$cellAddress] = $break;
+            unset($this->rowBreaks[$cellAddress], $this->columnBreaks[$cellAddress]);
+        } elseif ($break === self::BREAK_ROW) {
+            $this->rowBreaks[$cellAddress] = new PageBreak($break, $cellAddress, $max);
+        } elseif ($break === self::BREAK_COLUMN) {
+            $this->columnBreaks[$cellAddress] = new PageBreak($break, $cellAddress, $max);
         }
 
         return $this;
     }
 
-    /**
-     * Set break on a cell by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the setBreak() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::setBreak()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     * @param int $break Break type (type of Worksheet::BREAK_*)
-     *
-     * @return $this
-     */
-    public function setBreakByColumnAndRow($columnIndex, $row, $break)
-    {
-        return $this->setBreak(Coordinate::stringFromColumnIndex($columnIndex) . $row, $break);
-    }
-
     /**
      * Get breaks.
      *
      * @return int[]
      */
-    public function getBreaks()
+    public function getBreaks(): array
     {
-        return $this->breaks;
+        $breaks = [];
+        /** @var callable $compareFunction */
+        $compareFunction = [self::class, 'compareRowBreaks'];
+        uksort($this->rowBreaks, $compareFunction);
+        foreach ($this->rowBreaks as $break) {
+            $breaks[$break->getCoordinate()] = self::BREAK_ROW;
+        }
+        /** @var callable $compareFunction */
+        $compareFunction = [self::class, 'compareColumnBreaks'];
+        uksort($this->columnBreaks, $compareFunction);
+        foreach ($this->columnBreaks as $break) {
+            $breaks[$break->getCoordinate()] = self::BREAK_COLUMN;
+        }
+
+        return $breaks;
+    }
+
+    /**
+     * Get row breaks.
+     *
+     * @return PageBreak[]
+     */
+    public function getRowBreaks(): array
+    {
+        /** @var callable $compareFunction */
+        $compareFunction = [self::class, 'compareRowBreaks'];
+        uksort($this->rowBreaks, $compareFunction);
+
+        return $this->rowBreaks;
+    }
+
+    protected static function compareRowBreaks(string $coordinate1, string $coordinate2): int
+    {
+        $row1 = Coordinate::indexesFromString($coordinate1)[1];
+        $row2 = Coordinate::indexesFromString($coordinate2)[1];
+
+        return $row1 - $row2;
+    }
+
+    protected static function compareColumnBreaks(string $coordinate1, string $coordinate2): int
+    {
+        $column1 = Coordinate::indexesFromString($coordinate1)[0];
+        $column2 = Coordinate::indexesFromString($coordinate2)[0];
+
+        return $column1 - $column2;
+    }
+
+    /**
+     * Get column breaks.
+     *
+     * @return PageBreak[]
+     */
+    public function getColumnBreaks(): array
+    {
+        /** @var callable $compareFunction */
+        $compareFunction = [self::class, 'compareColumnBreaks'];
+        uksort($this->columnBreaks, $compareFunction);
+
+        return $this->columnBreaks;
     }
 
     /**
      * Set merge on a cell range.
      *
-     * @param AddressRange|array<int>|string $range A simple string containing a Cell range like 'A1:E10'
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range A simple string containing a Cell range like 'A1:E10'
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or an AddressRange.
      * @param string $behaviour How the merged cells should behave.
@@ -1801,11 +1699,11 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function mergeCells($range, $behaviour = self::MERGE_CELL_CONTENT_EMPTY)
+    public function mergeCells(AddressRange|string|array $range, string $behaviour = self::MERGE_CELL_CONTENT_EMPTY): static
     {
         $range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));
 
-        if (strpos($range, ':') === false) {
+        if (!str_contains($range, ':')) {
             $range .= ":{$range}";
         }
 
@@ -1899,7 +1797,7 @@ class Worksheet implements IComparable
     public function mergeCellBehaviour(Cell $cell, string $upperLeft, string $behaviour, array $leftCellValue): array
     {
         if ($cell->getCoordinate() !== $upperLeft) {
-            Calculation::getInstance($cell->getWorksheet()->getParent())->flushInstance();
+            Calculation::getInstance($cell->getWorksheet()->getParentOrThrow())->flushInstance();
             if ($behaviour === self::MERGE_CELL_CONTENT_MERGE) {
                 $cellValue = $cell->getFormattedValue();
                 if ($cellValue !== '') {
@@ -1912,51 +1810,20 @@ class Worksheet implements IComparable
         return $leftCellValue;
     }
 
-    /**
-     * Set merge on a cell range by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the mergeCells() method with a cell address range such as 'C5:F8' instead;,
-     *          or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
-     *          or an AddressRange object.
-     * @see Worksheet::mergeCells()
-     *
-     * @param int $columnIndex1 Numeric column coordinate of the first cell
-     * @param int $row1 Numeric row coordinate of the first cell
-     * @param int $columnIndex2 Numeric column coordinate of the last cell
-     * @param int $row2 Numeric row coordinate of the last cell
-     * @param string $behaviour How the merged cells should behave.
-     *               Possible values are:
-     *                   MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells
-     *                   MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
-     *                   MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell
-     *
-     * @return $this
-     */
-    public function mergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $behaviour = self::MERGE_CELL_CONTENT_EMPTY)
-    {
-        $cellRange = new CellRange(
-            CellAddress::fromColumnAndRow($columnIndex1, $row1),
-            CellAddress::fromColumnAndRow($columnIndex2, $row2)
-        );
-
-        return $this->mergeCells($cellRange, $behaviour);
-    }
-
     /**
      * Remove merge on a cell range.
      *
-     * @param AddressRange|array<int>|string $range A simple string containing a Cell range like 'A1:E10'
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range A simple string containing a Cell range like 'A1:E10'
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or an AddressRange.
      *
      * @return $this
      */
-    public function unmergeCells($range)
+    public function unmergeCells(AddressRange|string|array $range): static
     {
         $range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));
 
-        if (strpos($range, ':') !== false) {
+        if (str_contains($range, ':')) {
             if (isset($this->mergeCells[$range])) {
                 unset($this->mergeCells[$range]);
             } else {
@@ -1969,38 +1836,12 @@ class Worksheet implements IComparable
         return $this;
     }
 
-    /**
-     * Remove merge on a cell range by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the unmergeCells() method with a cell address range such as 'C5:F8' instead;,
-     *          or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
-     *          or an AddressRange object.
-     * @see Worksheet::unmergeCells()
-     *
-     * @param int $columnIndex1 Numeric column coordinate of the first cell
-     * @param int $row1 Numeric row coordinate of the first cell
-     * @param int $columnIndex2 Numeric column coordinate of the last cell
-     * @param int $row2 Numeric row coordinate of the last cell
-     *
-     * @return $this
-     */
-    public function unmergeCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
-    {
-        $cellRange = new CellRange(
-            CellAddress::fromColumnAndRow($columnIndex1, $row1),
-            CellAddress::fromColumnAndRow($columnIndex2, $row2)
-        );
-
-        return $this->unmergeCells($cellRange);
-    }
-
     /**
      * Get merge cells array.
      *
      * @return string[]
      */
-    public function getMergeCells()
+    public function getMergeCells(): array
     {
         return $this->mergeCells;
     }
@@ -2013,7 +1854,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setMergeCells(array $mergeCells)
+    public function setMergeCells(array $mergeCells): static
     {
         $this->mergeCells = $mergeCells;
 
@@ -2023,7 +1864,7 @@ class Worksheet implements IComparable
     /**
      * Set protection on a cell or cell range.
      *
-     * @param AddressRange|array<int>|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or a CellAddress or AddressRange object.
      * @param string $password Password to unlock the protection
@@ -2031,56 +1872,28 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function protectCells($range, $password, $alreadyHashed = false)
+    public function protectCells(AddressRange|CellAddress|int|string|array $range, string $password = '', bool $alreadyHashed = false, string $name = '', string $securityDescriptor = ''): static
     {
         $range = Functions::trimSheetFromCellReference(Validations::validateCellOrCellRange($range));
 
-        if (!$alreadyHashed) {
+        if (!$alreadyHashed && $password !== '') {
             $password = Shared\PasswordHasher::hashPassword($password);
         }
-        $this->protectedCells[$range] = $password;
+        $this->protectedCells[$range] = new ProtectedRange($range, $password, $name, $securityDescriptor);
 
         return $this;
     }
 
-    /**
-     * Set protection on a cell range by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the protectCells() method with a cell address range such as 'C5:F8' instead;,
-     *          or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
-     *          or an AddressRange object.
-     * @see Worksheet::protectCells()
-     *
-     * @param int $columnIndex1 Numeric column coordinate of the first cell
-     * @param int $row1 Numeric row coordinate of the first cell
-     * @param int $columnIndex2 Numeric column coordinate of the last cell
-     * @param int $row2 Numeric row coordinate of the last cell
-     * @param string $password Password to unlock the protection
-     * @param bool $alreadyHashed If the password has already been hashed, set this to true
-     *
-     * @return $this
-     */
-    public function protectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2, $password, $alreadyHashed = false)
-    {
-        $cellRange = new CellRange(
-            CellAddress::fromColumnAndRow($columnIndex1, $row1),
-            CellAddress::fromColumnAndRow($columnIndex2, $row2)
-        );
-
-        return $this->protectCells($cellRange, $password, $alreadyHashed);
-    }
-
     /**
      * Remove protection on a cell or cell range.
      *
-     * @param AddressRange|array<int>|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or a CellAddress or AddressRange object.
      *
      * @return $this
      */
-    public function unprotectCells($range)
+    public function unprotectCells(AddressRange|CellAddress|int|string|array $range): static
     {
         $range = Functions::trimSheetFromCellReference(Validations::validateCellOrCellRange($range));
 
@@ -2094,47 +1907,37 @@ class Worksheet implements IComparable
     }
 
     /**
-     * Remove protection on a cell range by using numeric cell coordinates.
+     * Get password for protected cells.
      *
-     * @deprecated 1.23.0
-     *      Use the unprotectCells() method with a cell address range such as 'C5:F8' instead;,
-     *          or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
-     *          or an AddressRange object.
-     * @see Worksheet::unprotectCells()
+     * @return string[]
      *
-     * @param int $columnIndex1 Numeric column coordinate of the first cell
-     * @param int $row1 Numeric row coordinate of the first cell
-     * @param int $columnIndex2 Numeric column coordinate of the last cell
-     * @param int $row2 Numeric row coordinate of the last cell
-     *
-     * @return $this
+     * @deprecated 2.0.1 use getProtectedCellRanges instead
+     * @see Worksheet::getProtectedCellRanges()
      */
-    public function unprotectCellsByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
+    public function getProtectedCells(): array
     {
-        $cellRange = new CellRange(
-            CellAddress::fromColumnAndRow($columnIndex1, $row1),
-            CellAddress::fromColumnAndRow($columnIndex2, $row2)
-        );
+        $array = [];
+        foreach ($this->protectedCells as $key => $protectedRange) {
+            $array[$key] = $protectedRange->getPassword();
+        }
 
-        return $this->unprotectCells($cellRange);
+        return $array;
     }
 
     /**
      * Get protected cells.
      *
-     * @return string[]
+     * @return ProtectedRange[]
      */
-    public function getProtectedCells()
+    public function getProtectedCellRanges(): array
     {
         return $this->protectedCells;
     }
 
     /**
      * Get Autofilter.
-     *
-     * @return AutoFilter
      */
-    public function getAutoFilter()
+    public function getAutoFilter(): AutoFilter
     {
         return $this->autoFilter;
     }
@@ -2142,14 +1945,14 @@ class Worksheet implements IComparable
     /**
      * Set AutoFilter.
      *
-     * @param AddressRange|array<int>|AutoFilter|string $autoFilterOrRange
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|AutoFilter|string $autoFilterOrRange
      *            A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or an AddressRange.
      *
      * @return $this
      */
-    public function setAutoFilter($autoFilterOrRange)
+    public function setAutoFilter(AddressRange|string|array|AutoFilter $autoFilterOrRange): static
     {
         if (is_object($autoFilterOrRange) && ($autoFilterOrRange instanceof AutoFilter)) {
             $this->autoFilter = $autoFilterOrRange;
@@ -2162,32 +1965,6 @@ class Worksheet implements IComparable
         return $this;
     }
 
-    /**
-     * Set Autofilter Range by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the setAutoFilter() method with a cell address range such as 'C5:F8' instead;,
-     *          or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
-     *          or an AddressRange object or AutoFilter object.
-     * @see Worksheet::setAutoFilter()
-     *
-     * @param int $columnIndex1 Numeric column coordinate of the first cell
-     * @param int $row1 Numeric row coordinate of the first cell
-     * @param int $columnIndex2 Numeric column coordinate of the second cell
-     * @param int $row2 Numeric row coordinate of the second cell
-     *
-     * @return $this
-     */
-    public function setAutoFilterByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2)
-    {
-        $cellRange = new CellRange(
-            CellAddress::fromColumnAndRow($columnIndex1, $row1),
-            CellAddress::fromColumnAndRow($columnIndex2, $row2)
-        );
-
-        return $this->setAutoFilter($cellRange);
-    }
-
     /**
      * Remove autofilter.
      */
@@ -2203,7 +1980,7 @@ class Worksheet implements IComparable
      *
      * @return ArrayObject<int, Table>
      */
-    public function getTableCollection()
+    public function getTableCollection(): ArrayObject
     {
         return $this->tableCollection;
     }
@@ -2296,10 +2073,8 @@ class Worksheet implements IComparable
 
     /**
      * Get Freeze Pane.
-     *
-     * @return null|string
      */
-    public function getFreezePane()
+    public function getFreezePane(): ?string
     {
         return $this->freezePane;
     }
@@ -2313,17 +2088,23 @@ class Worksheet implements IComparable
      *     - B1 will freeze the columns to the left of cell B1 (i.e column A)
      *     - B2 will freeze the rows above and to the left of cell B2 (i.e row 1 and column A)
      *
-     * @param null|array<int>|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param null|array{0: int, 1: int}|CellAddress|string $coordinate Coordinate of the cell as a string, eg: 'C5';
      *            or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      *        Passing a null value for this argument will clear any existing freeze pane for this worksheet.
-     * @param null|array<int>|CellAddress|string $topLeftCell default position of the right bottom pane
+     * @param null|array{0: int, 1: int}|CellAddress|string $topLeftCell default position of the right bottom pane
      *            Coordinate of the cell as a string, eg: 'C5'; or as an array of [$columnIndex, $row] (e.g. [3, 5]),
      *            or a CellAddress object.
      *
      * @return $this
      */
-    public function freezePane($coordinate, $topLeftCell = null)
+    public function freezePane(null|CellAddress|string|array $coordinate, null|CellAddress|string|array $topLeftCell = null, bool $frozenSplit = false): static
     {
+        $this->panes = [
+            'bottomRight' => null,
+            'bottomLeft' => null,
+            'topRight' => null,
+            'topLeft' => null,
+        ];
         $cellAddress = ($coordinate !== null)
             ? Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate))
             : null;
@@ -2339,8 +2120,28 @@ class Worksheet implements IComparable
             $topLeftCell = $coordinate[0] . $coordinate[1];
         }
 
+        $topLeftCell = "$topLeftCell";
+        $this->paneTopLeftCell = $topLeftCell;
+
         $this->freezePane = $cellAddress;
         $this->topLeftCell = $topLeftCell;
+        if ($cellAddress === null) {
+            $this->paneState = '';
+            $this->xSplit = $this->ySplit = 0;
+            $this->activePane = '';
+        } else {
+            $coordinates = Coordinate::indexesFromString($cellAddress);
+            $this->xSplit = $coordinates[0] - 1;
+            $this->ySplit = $coordinates[1] - 1;
+            if ($this->xSplit > 0 || $this->ySplit > 0) {
+                $this->paneState = $frozenSplit ? self::PANE_FROZENSPLIT : self::PANE_FROZEN;
+                $this->setSelectedCellsActivePane();
+            } else {
+                $this->paneState = '';
+                $this->freezePane = null;
+                $this->activePane = '';
+            }
+        }
 
         return $this;
     }
@@ -2352,53 +2153,135 @@ class Worksheet implements IComparable
         return $this;
     }
 
-    /**
-     * Freeze Pane by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the freezePane() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::freezePane()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     *
-     * @return $this
-     */
-    public function freezePaneByColumnAndRow($columnIndex, $row)
-    {
-        return $this->freezePane(Coordinate::stringFromColumnIndex($columnIndex) . $row);
-    }
-
     /**
      * Unfreeze Pane.
      *
      * @return $this
      */
-    public function unfreezePane()
+    public function unfreezePane(): static
     {
         return $this->freezePane(null);
     }
 
     /**
      * Get the default position of the right bottom pane.
-     *
-     * @return null|string
      */
-    public function getTopLeftCell()
+    public function getTopLeftCell(): ?string
     {
         return $this->topLeftCell;
     }
 
+    public function getPaneTopLeftCell(): string
+    {
+        return $this->paneTopLeftCell;
+    }
+
+    public function setPaneTopLeftCell(string $paneTopLeftCell): self
+    {
+        $this->paneTopLeftCell = $paneTopLeftCell;
+
+        return $this;
+    }
+
+    public function usesPanes(): bool
+    {
+        return $this->xSplit > 0 || $this->ySplit > 0;
+    }
+
+    public function getPane(string $position): ?Pane
+    {
+        return $this->panes[$position] ?? null;
+    }
+
+    public function setPane(string $position, ?Pane $pane): self
+    {
+        if (array_key_exists($position, $this->panes)) {
+            $this->panes[$position] = $pane;
+        }
+
+        return $this;
+    }
+
+    /** @return (null|Pane)[] */
+    public function getPanes(): array
+    {
+        return $this->panes;
+    }
+
+    public function getActivePane(): string
+    {
+        return $this->activePane;
+    }
+
+    public function setActivePane(string $activePane): self
+    {
+        $this->activePane = array_key_exists($activePane, $this->panes) ? $activePane : '';
+
+        return $this;
+    }
+
+    public function getXSplit(): int
+    {
+        return $this->xSplit;
+    }
+
+    public function setXSplit(int $xSplit): self
+    {
+        $this->xSplit = $xSplit;
+        if (in_array($this->paneState, self::VALIDFROZENSTATE, true)) {
+            $this->freezePane([$this->xSplit + 1, $this->ySplit + 1], $this->topLeftCell, $this->paneState === self::PANE_FROZENSPLIT);
+        }
+
+        return $this;
+    }
+
+    public function getYSplit(): int
+    {
+        return $this->ySplit;
+    }
+
+    public function setYSplit(int $ySplit): self
+    {
+        $this->ySplit = $ySplit;
+        if (in_array($this->paneState, self::VALIDFROZENSTATE, true)) {
+            $this->freezePane([$this->xSplit + 1, $this->ySplit + 1], $this->topLeftCell, $this->paneState === self::PANE_FROZENSPLIT);
+        }
+
+        return $this;
+    }
+
+    public function getPaneState(): string
+    {
+        return $this->paneState;
+    }
+
+    public const PANE_FROZEN = 'frozen';
+    public const PANE_FROZENSPLIT = 'frozenSplit';
+    public const PANE_SPLIT = 'split';
+    private const VALIDPANESTATE = [self::PANE_FROZEN, self::PANE_SPLIT, self::PANE_FROZENSPLIT];
+    private const VALIDFROZENSTATE = [self::PANE_FROZEN, self::PANE_FROZENSPLIT];
+
+    public function setPaneState(string $paneState): self
+    {
+        $this->paneState = in_array($paneState, self::VALIDPANESTATE, true) ? $paneState : '';
+        if (in_array($this->paneState, self::VALIDFROZENSTATE, true)) {
+            $this->freezePane([$this->xSplit + 1, $this->ySplit + 1], $this->topLeftCell, $this->paneState === self::PANE_FROZENSPLIT);
+        } else {
+            $this->freezePane = null;
+        }
+
+        return $this;
+    }
+
     /**
      * Insert a new row, updating all possible related data.
      *
-     * @param int $before Insert before this one
-     * @param int $numberOfRows Number of rows to insert
+     * @param int $before Insert before this row number
+     * @param int $numberOfRows Number of new rows to insert
      *
      * @return $this
      */
-    public function insertNewRowBefore($before, $numberOfRows = 1)
+    public function insertNewRowBefore(int $before, int $numberOfRows = 1): static
     {
         if ($before >= 1) {
             $objReferenceHelper = ReferenceHelper::getInstance();
@@ -2413,12 +2296,12 @@ class Worksheet implements IComparable
     /**
      * Insert a new column, updating all possible related data.
      *
-     * @param string $before Insert before this one, eg: 'A'
-     * @param int $numberOfColumns Number of columns to insert
+     * @param string $before Insert before this column Name, eg: 'A'
+     * @param int $numberOfColumns Number of new columns to insert
      *
      * @return $this
      */
-    public function insertNewColumnBefore($before, $numberOfColumns = 1)
+    public function insertNewColumnBefore(string $before, int $numberOfColumns = 1): static
     {
         if (!is_numeric($before)) {
             $objReferenceHelper = ReferenceHelper::getInstance();
@@ -2433,12 +2316,12 @@ class Worksheet implements IComparable
     /**
      * Insert a new column, updating all possible related data.
      *
-     * @param int $beforeColumnIndex Insert before this one (numeric column coordinate of the cell)
-     * @param int $numberOfColumns Number of columns to insert
+     * @param int $beforeColumnIndex Insert before this column ID (numeric column coordinate of the cell)
+     * @param int $numberOfColumns Number of new columns to insert
      *
      * @return $this
      */
-    public function insertNewColumnBeforeByIndex($beforeColumnIndex, $numberOfColumns = 1)
+    public function insertNewColumnBeforeByIndex(int $beforeColumnIndex, int $numberOfColumns = 1): static
     {
         if ($beforeColumnIndex >= 1) {
             return $this->insertNewColumnBefore(Coordinate::stringFromColumnIndex($beforeColumnIndex), $numberOfColumns);
@@ -2450,12 +2333,12 @@ class Worksheet implements IComparable
     /**
      * Delete a row, updating all possible related data.
      *
-     * @param int $row Remove starting with this one
+     * @param int $row Remove rows, starting with this row number
      * @param int $numberOfRows Number of rows to remove
      *
      * @return $this
      */
-    public function removeRow($row, $numberOfRows = 1)
+    public function removeRow(int $row, int $numberOfRows = 1): static
     {
         if ($row < 1) {
             throw new Exception('Rows to be deleted should at least start from row 1.');
@@ -2467,7 +2350,7 @@ class Worksheet implements IComparable
 
         for ($r = 0; $r < $numberOfRows; ++$r) {
             if ($row + $r <= $highestRow) {
-                $this->getCellCollection()->removeRow($row + $r);
+                $this->cellCollection->removeRow($row + $r);
                 ++$removedRowsCounter;
             }
         }
@@ -2475,7 +2358,7 @@ class Worksheet implements IComparable
         $objReferenceHelper = ReferenceHelper::getInstance();
         $objReferenceHelper->insertNewBefore('A' . ($row + $numberOfRows), 0, -$numberOfRows, $this);
         for ($r = 0; $r < $removedRowsCounter; ++$r) {
-            $this->getCellCollection()->removeRow($highestRow);
+            $this->cellCollection->removeRow($highestRow);
             --$highestRow;
         }
 
@@ -2506,12 +2389,12 @@ class Worksheet implements IComparable
     /**
      * Remove a column, updating all possible related data.
      *
-     * @param string $column Remove starting with this one, eg: 'A'
+     * @param string $column Remove columns starting with this column name, eg: 'A'
      * @param int $numberOfColumns Number of columns to remove
      *
      * @return $this
      */
-    public function removeColumn($column, $numberOfColumns = 1)
+    public function removeColumn(string $column, int $numberOfColumns = 1): static
     {
         if (is_numeric($column)) {
             throw new Exception('Column references should not be numeric.');
@@ -2536,7 +2419,7 @@ class Worksheet implements IComparable
         $maxPossibleColumnsToBeRemoved = $highestColumnIndex - $pColumnIndex + 1;
 
         for ($c = 0, $n = min($maxPossibleColumnsToBeRemoved, $numberOfColumns); $c < $n; ++$c) {
-            $this->getCellCollection()->removeColumn($highestColumn);
+            $this->cellCollection->removeColumn($highestColumn);
             $highestColumn = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($highestColumn) - 1);
         }
 
@@ -2568,12 +2451,12 @@ class Worksheet implements IComparable
     /**
      * Remove a column, updating all possible related data.
      *
-     * @param int $columnIndex Remove starting with this one (numeric column coordinate of the cell)
+     * @param int $columnIndex Remove starting with this column Index (numeric column coordinate)
      * @param int $numColumns Number of columns to remove
      *
      * @return $this
      */
-    public function removeColumnByIndex($columnIndex, $numColumns = 1)
+    public function removeColumnByIndex(int $columnIndex, int $numColumns = 1): static
     {
         if ($columnIndex >= 1) {
             return $this->removeColumn(Coordinate::stringFromColumnIndex($columnIndex), $numColumns);
@@ -2697,7 +2580,7 @@ class Worksheet implements IComparable
      *
      * @return Comment[]
      */
-    public function getComments()
+    public function getComments(): array
     {
         return $this->comments;
     }
@@ -2719,18 +2602,18 @@ class Worksheet implements IComparable
     /**
      * Remove comment from cell.
      *
-     * @param array<int>|CellAddress|string $cellCoordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param array{0: int, 1: int}|CellAddress|string $cellCoordinate Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      *
      * @return $this
      */
-    public function removeComment($cellCoordinate): self
+    public function removeComment(CellAddress|string|array $cellCoordinate): self
     {
         $cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($cellCoordinate));
 
         if (Coordinate::coordinateIsRange($cellAddress)) {
             throw new Exception('Cell coordinate string can not be a range of cells.');
-        } elseif (strpos($cellAddress, '$') !== false) {
+        } elseif (str_contains($cellAddress, '$')) {
             throw new Exception('Cell coordinate string must not be absolute.');
         } elseif ($cellAddress == '') {
             throw new Exception('Cell coordinate can not be zero-length string.');
@@ -2746,16 +2629,16 @@ class Worksheet implements IComparable
     /**
      * Get comment for cell.
      *
-     * @param array<int>|CellAddress|string $cellCoordinate Coordinate of the cell as a string, eg: 'C5';
+     * @param array{0: int, 1: int}|CellAddress|string $cellCoordinate Coordinate of the cell as a string, eg: 'C5';
      *               or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
      */
-    public function getComment($cellCoordinate): Comment
+    public function getComment(CellAddress|string|array $cellCoordinate, bool $attachNew = true): Comment
     {
         $cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($cellCoordinate));
 
         if (Coordinate::coordinateIsRange($cellAddress)) {
             throw new Exception('Cell coordinate string can not be a range of cells.');
-        } elseif (strpos($cellAddress, '$') !== false) {
+        } elseif (str_contains($cellAddress, '$')) {
             throw new Exception('Cell coordinate string must not be absolute.');
         } elseif ($cellAddress == '') {
             throw new Exception('Cell coordinate can not be zero-length string.');
@@ -2768,43 +2651,27 @@ class Worksheet implements IComparable
 
         // If not, create a new comment.
         $newComment = new Comment();
-        $this->comments[$cellAddress] = $newComment;
+        if ($attachNew) {
+            $this->comments[$cellAddress] = $newComment;
+        }
 
         return $newComment;
     }
 
-    /**
-     * Get comment for cell by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the getComment() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::getComment()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     */
-    public function getCommentByColumnAndRow($columnIndex, $row): Comment
-    {
-        return $this->getComment(Coordinate::stringFromColumnIndex($columnIndex) . $row);
-    }
-
     /**
      * Get active cell.
      *
      * @return string Example: 'A1'
      */
-    public function getActiveCell()
+    public function getActiveCell(): string
     {
         return $this->activeCell;
     }
 
     /**
      * Get selected cells.
-     *
-     * @return string
      */
-    public function getSelectedCells()
+    public function getSelectedCells(): string
     {
         return $this->selectedCells;
     }
@@ -2816,7 +2683,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setSelectedCell($coordinate)
+    public function setSelectedCell(string $coordinate): static
     {
         return $this->setSelectedCells($coordinate);
     }
@@ -2824,13 +2691,13 @@ class Worksheet implements IComparable
     /**
      * Select a range of cells.
      *
-     * @param AddressRange|array<int>|CellAddress|int|string $coordinate A simple string containing a Cell range like 'A1:E10'
+     * @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $coordinate A simple string containing a Cell range like 'A1:E10'
      *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
      *              or a CellAddress or AddressRange object.
      *
      * @return $this
      */
-    public function setSelectedCells($coordinate)
+    public function setSelectedCells(AddressRange|CellAddress|int|string|array $coordinate): static
     {
         if (is_string($coordinate)) {
             $coordinate = Validations::definedNameToCoordinate($coordinate, $this);
@@ -2844,34 +2711,34 @@ class Worksheet implements IComparable
             $this->activeCell = $coordinate;
         }
         $this->selectedCells = $coordinate;
+        $this->setSelectedCellsActivePane();
 
         return $this;
     }
 
-    /**
-     * Selected cell by using numeric cell coordinates.
-     *
-     * @deprecated 1.23.0
-     *      Use the setSelectedCells() method with a cell address such as 'C5' instead;,
-     *          or passing in an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
-     * @see Worksheet::setSelectedCells()
-     *
-     * @param int $columnIndex Numeric column coordinate of the cell
-     * @param int $row Numeric row coordinate of the cell
-     *
-     * @return $this
-     */
-    public function setSelectedCellByColumnAndRow($columnIndex, $row)
+    private function setSelectedCellsActivePane(): void
     {
-        return $this->setSelectedCells(Coordinate::stringFromColumnIndex($columnIndex) . $row);
+        if (!empty($this->freezePane)) {
+            $coordinateC = Coordinate::indexesFromString($this->freezePane);
+            $coordinateT = Coordinate::indexesFromString($this->activeCell);
+            if ($coordinateC[0] === 1) {
+                $activePane = ($coordinateT[1] <= $coordinateC[1]) ? 'topLeft' : 'bottomLeft';
+            } elseif ($coordinateC[1] === 1) {
+                $activePane = ($coordinateT[0] <= $coordinateC[0]) ? 'topLeft' : 'topRight';
+            } elseif ($coordinateT[1] <= $coordinateC[1]) {
+                $activePane = ($coordinateT[0] <= $coordinateC[0]) ? 'topLeft' : 'topRight';
+            } else {
+                $activePane = ($coordinateT[0] <= $coordinateC[0]) ? 'bottomLeft' : 'bottomRight';
+            }
+            $this->setActivePane($activePane);
+            $this->panes[$activePane] = new Pane($activePane, $this->selectedCells, $this->activeCell);
+        }
     }
 
     /**
      * Get right-to-left.
-     *
-     * @return bool
      */
-    public function getRightToLeft()
+    public function getRightToLeft(): bool
     {
         return $this->rightToLeft;
     }
@@ -2883,7 +2750,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setRightToLeft($value)
+    public function setRightToLeft(bool $value): static
     {
         $this->rightToLeft = $value;
 
@@ -2900,7 +2767,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function fromArray(array $source, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
+    public function fromArray(array $source, mixed $nullValue = null, string $startCell = 'A1', bool $strictNullComparison = false): static
     {
         //    Convert a 1-D array to 2-D (for ease of looping)
         if (!is_array(end($source))) {
@@ -2933,74 +2800,177 @@ class Worksheet implements IComparable
         return $this;
     }
 
+    /**
+     * @param null|bool|float|int|RichText|string $nullValue value to use when null
+     *
+     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Calculation\Exception
+     */
+    protected function cellToArray(Cell $cell, bool $calculateFormulas, bool $formatData, mixed $nullValue): mixed
+    {
+        $returnValue = $nullValue;
+
+        if ($cell->getValue() !== null) {
+            if ($cell->getValue() instanceof RichText) {
+                $returnValue = $cell->getValue()->getPlainText();
+            } else {
+                $returnValue = ($calculateFormulas) ? $cell->getCalculatedValue() : $cell->getValue();
+            }
+
+            if ($formatData) {
+                $style = $this->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex());
+                /** @var null|bool|float|int|RichText|string */
+                $returnValuex = $returnValue;
+                $returnValue = NumberFormat::toFormattedString(
+                    $returnValuex,
+                    $style->getNumberFormat()->getFormatCode() ?? NumberFormat::FORMAT_GENERAL
+                );
+            }
+        }
+
+        return $returnValue;
+    }
+
     /**
      * Create array from a range of cells.
      *
-     * @param string $range Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
-     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param null|bool|float|int|RichText|string $nullValue Value returned in the array entry if a cell doesn't exist
      * @param bool $calculateFormulas Should formulas be calculated?
      * @param bool $formatData Should formatting be applied to cell values?
      * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
-     *                               True - Return rows and columns indexed by their actual row and column IDs
-     *
-     * @return array
+     *                             True - Return rows and columns indexed by their actual row and column IDs
+     * @param bool $ignoreHidden False - Return values for rows/columns even if they are defined as hidden.
+     *                            True - Don't return values for rows/columns that are defined as hidden.
      */
-    public function rangeToArray($range, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
-    {
-        // Returnvalue
+    public function rangeToArray(
+        string $range,
+        mixed $nullValue = null,
+        bool $calculateFormulas = true,
+        bool $formatData = true,
+        bool $returnCellRef = false,
+        bool $ignoreHidden = false
+    ): array {
         $returnValue = [];
+
+        // Loop through rows
+        foreach ($this->rangeToArrayYieldRows($range, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden) as $rowRef => $rowArray) {
+            $returnValue[$rowRef] = $rowArray;
+        }
+
+        // Return
+        return $returnValue;
+    }
+
+    /**
+     * Create array from a range of cells, yielding each row in turn.
+     *
+     * @param null|bool|float|int|RichText|string $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param bool $calculateFormulas Should formulas be calculated?
+     * @param bool $formatData Should formatting be applied to cell values?
+     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+     *                             True - Return rows and columns indexed by their actual row and column IDs
+     * @param bool $ignoreHidden False - Return values for rows/columns even if they are defined as hidden.
+     *                            True - Don't return values for rows/columns that are defined as hidden.
+     *
+     * @return Generator<array>
+     */
+    public function rangeToArrayYieldRows(
+        string $range,
+        mixed $nullValue = null,
+        bool $calculateFormulas = true,
+        bool $formatData = true,
+        bool $returnCellRef = false,
+        bool $ignoreHidden = false
+    ) {
+        $range = Validations::validateCellOrCellRange($range);
+
         //    Identify the range that we need to extract from the worksheet
         [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($range);
         $minCol = Coordinate::stringFromColumnIndex($rangeStart[0]);
         $minRow = $rangeStart[1];
         $maxCol = Coordinate::stringFromColumnIndex($rangeEnd[0]);
         $maxRow = $rangeEnd[1];
+        $minColInt = $rangeStart[0];
+        $maxColInt = $rangeEnd[0];
 
         ++$maxCol;
+        /** @var array<string, bool> */
+        $hiddenColumns = [];
+        $nullRow = $this->buildNullRow($nullValue, $minCol, $maxCol, $returnCellRef, $ignoreHidden, $hiddenColumns);
+        $hideColumns = !empty($hiddenColumns);
+
+        $keys = $this->cellCollection->getSortedCoordinatesInt();
+        $keyIndex = 0;
+        $keysCount = count($keys);
         // Loop through rows
-        $r = -1;
         for ($row = $minRow; $row <= $maxRow; ++$row) {
-            $rRef = $returnCellRef ? $row : ++$r;
-            $c = -1;
-            // Loop through columns in the current row
-            for ($col = $minCol; $col != $maxCol; ++$col) {
-                $cRef = $returnCellRef ? $col : ++$c;
-                //    Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
-                //        so we test and retrieve directly against cellCollection
-                if ($this->cellCollection->has($col . $row)) {
-                    // Cell exists
-                    $cell = $this->cellCollection->get($col . $row);
-                    if ($cell->getValue() !== null) {
-                        if ($cell->getValue() instanceof RichText) {
-                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
-                        } else {
-                            if ($calculateFormulas) {
-                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
-                            } else {
-                                $returnValue[$rRef][$cRef] = $cell->getValue();
+            if (($ignoreHidden === true) && ($this->isRowVisible($row) === false)) {
+                continue;
+            }
+            $rowRef = $returnCellRef ? $row : ($row - $minRow);
+            $returnValue = $nullRow;
+
+            $index = ($row - 1) * AddressRange::MAX_COLUMN_INT + 1;
+            $indexPlus = $index + AddressRange::MAX_COLUMN_INT - 1;
+            while ($keyIndex < $keysCount && $keys[$keyIndex] < $index) {
+                ++$keyIndex;
+            }
+            while ($keyIndex < $keysCount && $keys[$keyIndex] <= $indexPlus) {
+                $key = $keys[$keyIndex];
+                $thisRow = intdiv($key - 1, AddressRange::MAX_COLUMN_INT) + 1;
+                $thisCol = ($key % AddressRange::MAX_COLUMN_INT) ?: AddressRange::MAX_COLUMN_INT;
+                if ($thisCol >= $minColInt && $thisCol <= $maxColInt) {
+                    $col = Coordinate::stringFromColumnIndex($thisCol);
+                    if ($hideColumns === false || !isset($hiddenColumns[$col])) {
+                        $columnRef = $returnCellRef ? $col : ($thisCol - $minColInt);
+                        $cell = $this->cellCollection->get("{$col}{$thisRow}");
+                        if ($cell !== null) {
+                            $value = $this->cellToArray($cell, $calculateFormulas, $formatData, $nullValue);
+                            if ($value !== $nullValue) {
+                                $returnValue[$columnRef] = $value;
                             }
                         }
-
-                        if ($formatData) {
-                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
-                            $returnValue[$rRef][$cRef] = NumberFormat::toFormattedString(
-                                $returnValue[$rRef][$cRef],
-                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : NumberFormat::FORMAT_GENERAL
-                            );
-                        }
-                    } else {
-                        // Cell holds a NULL
-                        $returnValue[$rRef][$cRef] = $nullValue;
                     }
-                } else {
-                    // Cell doesn't exist
-                    $returnValue[$rRef][$cRef] = $nullValue;
                 }
+                ++$keyIndex;
+            }
+
+            yield $rowRef => $returnValue;
+        }
+    }
+
+    /**
+     * Prepare a row data filled with null values to deduplicate the memory areas for empty rows.
+     *
+     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param string $minCol Start column of the range
+     * @param string $maxCol End column of the range
+     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+     *                              True - Return rows and columns indexed by their actual row and column IDs
+     * @param bool $ignoreHidden False - Return values for rows/columns even if they are defined as hidden.
+     *                             True - Don't return values for rows/columns that are defined as hidden.
+     * @param array<string, bool> $hiddenColumns
+     */
+    private function buildNullRow(
+        mixed $nullValue,
+        string $minCol,
+        string $maxCol,
+        bool $returnCellRef,
+        bool $ignoreHidden,
+        array &$hiddenColumns
+    ): array {
+        $nullRow = [];
+        $c = -1;
+        for ($col = $minCol; $col !== $maxCol; ++$col) {
+            if ($ignoreHidden === true && $this->columnDimensionExists($col) && $this->getColumnDimension($col)->getVisible() === false) {
+                $hiddenColumns[$col] = true;
+            } else {
+                $columnRef = $returnCellRef ? $col : ++$c;
+                $nullRow[$columnRef] = $nullValue;
             }
         }
 
-        // Return
-        return $returnValue;
+        return $nullRow;
     }
 
     private function validateNamedRange(string $definedName, bool $returnNullIfInvalid = false): ?DefinedName
@@ -3022,14 +2992,17 @@ class Worksheet implements IComparable
             throw new Exception('Defined Named ' . $definedName . ' is a formula, not a range or cell.');
         }
 
-        if ($namedRange->getLocalOnly() && $this->getHashCode() !== $namedRange->getWorksheet()->getHashCode()) {
-            if ($returnNullIfInvalid) {
-                return null;
-            }
+        if ($namedRange->getLocalOnly()) {
+            $worksheet = $namedRange->getWorksheet();
+            if ($worksheet === null || $this->getHashCode() !== $worksheet->getHashCode()) {
+                if ($returnNullIfInvalid) {
+                    return null;
+                }
 
-            throw new Exception(
-                'Named range ' . $definedName . ' is not accessible from within sheet ' . $this->getTitle()
-            );
+                throw new Exception(
+                    'Named range ' . $definedName . ' is not accessible from within sheet ' . $this->getTitle()
+                );
+            }
         }
 
         return $namedRange;
@@ -3039,38 +3012,54 @@ class Worksheet implements IComparable
      * Create array from a range of cells.
      *
      * @param string $definedName The Named Range that should be returned
-     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param null|bool|float|int|RichText|string $nullValue Value returned in the array entry if a cell doesn't exist
      * @param bool $calculateFormulas Should formulas be calculated?
      * @param bool $formatData Should formatting be applied to cell values?
      * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
-     *                                True - Return rows and columns indexed by their actual row and column IDs
-     *
-     * @return array
+     *                             True - Return rows and columns indexed by their actual row and column IDs
+     * @param bool $ignoreHidden False - Return values for rows/columns even if they are defined as hidden.
+     *                            True - Don't return values for rows/columns that are defined as hidden.
      */
-    public function namedRangeToArray(string $definedName, $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
-    {
+    public function namedRangeToArray(
+        string $definedName,
+        mixed $nullValue = null,
+        bool $calculateFormulas = true,
+        bool $formatData = true,
+        bool $returnCellRef = false,
+        bool $ignoreHidden = false
+    ): array {
+        $retVal = [];
         $namedRange = $this->validateNamedRange($definedName);
-        $workSheet = $namedRange->getWorksheet();
-        /** @phpstan-ignore-next-line */
-        $cellRange = ltrim(substr($namedRange->getValue(), strrpos($namedRange->getValue(), '!')), '!');
-        $cellRange = str_replace('$', '', $cellRange);
+        if ($namedRange !== null) {
+            $cellRange = ltrim(substr($namedRange->getValue(), (int) strrpos($namedRange->getValue(), '!')), '!');
+            $cellRange = str_replace('$', '', $cellRange);
+            $workSheet = $namedRange->getWorksheet();
+            if ($workSheet !== null) {
+                $retVal = $workSheet->rangeToArray($cellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden);
+            }
+        }
 
-        return $workSheet->rangeToArray($cellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+        return $retVal;
     }
 
     /**
      * Create array from worksheet.
      *
-     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+     * @param null|bool|float|int|RichText|string $nullValue Value returned in the array entry if a cell doesn't exist
      * @param bool $calculateFormulas Should formulas be calculated?
      * @param bool $formatData Should formatting be applied to cell values?
      * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
-     *                               True - Return rows and columns indexed by their actual row and column IDs
-     *
-     * @return array
+     *                             True - Return rows and columns indexed by their actual row and column IDs
+     * @param bool $ignoreHidden False - Return values for rows/columns even if they are defined as hidden.
+     *                            True - Don't return values for rows/columns that are defined as hidden.
      */
-    public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
-    {
+    public function toArray(
+        mixed $nullValue = null,
+        bool $calculateFormulas = true,
+        bool $formatData = true,
+        bool $returnCellRef = false,
+        bool $ignoreHidden = false
+    ): array {
         // Garbage collect...
         $this->garbageCollect();
 
@@ -3079,18 +3068,16 @@ class Worksheet implements IComparable
         $maxRow = $this->getHighestRow();
 
         // Return
-        return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+        return $this->rangeToArray("A1:{$maxCol}{$maxRow}", $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden);
     }
 
     /**
      * Get row iterator.
      *
      * @param int $startRow The row number at which to start iterating
-     * @param int $endRow The row number at which to stop iterating
-     *
-     * @return RowIterator
+     * @param ?int $endRow The row number at which to stop iterating
      */
-    public function getRowIterator($startRow = 1, $endRow = null)
+    public function getRowIterator(int $startRow = 1, ?int $endRow = null): RowIterator
     {
         return new RowIterator($this, $startRow, $endRow);
     }
@@ -3099,11 +3086,9 @@ class Worksheet implements IComparable
      * Get column iterator.
      *
      * @param string $startColumn The column address at which to start iterating
-     * @param string $endColumn The column address at which to stop iterating
-     *
-     * @return ColumnIterator
+     * @param ?string $endColumn The column address at which to stop iterating
      */
-    public function getColumnIterator($startColumn = 'A', $endColumn = null)
+    public function getColumnIterator(string $startColumn = 'A', ?string $endColumn = null): ColumnIterator
     {
         return new ColumnIterator($this, $startColumn, $endColumn);
     }
@@ -3113,7 +3098,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function garbageCollect()
+    public function garbageCollect(): static
     {
         // Flush cache
         $this->cellCollection->get('A1');
@@ -3150,7 +3135,7 @@ class Worksheet implements IComparable
      *
      * @return string Hash code
      */
-    public function getHashCode()
+    public function getHashCode(): string
     {
         if ($this->dirty) {
             $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
@@ -3170,12 +3155,12 @@ class Worksheet implements IComparable
      * Example: extractSheetTitle("A1", true) ==> ['', 'A1'];
      * Example: extractSheetTitle("A1:C3", true) ==> ['', 'A1:C3']
      *
-     * @param string $range Range to extract title from
+     * @param ?string $range Range to extract title from
      * @param bool $returnRange Return range? (see example)
      *
-     * @return mixed
+     * @return ($range is non-empty-string ? ($returnRange is true ? array{0: string, 1: string} : string) : ($returnRange is true ? array{0: null, 1: null} : null))
      */
-    public static function extractSheetTitle($range, $returnRange = false)
+    public static function extractSheetTitle(?string $range, bool $returnRange = false): array|null|string
     {
         if (empty($range)) {
             return $returnRange ? [null, null] : null;
@@ -3197,10 +3182,8 @@ class Worksheet implements IComparable
      * Get hyperlink.
      *
      * @param string $cellCoordinate Cell coordinate to get hyperlink for, eg: 'A1'
-     *
-     * @return Hyperlink
      */
-    public function getHyperlink($cellCoordinate)
+    public function getHyperlink(string $cellCoordinate): Hyperlink
     {
         // return hyperlink if we already have one
         if (isset($this->hyperlinkCollection[$cellCoordinate])) {
@@ -3220,7 +3203,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setHyperlink($cellCoordinate, ?Hyperlink $hyperlink = null)
+    public function setHyperlink(string $cellCoordinate, ?Hyperlink $hyperlink = null): static
     {
         if ($hyperlink === null) {
             unset($this->hyperlinkCollection[$cellCoordinate]);
@@ -3235,10 +3218,8 @@ class Worksheet implements IComparable
      * Hyperlink at a specific coordinate exists?
      *
      * @param string $coordinate eg: 'A1'
-     *
-     * @return bool
      */
-    public function hyperlinkExists($coordinate)
+    public function hyperlinkExists(string $coordinate): bool
     {
         return isset($this->hyperlinkCollection[$coordinate]);
     }
@@ -3248,7 +3229,7 @@ class Worksheet implements IComparable
      *
      * @return Hyperlink[]
      */
-    public function getHyperlinkCollection()
+    public function getHyperlinkCollection(): array
     {
         return $this->hyperlinkCollection;
     }
@@ -3257,10 +3238,8 @@ class Worksheet implements IComparable
      * Get data validation.
      *
      * @param string $cellCoordinate Cell coordinate to get data validation for, eg: 'A1'
-     *
-     * @return DataValidation
      */
-    public function getDataValidation($cellCoordinate)
+    public function getDataValidation(string $cellCoordinate): DataValidation
     {
         // return data validation if we already have one
         if (isset($this->dataValidationCollection[$cellCoordinate])) {
@@ -3280,7 +3259,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setDataValidation($cellCoordinate, ?DataValidation $dataValidation = null)
+    public function setDataValidation(string $cellCoordinate, ?DataValidation $dataValidation = null): static
     {
         if ($dataValidation === null) {
             unset($this->dataValidationCollection[$cellCoordinate]);
@@ -3295,10 +3274,8 @@ class Worksheet implements IComparable
      * Data validation at a specific coordinate exists?
      *
      * @param string $coordinate eg: 'A1'
-     *
-     * @return bool
      */
-    public function dataValidationExists($coordinate)
+    public function dataValidationExists(string $coordinate): bool
     {
         return isset($this->dataValidationCollection[$coordinate]);
     }
@@ -3308,7 +3285,7 @@ class Worksheet implements IComparable
      *
      * @return DataValidation[]
      */
-    public function getDataValidationCollection()
+    public function getDataValidationCollection(): array
     {
         return $this->dataValidationCollection;
     }
@@ -3316,11 +3293,9 @@ class Worksheet implements IComparable
     /**
      * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet.
      *
-     * @param string $range
-     *
      * @return string Adjusted range value
      */
-    public function shrinkRangeToFit($range)
+    public function shrinkRangeToFit(string $range): string
     {
         $maxCol = $this->getHighestColumn();
         $maxRow = $this->getHighestRow();
@@ -3351,10 +3326,8 @@ class Worksheet implements IComparable
 
     /**
      * Get tab color.
-     *
-     * @return Color
      */
-    public function getTabColor()
+    public function getTabColor(): Color
     {
         if ($this->tabColor === null) {
             $this->tabColor = new Color();
@@ -3368,7 +3341,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function resetTabColor()
+    public function resetTabColor(): static
     {
         $this->tabColor = null;
 
@@ -3377,20 +3350,16 @@ class Worksheet implements IComparable
 
     /**
      * Tab color set?
-     *
-     * @return bool
      */
-    public function isTabColorSet()
+    public function isTabColorSet(): bool
     {
         return $this->tabColor !== null;
     }
 
     /**
      * Copy worksheet (!= clone!).
-     *
-     * @return static
      */
-    public function copy()
+    public function copy(): static
     {
         return clone $this;
     }
@@ -3418,7 +3387,7 @@ class Worksheet implements IComparable
             $iterator = new RowIterator($this, $rowId, $rowId);
             $iterator->seek($rowId);
             $row = $iterator->current();
-        } catch (Exception $e) {
+        } catch (Exception) {
             return true;
         }
 
@@ -3448,7 +3417,7 @@ class Worksheet implements IComparable
             $iterator = new ColumnIterator($this, $columnId, $columnId);
             $iterator->seek($columnId);
             $column = $iterator->current();
-        } catch (Exception $e) {
+        } catch (Exception) {
             return true;
         }
 
@@ -3474,10 +3443,23 @@ class Worksheet implements IComparable
                     $currentCollection = $this->drawingCollection;
                     $this->drawingCollection = new ArrayObject();
                     foreach ($currentCollection as $item) {
-                        if (is_object($item)) {
-                            $newDrawing = clone $item;
-                            $newDrawing->setWorksheet($this);
-                        }
+                        $newDrawing = clone $item;
+                        $newDrawing->setWorksheet($this);
+                    }
+                } elseif ($key == 'tableCollection') {
+                    $currentCollection = $this->tableCollection;
+                    $this->tableCollection = new ArrayObject();
+                    foreach ($currentCollection as $item) {
+                        $newTable = clone $item;
+                        $newTable->setName($item->getName() . 'clone');
+                        $this->addTable($newTable);
+                    }
+                } elseif ($key == 'chartCollection') {
+                    $currentCollection = $this->chartCollection;
+                    $this->chartCollection = new ArrayObject();
+                    foreach ($currentCollection as $item) {
+                        $newChart = clone $item;
+                        $this->addChart($newChart);
                     }
                 } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) {
                     $newAutoFilter = clone $this->autoFilter;
@@ -3500,7 +3482,7 @@ class Worksheet implements IComparable
      *
      * @return $this
      */
-    public function setCodeName($codeName, $validate = true)
+    public function setCodeName(string $codeName, bool $validate = true): static
     {
         // Is this a 'rename' or not?
         if ($this->getCodeName() == $codeName) {
@@ -3516,16 +3498,16 @@ class Worksheet implements IComparable
 
             // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
 
-            if ($this->getParent()) {
+            if ($this->parent !== null) {
                 // Is there already such sheet name?
-                if ($this->getParent()->sheetCodeNameExists($codeName)) {
+                if ($this->parent->sheetCodeNameExists($codeName)) {
                     // Use name, but append with lowest possible integer
 
                     if (Shared\StringHelper::countCharacters($codeName) > 29) {
                         $codeName = Shared\StringHelper::substring($codeName, 0, 29);
                     }
                     $i = 1;
-                    while ($this->getParent()->sheetCodeNameExists($codeName . '_' . $i)) {
+                    while ($this->getParentOrThrow()->sheetCodeNameExists($codeName . '_' . $i)) {
                         ++$i;
                         if ($i == 10) {
                             if (Shared\StringHelper::countCharacters($codeName) > 28) {
@@ -3550,20 +3532,16 @@ class Worksheet implements IComparable
 
     /**
      * Return the code name of the sheet.
-     *
-     * @return null|string
      */
-    public function getCodeName()
+    public function getCodeName(): ?string
     {
         return $this->codeName;
     }
 
     /**
      * Sheet has a code name ?
-     *
-     * @return bool
      */
-    public function hasCodeName()
+    public function hasCodeName(): bool
     {
         return $this->codeName !== null;
     }
@@ -3572,4 +3550,146 @@ class Worksheet implements IComparable
     {
         return preg_match(self::SHEET_NAME_REQUIRES_NO_QUOTES, $sheetName) !== 1;
     }
+
+    public function isRowVisible(int $row): bool
+    {
+        return !$this->rowDimensionExists($row) || $this->getRowDimension($row)->getVisible();
+    }
+
+    /**
+     * Same as Cell->isLocked, but without creating cell if it doesn't exist.
+     */
+    public function isCellLocked(string $coordinate): bool
+    {
+        if ($this->getProtection()->getsheet() !== true) {
+            return false;
+        }
+        if ($this->cellExists($coordinate)) {
+            return $this->getCell($coordinate)->isLocked();
+        }
+        $spreadsheet = $this->parent;
+        $xfIndex = $this->getXfIndex($coordinate);
+        if ($spreadsheet === null || $xfIndex === null) {
+            return true;
+        }
+
+        return $spreadsheet->getCellXfByIndex($xfIndex)->getProtection()->getLocked() !== StyleProtection::PROTECTION_UNPROTECTED;
+    }
+
+    /**
+     * Same as Cell->isHiddenOnFormulaBar, but without creating cell if it doesn't exist.
+     */
+    public function isCellHiddenOnFormulaBar(string $coordinate): bool
+    {
+        if ($this->cellExists($coordinate)) {
+            return $this->getCell($coordinate)->isHiddenOnFormulaBar();
+        }
+
+        // cell doesn't exist, therefore isn't a formula,
+        // therefore isn't hidden on formula bar.
+        return false;
+    }
+
+    private function getXfIndex(string $coordinate): ?int
+    {
+        [$column, $row] = Coordinate::coordinateFromString($coordinate);
+        $row = (int) $row;
+        $xfIndex = null;
+        if ($this->rowDimensionExists($row)) {
+            $xfIndex = $this->getRowDimension($row)->getXfIndex();
+        }
+        if ($xfIndex === null && $this->ColumnDimensionExists($column)) {
+            $xfIndex = $this->getColumnDimension($column)->getXfIndex();
+        }
+
+        return $xfIndex;
+    }
+
+    private string $backgroundImage = '';
+
+    private string $backgroundMime = '';
+
+    private string $backgroundExtension = '';
+
+    public function getBackgroundImage(): string
+    {
+        return $this->backgroundImage;
+    }
+
+    public function getBackgroundMime(): string
+    {
+        return $this->backgroundMime;
+    }
+
+    public function getBackgroundExtension(): string
+    {
+        return $this->backgroundExtension;
+    }
+
+    /**
+     * Set background image.
+     * Used on read/write for Xlsx.
+     * Used on write for Html.
+     *
+     * @param string $backgroundImage Image represented as a string, e.g. results of file_get_contents
+     */
+    public function setBackgroundImage(string $backgroundImage): self
+    {
+        $imageArray = getimagesizefromstring($backgroundImage) ?: ['mime' => ''];
+        $mime = $imageArray['mime'];
+        if ($mime !== '') {
+            $extension = explode('/', $mime);
+            $extension = $extension[1];
+            $this->backgroundImage = $backgroundImage;
+            $this->backgroundMime = $mime;
+            $this->backgroundExtension = $extension;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Copy cells, adjusting relative cell references in formulas.
+     * Acts similarly to Excel "fill handle" feature.
+     *
+     * @param string $fromCell Single source cell, e.g. C3
+     * @param string $toCells Single cell or cell range, e.g. C4 or C4:C10
+     * @param bool $copyStyle Copy styles as well as values, defaults to true
+     */
+    public function copyCells(string $fromCell, string $toCells, bool $copyStyle = true): void
+    {
+        $toArray = Coordinate::extractAllCellReferencesInRange($toCells);
+        $valueString = $this->getCell($fromCell)->getValueString();
+        $style = $this->getStyle($fromCell)->exportArray();
+        $fromIndexes = Coordinate::indexesFromString($fromCell);
+        $referenceHelper = ReferenceHelper::getInstance();
+        foreach ($toArray as $destination) {
+            if ($destination !== $fromCell) {
+                $toIndexes = Coordinate::indexesFromString($destination);
+                $this->getCell($destination)->setValue($referenceHelper->updateFormulaReferences($valueString, 'A1', $toIndexes[0] - $fromIndexes[0], $toIndexes[1] - $fromIndexes[1]));
+                if ($copyStyle) {
+                    $this->getCell($destination)->getStyle()->applyFromArray($style);
+                }
+            }
+        }
+    }
+
+    public function applyStylesFromArray(string $coordinate, array $styleArray): bool
+    {
+        $spreadsheet = $this->parent;
+        if ($spreadsheet === null) {
+            return false;
+        }
+        $activeSheetIndex = $spreadsheet->getActiveSheetIndex();
+        $originalSelected = $this->selectedCells;
+        $originalActive = $this->activeCell;
+        $this->getStyle($coordinate)->applyFromArray($styleArray);
+        $this->activeCell = $originalActive;
+        $this->selectedCells = $originalSelected;
+        if ($activeSheetIndex >= 0) {
+            $spreadsheet->setActiveSheetIndex($activeSheetIndex);
+        }
+
+        return true;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php
index f903e93..3883244 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php
@@ -7,74 +7,63 @@ abstract class BaseWriter implements IWriter
     /**
      * Write charts that are defined in the workbook?
      * Identifies whether the Writer should write definitions for any charts that exist in the PhpSpreadsheet object.
-     *
-     * @var bool
      */
-    protected $includeCharts = false;
+    protected bool $includeCharts = false;
 
     /**
      * Pre-calculate formulas
      * Forces PhpSpreadsheet to recalculate all formulae in a workbook when saving, so that the pre-calculated values are
      * immediately available to MS Excel or other office spreadsheet viewer when opening the file.
-     *
-     * @var bool
      */
-    protected $preCalculateFormulas = true;
+    protected bool $preCalculateFormulas = true;
 
     /**
      * Use disk caching where possible?
-     *
-     * @var bool
      */
-    private $useDiskCaching = false;
+    private bool $useDiskCaching = false;
 
     /**
      * Disk caching directory.
-     *
-     * @var string
      */
-    private $diskCachingDirectory = './';
+    private string $diskCachingDirectory = './';
 
     /**
      * @var resource
      */
     protected $fileHandle;
 
-    /**
-     * @var bool
-     */
-    private $shouldCloseFile;
+    private bool $shouldCloseFile;
 
-    public function getIncludeCharts()
+    public function getIncludeCharts(): bool
     {
         return $this->includeCharts;
     }
 
-    public function setIncludeCharts($includeCharts)
+    public function setIncludeCharts(bool $includeCharts): self
     {
-        $this->includeCharts = (bool) $includeCharts;
+        $this->includeCharts = $includeCharts;
 
         return $this;
     }
 
-    public function getPreCalculateFormulas()
+    public function getPreCalculateFormulas(): bool
     {
         return $this->preCalculateFormulas;
     }
 
-    public function setPreCalculateFormulas($precalculateFormulas)
+    public function setPreCalculateFormulas(bool $precalculateFormulas): self
     {
-        $this->preCalculateFormulas = (bool) $precalculateFormulas;
+        $this->preCalculateFormulas = $precalculateFormulas;
 
         return $this;
     }
 
-    public function getUseDiskCaching()
+    public function getUseDiskCaching(): bool
     {
         return $this->useDiskCaching;
     }
 
-    public function setUseDiskCaching($useDiskCache, $cacheDirectory = null)
+    public function setUseDiskCaching(bool $useDiskCache, ?string $cacheDirectory = null): self
     {
         $this->useDiskCaching = $useDiskCache;
 
@@ -89,7 +78,7 @@ abstract class BaseWriter implements IWriter
         return $this;
     }
 
-    public function getDiskCachingDirectory()
+    public function getDiskCachingDirectory(): string
     {
         return $this->diskCachingDirectory;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
index 8f0ceda..de7de30 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Csv.php
@@ -3,73 +3,64 @@
 namespace PhpOffice\PhpSpreadsheet\Writer;
 
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use Stringable;
 
 class Csv extends BaseWriter
 {
     /**
      * PhpSpreadsheet object.
-     *
-     * @var Spreadsheet
      */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     /**
      * Delimiter.
-     *
-     * @var string
      */
-    private $delimiter = ',';
+    private string $delimiter = ',';
 
     /**
      * Enclosure.
-     *
-     * @var string
      */
-    private $enclosure = '"';
+    private string $enclosure = '"';
 
     /**
      * Line ending.
-     *
-     * @var string
      */
-    private $lineEnding = PHP_EOL;
+    private string $lineEnding = PHP_EOL;
 
     /**
      * Sheet index to write.
-     *
-     * @var int
      */
-    private $sheetIndex = 0;
+    private int $sheetIndex = 0;
 
     /**
      * Whether to write a UTF8 BOM.
-     *
-     * @var bool
      */
-    private $useBOM = false;
+    private bool $useBOM = false;
 
     /**
      * Whether to write a Separator line as the first line of the file
      *     sep=x.
-     *
-     * @var bool
      */
-    private $includeSeparatorLine = false;
+    private bool $includeSeparatorLine = false;
 
     /**
      * Whether to write a fully Excel compatible CSV file.
-     *
-     * @var bool
      */
-    private $excelCompatibility = false;
+    private bool $excelCompatibility = false;
 
     /**
      * Output encoding.
-     *
-     * @var string
      */
-    private $outputEncoding = '';
+    private string $outputEncoding = '';
+
+    /**
+     * Whether number of columns should be allowed to vary
+     * between rows, or use a fixed range based on the max
+     * column overall.
+     */
+    private bool $variableColumns = false;
 
     /**
      * Create a new CSV.
@@ -122,11 +113,18 @@ class Csv extends BaseWriter
         $maxRow = $sheet->getHighestDataRow();
 
         // Write rows to file
-        for ($row = 1; $row <= $maxRow; ++$row) {
-            // Convert the row to an array...
-            $cellsArray = $sheet->rangeToArray('A' . $row . ':' . $maxCol . $row, '', $this->preCalculateFormulas);
-            // ... and write to the file
-            $this->writeLine($this->fileHandle, $cellsArray[0]);
+        $row = 0;
+        foreach ($sheet->rangeToArrayYieldRows("A1:$maxCol$maxRow", '', $this->preCalculateFormulas) as $cellsArray) {
+            ++$row;
+            if ($this->variableColumns) {
+                $column = $sheet->getHighestDataColumn($row);
+                if ($column === 'A' && !$sheet->cellExists("A$row")) {
+                    $cellsArray = [];
+                } else {
+                    array_splice($cellsArray, Coordinate::columnIndexFromString($column));
+                }
+            }
+            $this->writeLine($this->fileHandle, $cellsArray);
         }
 
         $this->maybeCloseFileHandle();
@@ -251,8 +249,7 @@ class Csv extends BaseWriter
         return $this;
     }
 
-    /** @var bool */
-    private $enclosureRequired = true;
+    private bool $enclosureRequired = true;
 
     public function setEnclosureRequired(bool $value): self
     {
@@ -269,9 +266,9 @@ class Csv extends BaseWriter
     /**
      * Convert boolean to TRUE/FALSE; otherwise return element cast to string.
      *
-     * @param mixed $element
+     * @param null|bool|float|int|string|Stringable $element element to be converted
      */
-    private static function elementToString($element): string
+    private static function elementToString(mixed $element): string
     {
         if (is_bool($element)) {
             return $element ? 'TRUE' : 'FALSE';
@@ -294,6 +291,7 @@ class Csv extends BaseWriter
         // Build the line
         $line = '';
 
+        /** @var null|bool|float|int|string|Stringable $element */
         foreach ($values as $element) {
             $element = self::elementToString($element);
             // Add delimiter
@@ -321,6 +319,28 @@ class Csv extends BaseWriter
         if ($this->outputEncoding != '') {
             $line = mb_convert_encoding($line, $this->outputEncoding);
         }
-        fwrite($fileHandle, /** @scrutinizer ignore-type */ $line);
+        fwrite($fileHandle, $line);
+    }
+
+    /**
+     * Get whether number of columns should be allowed to vary
+     * between rows, or use a fixed range based on the max
+     * column overall.
+     */
+    public function getVariableColumns(): bool
+    {
+        return $this->variableColumns;
+    }
+
+    /**
+     * Set whether number of columns should be allowed to vary
+     * between rows, or use a fixed range based on the max
+     * column overall.
+     */
+    public function setVariableColumns(bool $pValue): self
+    {
+        $this->variableColumns = $pValue;
+
+        return $this;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
index 0fef0f6..37a9c89 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Html.php
@@ -2,14 +2,16 @@
 
 namespace PhpOffice\PhpSpreadsheet\Writer;
 
-use HTMLPurifier;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Chart\Chart;
+use PhpOffice\PhpSpreadsheet\Comment;
+use PhpOffice\PhpSpreadsheet\Document\Properties;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\RichText\Run;
 use PhpOffice\PhpSpreadsheet\Settings;
+use PhpOffice\PhpSpreadsheet\Shared\Date;
 use PhpOffice\PhpSpreadsheet\Shared\Drawing as SharedDrawing;
 use PhpOffice\PhpSpreadsheet\Shared\File;
 use PhpOffice\PhpSpreadsheet\Shared\Font as SharedFont;
@@ -22,6 +24,7 @@ use PhpOffice\PhpSpreadsheet\Style\Fill;
 use PhpOffice\PhpSpreadsheet\Style\Font;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\BaseDrawing;
 use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
 use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
 use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
@@ -29,117 +32,101 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class Html extends BaseWriter
 {
+    private const DEFAULT_CELL_WIDTH_POINTS = 42;
+
+    private const DEFAULT_CELL_WIDTH_PIXELS = 56;
+
+    /**
+     * Migration aid to tell if html tags will be treated as plaintext in comments.
+     *     if (
+     *         defined(
+     *             \PhpOffice\PhpSpreadsheet\Writer\Html::class
+     *             . '::COMMENT_HTML_TAGS_PLAINTEXT'
+     *         )
+     *     ) {
+     *         new logic with styling in TextRun elements
+     *     } else {
+     *         old logic with styling via Html tags
+     *     }.
+     */
+    public const COMMENT_HTML_TAGS_PLAINTEXT = true;
+
     /**
      * Spreadsheet object.
-     *
-     * @var Spreadsheet
      */
-    protected $spreadsheet;
+    protected Spreadsheet $spreadsheet;
 
     /**
      * Sheet index to write.
-     *
-     * @var null|int
      */
-    private $sheetIndex = 0;
+    private ?int $sheetIndex = 0;
 
     /**
      * Images root.
-     *
-     * @var string
      */
-    private $imagesRoot = '';
+    private string $imagesRoot = '';
 
     /**
      * embed images, or link to images.
-     *
-     * @var bool
      */
-    protected $embedImages = false;
+    protected bool $embedImages = false;
 
     /**
      * Use inline CSS?
-     *
-     * @var bool
      */
-    private $useInlineCss = false;
-
-    /**
-     * Use embedded CSS?
-     *
-     * @var bool
-     */
-    private $useEmbeddedCSS = true;
+    private bool $useInlineCss = false;
 
     /**
      * Array of CSS styles.
-     *
-     * @var array
      */
-    private $cssStyles;
+    private ?array $cssStyles = null;
 
     /**
      * Array of column widths in points.
-     *
-     * @var array
      */
-    private $columnWidths;
+    private array $columnWidths;
 
     /**
      * Default font.
-     *
-     * @var Font
      */
-    private $defaultFont;
+    private Font $defaultFont;
 
     /**
      * Flag whether spans have been calculated.
-     *
-     * @var bool
      */
-    private $spansAreCalculated = false;
+    private bool $spansAreCalculated = false;
 
     /**
      * Excel cells that should not be written as HTML cells.
-     *
-     * @var array
      */
-    private $isSpannedCell = [];
+    private array $isSpannedCell = [];
 
     /**
      * Excel cells that are upper-left corner in a cell merge.
-     *
-     * @var array
      */
-    private $isBaseCell = [];
+    private array $isBaseCell = [];
 
     /**
      * Excel rows that should not be written as HTML rows.
-     *
-     * @var array
      */
-    private $isSpannedRow = [];
+    private array $isSpannedRow = [];
 
     /**
      * Is the current writer creating PDF?
-     *
-     * @var bool
      */
-    protected $isPdf = false;
+    protected bool $isPdf = false;
 
     /**
      * Is the current writer creating mPDF?
      *
-     * @var bool
+     * @deprecated 2.0.1 use instanceof Mpdf instead
      */
-    protected $isMPdf = false;
+    protected bool $isMPdf = false;
 
     /**
      * Generate the Navigation block.
-     *
-     * @var bool
      */
-    private $generateSheetNavigationBlock = true;
+    private bool $generateSheetNavigationBlock = true;
 
     /**
      * Callback for editing generated html.
@@ -148,6 +135,12 @@ class Html extends BaseWriter
      */
     private $editHtmlCallback;
 
+    /** @var BaseDrawing[] */
+    private $sheetDrawings;
+
+    /** @var Chart[] */
+    private $sheetCharts;
+
     /**
      * Create a new HTML.
      */
@@ -178,10 +171,8 @@ class Html extends BaseWriter
 
     /**
      * Save Spreadsheet as html to variable.
-     *
-     * @return string
      */
-    public function generateHtmlAll()
+    public function generateHtmlAll(): string
     {
         // garbage collect
         $this->spreadsheet->garbageCollect();
@@ -235,10 +226,8 @@ class Html extends BaseWriter
      * Map VAlign.
      *
      * @param string $vAlign Vertical alignment
-     *
-     * @return string
      */
-    private function mapVAlign($vAlign)
+    private function mapVAlign(string $vAlign): string
     {
         return Alignment::VERTICAL_ALIGNMENT_FOR_HTML[$vAlign] ?? '';
     }
@@ -247,16 +236,15 @@ class Html extends BaseWriter
      * Map HAlign.
      *
      * @param string $hAlign Horizontal alignment
-     *
-     * @return string
      */
-    private function mapHAlign($hAlign)
+    private function mapHAlign(string $hAlign): string
     {
         return Alignment::HORIZONTAL_ALIGNMENT_FOR_HTML[$hAlign] ?? '';
     }
 
+    const BORDER_NONE = 'none';
     const BORDER_ARR = [
-        Border::BORDER_NONE => 'none',
+        Border::BORDER_NONE => self::BORDER_NONE,
         Border::BORDER_DASHDOT => '1px dashed',
         Border::BORDER_DASHDOTDOT => '1px dotted',
         Border::BORDER_DASHED => '1px dashed',
@@ -274,12 +262,10 @@ class Html extends BaseWriter
      * Map border style.
      *
      * @param int|string $borderStyle Sheet index
-     *
-     * @return string
      */
-    private function mapBorderStyle($borderStyle)
+    private function mapBorderStyle($borderStyle): string
     {
-        return array_key_exists($borderStyle, self::BORDER_ARR) ? self::BORDER_ARR[$borderStyle] : '1px solid';
+        return self::BORDER_ARR[$borderStyle] ?? '1px solid';
     }
 
     /**
@@ -297,7 +283,7 @@ class Html extends BaseWriter
      *
      * @return $this
      */
-    public function setSheetIndex($sheetIndex)
+    public function setSheetIndex(int $sheetIndex): static
     {
         $this->sheetIndex = $sheetIndex;
 
@@ -306,10 +292,8 @@ class Html extends BaseWriter
 
     /**
      * Get sheet index.
-     *
-     * @return bool
      */
-    public function getGenerateSheetNavigationBlock()
+    public function getGenerateSheetNavigationBlock(): bool
     {
         return $this->generateSheetNavigationBlock;
     }
@@ -321,7 +305,7 @@ class Html extends BaseWriter
      *
      * @return $this
      */
-    public function setGenerateSheetNavigationBlock($generateSheetNavigationBlock)
+    public function setGenerateSheetNavigationBlock(bool $generateSheetNavigationBlock): static
     {
         $this->generateSheetNavigationBlock = (bool) $generateSheetNavigationBlock;
 
@@ -333,7 +317,7 @@ class Html extends BaseWriter
      *
      * @return $this
      */
-    public function writeAllSheets()
+    public function writeAllSheets(): static
     {
         $this->sheetIndex = null;
 
@@ -342,21 +326,27 @@ class Html extends BaseWriter
 
     private static function generateMeta(?string $val, string $desc): string
     {
-        return $val
+        return ($val || $val === '0')
             ? ('      <meta name="' . $desc . '" content="' . htmlspecialchars($val, Settings::htmlEntityFlags()) . '" />' . PHP_EOL)
             : '';
     }
 
     public const BODY_LINE = '  <body>' . PHP_EOL;
 
+    private const CUSTOM_TO_META = [
+        Properties::PROPERTY_TYPE_BOOLEAN => 'bool',
+        Properties::PROPERTY_TYPE_DATE => 'date',
+        Properties::PROPERTY_TYPE_FLOAT => 'float',
+        Properties::PROPERTY_TYPE_INTEGER => 'int',
+        Properties::PROPERTY_TYPE_STRING => 'string',
+    ];
+
     /**
      * Generate HTML header.
      *
      * @param bool $includeStyles Include styles?
-     *
-     * @return string
      */
-    public function generateHTMLHeader($includeStyles = false)
+    public function generateHTMLHeader(bool $includeStyles = false): string
     {
         // Construct HTML
         $properties = $this->spreadsheet->getProperties();
@@ -365,7 +355,11 @@ class Html extends BaseWriter
         $html .= '  <head>' . PHP_EOL;
         $html .= '      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . PHP_EOL;
         $html .= '      <meta name="generator" content="PhpSpreadsheet, https://github.com/PHPOffice/PhpSpreadsheet" />' . PHP_EOL;
-        $html .= '      <title>' . htmlspecialchars($properties->getTitle(), Settings::htmlEntityFlags()) . '</title>' . PHP_EOL;
+        $title = $properties->getTitle();
+        if ($title === '') {
+            $title = $this->spreadsheet->getActiveSheet()->getTitle();
+        }
+        $html .= '      <title>' . htmlspecialchars($title, Settings::htmlEntityFlags()) . '</title>' . PHP_EOL;
         $html .= self::generateMeta($properties->getCreator(), 'author');
         $html .= self::generateMeta($properties->getTitle(), 'title');
         $html .= self::generateMeta($properties->getDescription(), 'description');
@@ -374,6 +368,37 @@ class Html extends BaseWriter
         $html .= self::generateMeta($properties->getCategory(), 'category');
         $html .= self::generateMeta($properties->getCompany(), 'company');
         $html .= self::generateMeta($properties->getManager(), 'manager');
+        $html .= self::generateMeta($properties->getLastModifiedBy(), 'lastModifiedBy');
+        $html .= self::generateMeta($properties->getViewport(), 'viewport');
+        $date = Date::dateTimeFromTimestamp((string) $properties->getCreated());
+        $date->setTimeZone(Date::getDefaultOrLocalTimeZone());
+        $html .= self::generateMeta($date->format(DATE_W3C), 'created');
+        $date = Date::dateTimeFromTimestamp((string) $properties->getModified());
+        $date->setTimeZone(Date::getDefaultOrLocalTimeZone());
+        $html .= self::generateMeta($date->format(DATE_W3C), 'modified');
+
+        $customProperties = $properties->getCustomProperties();
+        foreach ($customProperties as $customProperty) {
+            $propertyValue = $properties->getCustomPropertyValue($customProperty);
+            $propertyType = $properties->getCustomPropertyType($customProperty);
+            $propertyQualifier = self::CUSTOM_TO_META[$propertyType] ?? null;
+            if ($propertyQualifier !== null) {
+                if ($propertyType === Properties::PROPERTY_TYPE_BOOLEAN) {
+                    $propertyValue = $propertyValue ? '1' : '0';
+                } elseif ($propertyType === Properties::PROPERTY_TYPE_DATE) {
+                    $date = Date::dateTimeFromTimestamp((string) $propertyValue);
+                    $date->setTimeZone(Date::getDefaultOrLocalTimeZone());
+                    $propertyValue = $date->format(DATE_W3C);
+                } else {
+                    $propertyValue = (string) $propertyValue;
+                }
+                $html .= self::generateMeta($propertyValue, "custom.$propertyQualifier.$customProperty");
+            }
+        }
+
+        if (!empty($properties->getHyperlinkBase())) {
+            $html .= '      <base href="' . $properties->getHyperlinkBase() . '" />' . PHP_EOL;
+        }
 
         $html .= $includeStyles ? $this->generateStyles(true) : $this->generatePageDeclarations(true);
 
@@ -433,10 +458,8 @@ class Html extends BaseWriter
 
     /**
      * Generate sheet data.
-     *
-     * @return string
      */
-    public function generateSheetData()
+    public function generateSheetData(): string
     {
         $sheets = $this->generateSheetPrep();
 
@@ -448,11 +471,14 @@ class Html extends BaseWriter
         foreach ($sheets as $sheet) {
             // Write table header
             $html .= $this->generateTableHeader($sheet);
+            $this->sheetCharts = [];
+            $this->sheetDrawings = [];
 
             // Get worksheet dimension
             [$min, $max] = explode(':', $sheet->calculateWorksheetDataDimension());
-            [$minCol, $minRow] = Coordinate::indexesFromString($min);
+            [$minCol, $minRow, $minColString] = Coordinate::indexesFromString($min);
             [$maxCol, $maxRow] = Coordinate::indexesFromString($max);
+            $this->extendRowsAndColumns($sheet, $maxCol, $maxRow);
 
             [$theadStart, $theadEnd, $tbodyStart] = $this->generateSheetStarts($sheet, $minRow);
 
@@ -463,23 +489,26 @@ class Html extends BaseWriter
                 $html .= $startTag;
 
                 // Write row if there are HTML table cells in it
-                if (!isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) {
+                if ($this->shouldGenerateRow($sheet, $row) && !isset($this->isSpannedRow[$sheet->getParent()->getIndex($sheet)][$row])) {
                     // Start a new rowData
                     $rowData = [];
                     // Loop through columns
                     $column = $minCol;
+                    $colStr = $minColString;
                     while ($column <= $maxCol) {
                         // Cell exists?
                         $cellAddress = Coordinate::stringFromColumnIndex($column) . $row;
-                        $rowData[$column++] = ($sheet->getCellCollection()->has($cellAddress)) ? $cellAddress : '';
+                        if ($this->shouldGenerateColumn($sheet, $colStr)) {
+                            $rowData[$column] = ($sheet->getCellCollection()->has($cellAddress)) ? $cellAddress : '';
+                        }
+                        ++$column;
+                        ++$colStr;
                     }
                     $html .= $this->generateRow($sheet, $rowData, $row - 1, $cellType);
                 }
 
                 $html .= $endTag;
             }
-            --$row;
-            $html .= $this->extendRowsForChartsAndImages($sheet, $row);
 
             // Write table footer
             $html .= $this->generateTableFooter();
@@ -499,10 +528,8 @@ class Html extends BaseWriter
 
     /**
      * Generate sheet tabs.
-     *
-     * @return string
      */
-    public function generateNavigation()
+    public function generateNavigation(): string
     {
         // Fetch sheets
         $sheets = [];
@@ -533,90 +560,41 @@ class Html extends BaseWriter
         return $html;
     }
 
-    /**
-     * Extend Row if chart is placed after nominal end of row.
-     * This code should be exercised by sample:
-     * Chart/32_Chart_read_write_PDF.php.
-     *
-     * @param int $row Row to check for charts
-     *
-     * @return array
-     */
-    private function extendRowsForCharts(Worksheet $worksheet, int $row)
+    private function extendRowsAndColumns(Worksheet $worksheet, int &$colMax, int &$rowMax): void
     {
-        $rowMax = $row;
-        $colMax = 'A';
-        $anyfound = false;
         if ($this->includeCharts) {
             foreach ($worksheet->getChartCollection() as $chart) {
                 if ($chart instanceof Chart) {
-                    $anyfound = true;
                     $chartCoordinates = $chart->getTopLeftPosition();
-                    $chartTL = Coordinate::coordinateFromString($chartCoordinates['cell']);
-                    $chartCol = Coordinate::columnIndexFromString($chartTL[0]);
+                    $this->sheetCharts[$chartCoordinates['cell']] = $chart;
+                    $chartTL = Coordinate::indexesFromString($chartCoordinates['cell']);
                     if ($chartTL[1] > $rowMax) {
                         $rowMax = $chartTL[1];
-                        if ($chartCol > Coordinate::columnIndexFromString($colMax)) {
-                            $colMax = $chartTL[0];
-                        }
+                    }
+                    if ($chartTL[0] > $colMax) {
+                        $colMax = $chartTL[0];
                     }
                 }
             }
         }
-
-        return [$rowMax, $colMax, $anyfound];
-    }
-
-    private function extendRowsForChartsAndImages(Worksheet $worksheet, int $row): string
-    {
-        [$rowMax, $colMax, $anyfound] = $this->extendRowsForCharts($worksheet, $row);
-
         foreach ($worksheet->getDrawingCollection() as $drawing) {
-            $anyfound = true;
-            $imageTL = Coordinate::coordinateFromString($drawing->getCoordinates());
-            $imageCol = Coordinate::columnIndexFromString($imageTL[0]);
+            $imageTL = Coordinate::indexesFromString($drawing->getCoordinates());
+            $this->sheetDrawings[$drawing->getCoordinates()] = $drawing;
             if ($imageTL[1] > $rowMax) {
                 $rowMax = $imageTL[1];
-                if ($imageCol > Coordinate::columnIndexFromString($colMax)) {
-                    $colMax = $imageTL[0];
-                }
+            }
+            if ($imageTL[0] > $colMax) {
+                $colMax = $imageTL[0];
             }
         }
-
-        // Don't extend rows if not needed
-        if ($row === $rowMax || !$anyfound) {
-            return '';
-        }
-
-        $html = '';
-        ++$colMax;
-        ++$row;
-        while ($row <= $rowMax) {
-            $html .= '<tr>';
-            for ($col = 'A'; $col != $colMax; ++$col) {
-                $htmlx = $this->writeImageInCell($worksheet, $col . $row);
-                $htmlx .= $this->includeCharts ? $this->writeChartInCell($worksheet, $col . $row) : '';
-                if ($htmlx) {
-                    $html .= "<td class='style0' style='position: relative;'>$htmlx</td>";
-                } else {
-                    $html .= "<td class='style0'></td>";
-                }
-            }
-            ++$row;
-            $html .= '</tr>' . PHP_EOL;
-        }
-
-        return $html;
     }
 
     /**
      * Convert Windows file name to file protocol URL.
      *
      * @param string $filename file name on local system
-     *
-     * @return string
      */
-    public static function winFileToUrl($filename, bool $mpdf = false)
+    public static function winFileToUrl(string $filename, bool $mpdf = false): string
     {
         // Windows filename
         if (substr($filename, 1, 2) === ':\\') {
@@ -630,21 +608,16 @@ class Html extends BaseWriter
     /**
      * Generate image tag in cell.
      *
-     * @param Worksheet $worksheet \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
      * @param string $coordinates Cell coordinates
-     *
-     * @return string
      */
-    private function writeImageInCell(Worksheet $worksheet, $coordinates)
+    private function writeImageInCell(string $coordinates): string
     {
         // Construct HTML
         $html = '';
 
         // Write images
-        foreach ($worksheet->getDrawingCollection() as $drawing) {
-            if ($drawing->getCoordinates() != $coordinates) {
-                continue;
-            }
+        $drawing = $this->sheetDrawings[$coordinates] ?? null;
+        if ($drawing !== null) {
             $filedesc = $drawing->getDescription();
             $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
             if ($drawing instanceof Drawing) {
@@ -663,22 +636,22 @@ class Html extends BaseWriter
                 $filename = htmlspecialchars($filename, Settings::htmlEntityFlags());
 
                 $html .= PHP_EOL;
-                $imageData = self::winFileToUrl($filename, $this->isMPdf);
+                $imageData = self::winFileToUrl($filename, $this instanceof Pdf\Mpdf);
 
-                if ($this->embedImages || substr($imageData, 0, 6) === 'zip://') {
+                if ($this->embedImages || str_starts_with($imageData, 'zip://')) {
                     $picture = @file_get_contents($filename);
                     if ($picture !== false) {
-                        $imageDetails = getimagesize($filename) ?: [];
+                        $imageDetails = getimagesize($filename) ?: ['mime' => ''];
                         // base64 encode the binary data
                         $base64 = base64_encode($picture);
                         $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
                     }
                 }
 
-                $html .= '<img style="position: absolute; z-index: 1; left: ' .
-                    $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: ' .
-                    $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="' .
-                    $imageData . '" alt="' . $filedesc . '" />';
+                $html .= '<img style="position: absolute; z-index: 1; left: '
+                    . $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px; width: '
+                    . $drawing->getWidth() . 'px; height: ' . $drawing->getHeight() . 'px;" src="'
+                    . $imageData . '" alt="' . $filedesc . '" />';
             } elseif ($drawing instanceof MemoryDrawing) {
                 $imageResource = $drawing->getImageResource();
                 if ($imageResource) {
@@ -691,9 +664,15 @@ class Html extends BaseWriter
 
                     //  Because of the nature of tables, width is more important than height.
                     //  max-width: 100% ensures that image doesnt overflow containing cell
+                    //    However, PR #3535 broke test
+                    //    25_In_memory_image, apparently because
+                    //    of the use of max-with. In addition,
+                    //    non-memory-drawings don't use max-width.
+                    //    Its use here is suspect and is being eliminated.
                     //  width: X sets width of supplied image.
                     //  As a result, images bigger than cell will be contained and images smaller will not get stretched
-                    $html .= '<img alt="' . $filedesc . '" src="' . $dataUri . '" style="max-width:100%;width:' . $drawing->getWidth() . 'px;" />';
+                    $html .= '<img alt="' . $filedesc . '" src="' . $dataUri . '" style="width:' . $drawing->getWidth() . 'px;left: '
+                        . $drawing->getOffsetX() . 'px; top: ' . $drawing->getOffsetY() . 'px;position: absolute; z-index: 1;" />';
                 }
             }
         }
@@ -712,29 +691,47 @@ class Html extends BaseWriter
         $html = '';
 
         // Write charts
-        foreach ($worksheet->getChartCollection() as $chart) {
-            if ($chart instanceof Chart) {
-                $chartCoordinates = $chart->getTopLeftPosition();
-                if ($chartCoordinates['cell'] == $coordinates) {
-                    $chartFileName = File::sysGetTempDir() . '/' . uniqid('', true) . '.png';
-                    if (!$chart->render($chartFileName)) {
-                        return '';
-                    }
-
-                    $html .= PHP_EOL;
-                    $imageDetails = getimagesize($chartFileName) ?: [];
-                    $filedesc = $chart->getTitle();
-                    $filedesc = $filedesc ? $filedesc->getCaptionText() : '';
-                    $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart';
-                    $picture = file_get_contents($chartFileName);
-                    if ($picture !== false) {
-                        $base64 = base64_encode($picture);
-                        $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
-
-                        $html .= '<img style="position: absolute; z-index: 1; left: ' . $chartCoordinates['xOffset'] . 'px; top: ' . $chartCoordinates['yOffset'] . 'px; width: ' . $imageDetails[0] . 'px; height: ' . $imageDetails[1] . 'px;" src="' . $imageData . '" alt="' . $filedesc . '" />' . PHP_EOL;
-                    }
-                    unlink($chartFileName);
+        $chart = $this->sheetCharts[$coordinates] ?? null;
+        if ($chart !== null) {
+            $chartCoordinates = $chart->getTopLeftPosition();
+            $chartFileName = File::sysGetTempDir() . '/' . uniqid('', true) . '.png';
+            $renderedWidth = $chart->getRenderedWidth();
+            $renderedHeight = $chart->getRenderedHeight();
+            if ($renderedWidth === null || $renderedHeight === null) {
+                $this->adjustRendererPositions($chart, $worksheet);
+            }
+            $title = $chart->getTitle();
+            $caption = null;
+            $filedesc = '';
+            if ($title !== null) {
+                $calculatedTitle = $title->getCalculatedTitle($worksheet->getParent());
+                if ($calculatedTitle !== null) {
+                    $caption = $title->getCaption();
+                    $title->setCaption($calculatedTitle);
                 }
+                $filedesc = $title->getCaptionText($worksheet->getParent());
+            }
+            $renderSuccessful = $chart->render($chartFileName);
+            $chart->setRenderedWidth($renderedWidth);
+            $chart->setRenderedHeight($renderedHeight);
+            if (isset($title, $caption)) {
+                $title->setCaption($caption);
+            }
+            if (!$renderSuccessful) {
+                return '';
+            }
+
+            $html .= PHP_EOL;
+            $imageDetails = getimagesize($chartFileName) ?: ['', '', 'mime' => ''];
+
+            $filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded chart';
+            $picture = file_get_contents($chartFileName);
+            unlink($chartFileName);
+            if ($picture !== false) {
+                $base64 = base64_encode($picture);
+                $imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
+
+                $html .= '<img style="position: absolute; z-index: 1; left: ' . $chartCoordinates['xOffset'] . 'px; top: ' . $chartCoordinates['yOffset'] . 'px; width: ' . $imageDetails[0] . 'px; height: ' . $imageDetails[1] . 'px;" src="' . $imageData . '" alt="' . $filedesc . '" />' . PHP_EOL;
             }
         }
 
@@ -742,14 +739,43 @@ class Html extends BaseWriter
         return $html;
     }
 
+    private function adjustRendererPositions(Chart $chart, Worksheet $sheet): void
+    {
+        $topLeft = $chart->getTopLeftPosition();
+        $bottomRight = $chart->getBottomRightPosition();
+        $tlCell = $topLeft['cell'];
+        $brCell = $bottomRight['cell'];
+        if ($tlCell !== '' && $brCell !== '') {
+            $tlCoordinate = Coordinate::indexesFromString($tlCell);
+            $brCoordinate = Coordinate::indexesFromString($brCell);
+            $totalHeight = 0.0;
+            $totalWidth = 0.0;
+            $defaultRowHeight = $sheet->getDefaultRowDimension()->getRowHeight();
+            $defaultRowHeight = SharedDrawing::pointsToPixels(($defaultRowHeight >= 0) ? $defaultRowHeight : SharedFont::getDefaultRowHeightByFont($this->defaultFont));
+            if ($tlCoordinate[1] <= $brCoordinate[1] && $tlCoordinate[0] <= $brCoordinate[0]) {
+                for ($row = $tlCoordinate[1]; $row <= $brCoordinate[1]; ++$row) {
+                    $height = $sheet->getRowDimension($row)->getRowHeight('pt');
+                    $totalHeight += ($height >= 0) ? $height : $defaultRowHeight;
+                }
+                $rightEdge = $brCoordinate[2];
+                ++$rightEdge;
+                for ($column = $tlCoordinate[2]; $column !== $rightEdge; ++$column) {
+                    $width = $sheet->getColumnDimension($column)->getWidth();
+                    $width = ($width < 0) ? self::DEFAULT_CELL_WIDTH_PIXELS : SharedDrawing::cellDimensionToPixels($sheet->getColumnDimension($column)->getWidth(), $this->defaultFont);
+                    $totalWidth += $width;
+                }
+                $chart->setRenderedWidth($totalWidth);
+                $chart->setRenderedHeight($totalHeight);
+            }
+        }
+    }
+
     /**
      * Generate CSS styles.
      *
      * @param bool $generateSurroundingHTML Generate surrounding HTML tags? (&lt;style&gt; and &lt;/style&gt;)
-     *
-     * @return string
      */
-    public function generateStyles($generateSurroundingHTML = true)
+    public function generateStyles(bool $generateSurroundingHTML = true): string
     {
         // Build CSS
         $css = $this->buildCSS($generateSurroundingHTML);
@@ -803,7 +829,17 @@ class Html extends BaseWriter
     private function buildCssPerSheet(Worksheet $sheet, array &$css): void
     {
         // Calculate hash code
-        $sheetIndex = $sheet->getParent()->getIndex($sheet);
+        $sheetIndex = $sheet->getParentOrThrow()->getIndex($sheet);
+        $setup = $sheet->getPageSetup();
+        if ($setup->getFitToPage() && $setup->getFitToHeight() === 1) {
+            $css["table.sheet$sheetIndex"]['page-break-inside'] = 'avoid';
+            $css["table.sheet$sheetIndex"]['break-inside'] = 'avoid';
+        }
+        $picture = $sheet->getBackgroundImage();
+        if ($picture !== '') {
+            $base64 = base64_encode($picture);
+            $css["table.sheet$sheetIndex"]['background-image'] = 'url(data:' . $sheet->getBackgroundMime() . ';base64,' . $base64 . ')';
+        }
 
         // Build styles
         // Calculate column widths
@@ -812,9 +848,13 @@ class Html extends BaseWriter
         // col elements, initialize
         $highestColumnIndex = Coordinate::columnIndexFromString($sheet->getHighestColumn()) - 1;
         $column = -1;
+        $colStr = 'A';
         while ($column++ < $highestColumnIndex) {
-            $this->columnWidths[$sheetIndex][$column] = 42; // approximation
-            $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = '42pt';
+            $this->columnWidths[$sheetIndex][$column] = self::DEFAULT_CELL_WIDTH_POINTS; // approximation
+            if ($this->shouldGenerateColumn($sheet, $colStr)) {
+                $css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = self::DEFAULT_CELL_WIDTH_POINTS . 'pt';
+            }
+            ++$colStr;
         }
 
         // col elements, loop through columnDimensions and set width
@@ -824,6 +864,9 @@ class Html extends BaseWriter
             $width = SharedDrawing::pixelsToPoints($width);
             if ($columnDimension->getVisible() === false) {
                 $css['table.sheet' . $sheetIndex . ' .column' . $column]['display'] = 'none';
+                // This would be better but Firefox has an 11-year-old bug.
+                // https://bugzilla.mozilla.org/show_bug.cgi?id=819045
+                //$css['table.sheet' . $sheetIndex . ' col.col' . $column]['visibility'] = 'collapse';
             }
             if ($width >= 0) {
                 $this->columnWidths[$sheetIndex][$column] = $width;
@@ -855,10 +898,8 @@ class Html extends BaseWriter
      * Build CSS styles.
      *
      * @param bool $generateSurroundingHTML Generate surrounding HTML style? (html { })
-     *
-     * @return array
      */
-    public function buildCSS($generateSurroundingHTML = true)
+    public function buildCSS(bool $generateSurroundingHTML = true): array
     {
         // Cached?
         if ($this->cssStyles !== null) {
@@ -949,10 +990,8 @@ class Html extends BaseWriter
 
     /**
      * Create CSS style.
-     *
-     * @return array
      */
-    private function createCSSStyle(Style $style)
+    private function createCSSStyle(Style $style): array
     {
         // Create CSS
         return array_merge(
@@ -965,10 +1004,8 @@ class Html extends BaseWriter
 
     /**
      * Create CSS style.
-     *
-     * @return array
      */
-    private function createCSSStyleAlignment(Alignment $alignment)
+    private function createCSSStyleAlignment(Alignment $alignment): array
     {
         // Construct CSS
         $css = [];
@@ -987,7 +1024,7 @@ class Html extends BaseWriter
         }
         $rotation = $alignment->getTextRotation();
         if ($rotation !== 0 && $rotation !== Alignment::TEXTROTATION_STACK_PHPSPREADSHEET) {
-            if ($this->isMPdf) {
+            if ($this instanceof Pdf\Mpdf) {
                 $css['text-rotate'] = "$rotation";
             } else {
                 $css['transform'] = "rotate({$rotation}deg)";
@@ -999,10 +1036,8 @@ class Html extends BaseWriter
 
     /**
      * Create CSS style.
-     *
-     * @return array
      */
-    private function createCSSStyleFont(Font $font)
+    private function createCSSStyleFont(Font $font): array
     {
         // Construct CSS
         $css = [];
@@ -1023,7 +1058,7 @@ class Html extends BaseWriter
         }
 
         $css['color'] = '#' . $font->getColor()->getRGB();
-        $css['font-family'] = '\'' . $font->getName() . '\'';
+        $css['font-family'] = '\'' . htmlspecialchars((string) $font->getName(), ENT_QUOTES) . '\'';
         $css['font-size'] = $font->getSize() . 'pt';
 
         return $css;
@@ -1033,19 +1068,33 @@ class Html extends BaseWriter
      * Create CSS style.
      *
      * @param Borders $borders Borders
-     *
-     * @return array
      */
-    private function createCSSStyleBorders(Borders $borders)
+    private function createCSSStyleBorders(Borders $borders): array
     {
         // Construct CSS
         $css = [];
 
         // Create CSS
-        $css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
-        $css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
-        $css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
-        $css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
+        if (!($this instanceof Pdf\Mpdf)) {
+            $css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
+            $css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
+            $css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
+            $css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
+        } else {
+            // Mpdf doesn't process !important, so omit unimportant border none
+            if ($borders->getBottom()->getBorderStyle() !== Border::BORDER_NONE) {
+                $css['border-bottom'] = $this->createCSSStyleBorder($borders->getBottom());
+            }
+            if ($borders->getTop()->getBorderStyle() !== Border::BORDER_NONE) {
+                $css['border-top'] = $this->createCSSStyleBorder($borders->getTop());
+            }
+            if ($borders->getLeft()->getBorderStyle() !== Border::BORDER_NONE) {
+                $css['border-left'] = $this->createCSSStyleBorder($borders->getLeft());
+            }
+            if ($borders->getRight()->getBorderStyle() !== Border::BORDER_NONE) {
+                $css['border-right'] = $this->createCSSStyleBorder($borders->getRight());
+            }
+        }
 
         return $css;
     }
@@ -1060,25 +1109,23 @@ class Html extends BaseWriter
         //    Create CSS - add !important to non-none border styles for merged cells
         $borderStyle = $this->mapBorderStyle($border->getBorderStyle());
 
-        return $borderStyle . ' #' . $border->getColor()->getRGB() . (($borderStyle == 'none') ? '' : ' !important');
+        return $borderStyle . ' #' . $border->getColor()->getRGB() . (($borderStyle === self::BORDER_NONE) ? '' : ' !important');
     }
 
     /**
      * Create CSS style (Fill).
      *
      * @param Fill $fill Fill
-     *
-     * @return array
      */
-    private function createCSSStyleFill(Fill $fill)
+    private function createCSSStyleFill(Fill $fill): array
     {
         // Construct HTML
         $css = [];
 
         // Create CSS
         if ($fill->getFillType() !== Fill::FILL_NONE) {
-            $value = $fill->getFillType() == Fill::FILL_NONE ?
-                'white' : '#' . $fill->getStartColor()->getRGB();
+            $value = $fill->getFillType() == Fill::FILL_NONE
+                ? 'white' : '#' . $fill->getStartColor()->getRGB();
             $css['background-color'] = $value;
         }
 
@@ -1100,8 +1147,8 @@ class Html extends BaseWriter
 
     private function generateTableTagInline(Worksheet $worksheet, string $id): string
     {
-        $style = isset($this->cssStyles['table']) ?
-            $this->assembleCSS($this->cssStyles['table']) : '';
+        $style = isset($this->cssStyles['table'])
+            ? $this->assembleCSS($this->cssStyles['table']) : '';
 
         $prntgrid = $worksheet->getPrintGridlines();
         $viewgrid = $this->isPdf ? $prntgrid : $worksheet->getShowGridlines();
@@ -1134,12 +1181,10 @@ class Html extends BaseWriter
      *
      * @param Worksheet $worksheet The worksheet for the table we are writing
      * @param bool $showid whether or not to add id to table tag
-     *
-     * @return string
      */
-    private function generateTableHeader(Worksheet $worksheet, $showid = true)
+    private function generateTableHeader(Worksheet $worksheet, bool $showid = true): string
     {
-        $sheetIndex = $worksheet->getParent()->getIndex($worksheet);
+        $sheetIndex = $worksheet->getParentOrThrow()->getIndex($worksheet);
 
         // Construct HTML
         $html = '';
@@ -1159,8 +1204,8 @@ class Html extends BaseWriter
             if (!$this->useInlineCss) {
                 $html .= '        <col class="col' . $i . '" />' . PHP_EOL;
             } else {
-                $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) ?
-                    $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) : '';
+                $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i])
+                    ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' col.col' . $i]) : '';
                 $html .= '        <col style="' . $style . '" />' . PHP_EOL;
             }
         }
@@ -1181,14 +1226,12 @@ class Html extends BaseWriter
      *
      * @param int $sheetIndex Sheet index (0-based)
      * @param int $row row number
-     *
-     * @return string
      */
-    private function generateRowStart(Worksheet $worksheet, $sheetIndex, $row)
+    private function generateRowStart(Worksheet $worksheet, int $sheetIndex, int $row): string
     {
         $html = '';
         if (count($worksheet->getBreaks()) > 0) {
-            $breaks = $worksheet->getBreaks();
+            $breaks = $worksheet->getRowBreaks();
 
             // check if a break is needed before this row
             if (isset($breaks['A' . $row])) {
@@ -1243,10 +1286,11 @@ class Html extends BaseWriter
         return [$cell, $cssClass, $coordinate];
     }
 
-    private function generateRowCellDataValueRich(Cell $cell, string &$cellData): void
+    private function generateRowCellDataValueRich(RichText $richText): string
     {
+        $cellData = '';
         // Loop through rich text elements
-        $elements = $cell->getValue()->getRichTextElements();
+        $elements = $richText->getRichTextElements();
         foreach ($elements as $element) {
             // Rich text start?
             if ($element instanceof Run) {
@@ -1261,6 +1305,8 @@ class Html extends BaseWriter
                         $cellData .= '<sub>';
                         $cellEnd = '</sub>';
                     }
+                } else {
+                    $cellData .= '<span>';
                 }
 
                 // Convert UTF8 data to PCDATA
@@ -1276,18 +1322,21 @@ class Html extends BaseWriter
                 $cellData .= htmlspecialchars($cellText, Settings::htmlEntityFlags());
             }
         }
+
+        return nl2br($cellData);
     }
 
     private function generateRowCellDataValue(Worksheet $worksheet, Cell $cell, string &$cellData): void
     {
         if ($cell->getValue() instanceof RichText) {
-            $this->generateRowCellDataValueRich($cell, $cellData);
+            $cellData .= $this->generateRowCellDataValueRich($cell->getValue());
         } else {
             $origData = $this->preCalculateFormulas ? $cell->getCalculatedValue() : $cell->getValue();
-            $formatCode = $worksheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode();
+            $origData2 = $this->preCalculateFormulas ? $cell->getCalculatedValueString() : $cell->getValueString();
+            $formatCode = $worksheet->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode();
 
             $cellData = NumberFormat::toFormattedString(
-                $origData ?? '',
+                $origData2,
                 $formatCode ?? NumberFormat::FORMAT_GENERAL,
                 [$this, 'formatColor']
             );
@@ -1295,19 +1344,15 @@ class Html extends BaseWriter
             if ($cellData === $origData) {
                 $cellData = htmlspecialchars($cellData, Settings::htmlEntityFlags());
             }
-            if ($worksheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperscript()) {
+            if ($worksheet->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperscript()) {
                 $cellData = '<sup>' . $cellData . '</sup>';
-            } elseif ($worksheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubscript()) {
+            } elseif ($worksheet->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubscript()) {
                 $cellData = '<sub>' . $cellData . '</sub>';
             }
         }
     }
 
-    /**
-     * @param null|Cell|string $cell
-     * @param array|string $cssClass
-     */
-    private function generateRowCellData(Worksheet $worksheet, $cell, &$cssClass, string $cellType): string
+    private function generateRowCellData(Worksheet $worksheet, null|Cell|string $cell, array|string &$cssClass): string
     {
         $cellData = '&nbsp;';
         if ($cell instanceof Cell) {
@@ -1331,18 +1376,14 @@ class Html extends BaseWriter
                 $cssClass .= ' style' . $cell->getXfIndex();
                 $cssClass .= ' ' . $cell->getDataType();
             } elseif (is_array($cssClass)) {
-                if ($cellType == 'th') {
-                    if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) {
-                        $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]);
-                    }
-                } else {
-                    if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) {
-                        $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]);
-                    }
+                $index = $cell->getXfIndex();
+                $styleIndex = 'td.style' . $index . ', th.style' . $index;
+                if (isset($this->cssStyles[$styleIndex])) {
+                    $cssClass = array_merge($cssClass, $this->cssStyles[$styleIndex]);
                 }
 
                 // General horizontal alignment: Actual horizontal alignment depends on dataType
-                $sharedStyle = $worksheet->getParent()->getCellXfByIndex($cell->getXfIndex());
+                $sharedStyle = $worksheet->getParentOrThrow()->getCellXfByIndex($cell->getXfIndex());
                 if (
                     $sharedStyle->getAlignment()->getHorizontal() == Alignment::HORIZONTAL_GENERAL
                     && isset($this->cssStyles['.' . $cell->getDataType()]['text-align'])
@@ -1373,13 +1414,21 @@ class Html extends BaseWriter
         return $html;
     }
 
-    /**
-     * @param array|string $cssClass
-     */
-    private function generateRowWriteCell(string &$html, Worksheet $worksheet, string $coordinate, string $cellType, string $cellData, int $colSpan, int $rowSpan, $cssClass, int $colNum, int $sheetIndex, int $row): void
-    {
+    private function generateRowWriteCell(
+        string &$html,
+        Worksheet $worksheet,
+        string $coordinate,
+        string $cellType,
+        string $cellData,
+        int $colSpan,
+        int $rowSpan,
+        array|string $cssClass,
+        int $colNum,
+        int $sheetIndex,
+        int $row
+    ): void {
         // Image?
-        $htmlx = $this->writeImageInCell($worksheet, $coordinate);
+        $htmlx = $this->writeImageInCell($coordinate);
         // Chart?
         $htmlx .= $this->generateRowIncludeCharts($worksheet, $coordinate);
         // Column start
@@ -1417,11 +1466,21 @@ class Html extends BaseWriter
                 $xcssClass['height'] = $height;
             }
             //** end of redundant code **
+            if ($this->useInlineCss) {
+                foreach (['border-top', 'border-bottom', 'border-right', 'border-left'] as $borderType) {
+                    if (($xcssClass[$borderType] ?? '') === 'none #000000') {
+                        unset($xcssClass[$borderType]);
+                    }
+                }
+            }
 
             if ($htmlx) {
                 $xcssClass['position'] = 'relative';
             }
             $html .= ' style="' . $this->assembleCSS($xcssClass) . '"';
+            if ($this->useInlineCss) {
+                $html .= ' class="gridlines gridlinesp"';
+            }
         }
         $html = $this->generateRowSpans($html, $rowSpan, $colSpan);
 
@@ -1443,18 +1502,12 @@ class Html extends BaseWriter
      * @param array $values Array containing cells in a row
      * @param int $row Row number (0-based)
      * @param string $cellType eg: 'td'
-     *
-     * @return string
      */
-    private function generateRow(Worksheet $worksheet, array $values, $row, $cellType)
+    private function generateRow(Worksheet $worksheet, array $values, int $row, string $cellType): string
     {
         // Sheet index
-        $sheetIndex = $worksheet->getParent()->getIndex($worksheet);
+        $sheetIndex = $worksheet->getParentOrThrow()->getIndex($worksheet);
         $html = $this->generateRowStart($worksheet, $sheetIndex, $row);
-        $generateDiv = $this->isMPdf && $worksheet->getRowDimension($row + 1)->getVisible() === false;
-        if ($generateDiv) {
-            $html .= '<div style="visibility:hidden; display:none;">' . PHP_EOL;
-        }
 
         // Write cells
         $colNum = 0;
@@ -1462,7 +1515,7 @@ class Html extends BaseWriter
             [$cell, $cssClass, $coordinate] = $this->generateRowCellCss($worksheet, $cellAddress, $row, $colNum);
 
             // Cell Data
-            $cellData = $this->generateRowCellData($worksheet, $cell, $cssClass, $cellType);
+            $cellData = $this->generateRowCellData($worksheet, $cell, $cssClass);
 
             // Hyperlink?
             if ($worksheet->hyperlinkExists($coordinate) && !$worksheet->getHyperlink($coordinate)->isInternal()) {
@@ -1470,14 +1523,14 @@ class Html extends BaseWriter
             }
 
             // Should the cell be written or is it swallowed by a rowspan or colspan?
-            $writeCell = !(isset($this->isSpannedCell[$worksheet->getParent()->getIndex($worksheet)][$row + 1][$colNum])
-                && $this->isSpannedCell[$worksheet->getParent()->getIndex($worksheet)][$row + 1][$colNum]);
+            $writeCell = !(isset($this->isSpannedCell[$worksheet->getParentOrThrow()->getIndex($worksheet)][$row + 1][$colNum])
+                && $this->isSpannedCell[$worksheet->getParentOrThrow()->getIndex($worksheet)][$row + 1][$colNum]);
 
             // Colspan and Rowspan
             $colSpan = 1;
             $rowSpan = 1;
-            if (isset($this->isBaseCell[$worksheet->getParent()->getIndex($worksheet)][$row + 1][$colNum])) {
-                $spans = $this->isBaseCell[$worksheet->getParent()->getIndex($worksheet)][$row + 1][$colNum];
+            if (isset($this->isBaseCell[$worksheet->getParentOrThrow()->getIndex($worksheet)][$row + 1][$colNum])) {
+                $spans = $this->isBaseCell[$worksheet->getParentOrThrow()->getIndex($worksheet)][$row + 1][$colNum];
                 $rowSpan = $spans['rowspan'];
                 $colSpan = $spans['colspan'];
 
@@ -1486,6 +1539,14 @@ class Html extends BaseWriter
                 $endCellCoord = Coordinate::stringFromColumnIndex($colNum + $colSpan) . ($row + $rowSpan);
                 if (!$this->useInlineCss) {
                     $cssClass .= ' style' . $worksheet->getCell($endCellCoord)->getXfIndex();
+                } else {
+                    $endBorders = $this->spreadsheet->getCellXfByIndex($worksheet->getCell($endCellCoord)->getXfIndex())->getBorders();
+                    $altBorders = $this->createCSSStyleBorders($endBorders);
+                    foreach ($altBorders as $altKey => $altValue) {
+                        if (str_contains($altValue, '!important')) {
+                            $cssClass[$altKey] = $altValue;
+                        }
+                    }
                 }
             }
 
@@ -1499,9 +1560,6 @@ class Html extends BaseWriter
         }
 
         // Write row end
-        if ($generateDiv) {
-            $html .= '</div>' . PHP_EOL;
-        }
         $html .= '          </tr>' . PHP_EOL;
 
         // Return
@@ -1510,10 +1568,8 @@ class Html extends BaseWriter
 
     /**
      * Takes array where of CSS properties / values and converts to CSS string.
-     *
-     * @return string
      */
-    private function assembleCSS(array $values = [])
+    private function assembleCSS(array $values = []): string
     {
         $pairs = [];
         foreach ($values as $property => $value) {
@@ -1526,10 +1582,8 @@ class Html extends BaseWriter
 
     /**
      * Get images root.
-     *
-     * @return string
      */
-    public function getImagesRoot()
+    public function getImagesRoot(): string
     {
         return $this->imagesRoot;
     }
@@ -1537,11 +1591,9 @@ class Html extends BaseWriter
     /**
      * Set images root.
      *
-     * @param string $imagesRoot
-     *
      * @return $this
      */
-    public function setImagesRoot($imagesRoot)
+    public function setImagesRoot(string $imagesRoot): static
     {
         $this->imagesRoot = $imagesRoot;
 
@@ -1550,10 +1602,8 @@ class Html extends BaseWriter
 
     /**
      * Get embed images.
-     *
-     * @return bool
      */
-    public function getEmbedImages()
+    public function getEmbedImages(): bool
     {
         return $this->embedImages;
     }
@@ -1561,11 +1611,9 @@ class Html extends BaseWriter
     /**
      * Set embed images.
      *
-     * @param bool $embedImages
-     *
      * @return $this
      */
-    public function setEmbedImages($embedImages)
+    public function setEmbedImages(bool $embedImages): static
     {
         $this->embedImages = $embedImages;
 
@@ -1574,10 +1622,8 @@ class Html extends BaseWriter
 
     /**
      * Get use inline CSS?
-     *
-     * @return bool
      */
-    public function getUseInlineCss()
+    public function getUseInlineCss(): bool
     {
         return $this->useInlineCss;
     }
@@ -1585,58 +1631,22 @@ class Html extends BaseWriter
     /**
      * Set use inline CSS?
      *
-     * @param bool $useInlineCss
-     *
      * @return $this
      */
-    public function setUseInlineCss($useInlineCss)
+    public function setUseInlineCss(bool $useInlineCss): static
     {
         $this->useInlineCss = $useInlineCss;
 
         return $this;
     }
 
-    /**
-     * Get use embedded CSS?
-     *
-     * @return bool
-     *
-     * @codeCoverageIgnore
-     *
-     * @deprecated no longer used
-     */
-    public function getUseEmbeddedCSS()
-    {
-        return $this->useEmbeddedCSS;
-    }
-
-    /**
-     * Set use embedded CSS?
-     *
-     * @param bool $useEmbeddedCSS
-     *
-     * @return $this
-     *
-     * @codeCoverageIgnore
-     *
-     * @deprecated no longer used
-     */
-    public function setUseEmbeddedCSS($useEmbeddedCSS)
-    {
-        $this->useEmbeddedCSS = $useEmbeddedCSS;
-
-        return $this;
-    }
-
     /**
      * Add color to formatted string as inline style.
      *
      * @param string $value Plain formatted value without color
      * @param string $format Format code
-     *
-     * @return string
      */
-    public function formatColor($value, $format)
+    public function formatColor(string $value, string $format): string
     {
         // Color information, e.g. [Red] is always at the beginning
         $color = null; // initialize
@@ -1670,8 +1680,8 @@ class Html extends BaseWriter
         // Identify all cells that should be omitted in HTML due to cell merge.
         // In HTML only the upper-left cell should be written and it should have
         //   appropriate rowspan / colspan attribute
-        $sheetIndexes = $this->sheetIndex !== null ?
-            [$this->sheetIndex] : range(0, $this->spreadsheet->getSheetCount() - 1);
+        $sheetIndexes = $this->sheetIndex !== null
+            ? [$this->sheetIndex] : range(0, $this->spreadsheet->getSheetCount() - 1);
 
         foreach ($sheetIndexes as $sheetIndex) {
             $sheet = $this->spreadsheet->getSheet($sheetIndex);
@@ -1761,24 +1771,21 @@ class Html extends BaseWriter
      * Write a comment in the same format as LibreOffice.
      *
      * @see https://github.com/LibreOffice/core/blob/9fc9bf3240f8c62ad7859947ab8a033ac1fe93fa/sc/source/filter/html/htmlexp.cxx#L1073-L1092
-     *
-     * @param string $coordinate
-     *
-     * @return string
      */
-    private function writeComment(Worksheet $worksheet, $coordinate)
+    private function writeComment(Worksheet $worksheet, string $coordinate): string
     {
         $result = '';
         if (!$this->isPdf && isset($worksheet->getComments()[$coordinate])) {
-            $sanitizer = new HTMLPurifier();
-            $cachePath = File::sysGetTempDir() . '/phpsppur';
-            if (is_dir($cachePath) || mkdir($cachePath)) {
-                $sanitizer->config->set('Cache.SerializerPath', $cachePath);
+            $sanitizedString = $this->generateRowCellDataValueRich($worksheet->getComment($coordinate)->getText());
+            $dir = ($worksheet->getComment($coordinate)->getTextboxDirection() === Comment::TEXTBOX_DIRECTION_RTL) ? ' dir="rtl"' : '';
+            $align = strtolower($worksheet->getComment($coordinate)->getAlignment());
+            $alignment = Alignment::HORIZONTAL_ALIGNMENT_FOR_HTML[$align] ?? '';
+            if ($alignment !== '') {
+                $alignment = " style=\"text-align:$alignment\"";
             }
-            $sanitizedString = $sanitizer->purify($worksheet->getComment($coordinate)->getText()->getPlainText());
             if ($sanitizedString !== '') {
                 $result .= '<a class="comment-indicator"></a>';
-                $result .= '<div class="comment">' . nl2br($sanitizedString) . '</div>';
+                $result .= "<div class=\"comment\"$dir$alignment>" . $sanitizedString . '</div>';
                 $result .= PHP_EOL;
             }
         }
@@ -1794,12 +1801,8 @@ class Html extends BaseWriter
 
     /**
      * Generate @page declarations.
-     *
-     * @param bool $generateSurroundingHTML
-     *
-     * @return    string
      */
-    private function generatePageDeclarations($generateSurroundingHTML)
+    private function generatePageDeclarations(bool $generateSurroundingHTML): string
     {
         // Ensure that Spans have been calculated?
         $this->calculateSpans();
@@ -1857,4 +1860,25 @@ class Html extends BaseWriter
 
         return $htmlPage;
     }
+
+    private function shouldGenerateRow(Worksheet $sheet, int $row): bool
+    {
+        if (!($this instanceof Pdf\Mpdf || $this instanceof Pdf\Tcpdf)) {
+            return true;
+        }
+
+        return $sheet->isRowVisible($row);
+    }
+
+    private function shouldGenerateColumn(Worksheet $sheet, string $colStr): bool
+    {
+        if (!($this instanceof Pdf\Mpdf || $this instanceof Pdf\Tcpdf)) {
+            return true;
+        }
+        if (!$sheet->columnDimensionExists($colStr)) {
+            return true;
+        }
+
+        return $sheet->getColumnDimension($colStr)->getVisible();
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php
index a4bd5d5..cd48306 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php
@@ -21,21 +21,17 @@ interface IWriter
      * Write charts in workbook?
      *        If this is true, then the Writer will write definitions for any charts that exist in the PhpSpreadsheet object.
      *        If false (the default) it will ignore any charts defined in the PhpSpreadsheet object.
-     *
-     * @return bool
      */
-    public function getIncludeCharts();
+    public function getIncludeCharts(): bool;
 
     /**
      * Set write charts in workbook
      *        Set to true, to advise the Writer to include any charts that exist in the PhpSpreadsheet object.
      *        Set to false (the default) to ignore charts.
      *
-     * @param bool $includeCharts
-     *
-     * @return IWriter
+     * @return $this
      */
-    public function setIncludeCharts($includeCharts);
+    public function setIncludeCharts(bool $includeCharts): self;
 
     /**
      * Get Pre-Calculate Formulas flag
@@ -44,10 +40,8 @@ interface IWriter
      *        viewer when opening the file
      *     If false, then formulae are not calculated on save. This is faster for saving in PhpSpreadsheet, but slower
      *        when opening the resulting file in MS Excel, because Excel has to recalculate the formulae itself.
-     *
-     * @return bool
      */
-    public function getPreCalculateFormulas();
+    public function getPreCalculateFormulas(): bool;
 
     /**
      * Set Pre-Calculate Formulas
@@ -56,9 +50,9 @@ interface IWriter
      *
      * @param bool $precalculateFormulas Pre-Calculate Formulas?
      *
-     * @return IWriter
+     * @return $this
      */
-    public function setPreCalculateFormulas($precalculateFormulas);
+    public function setPreCalculateFormulas(bool $precalculateFormulas): self;
 
     /**
      * Save PhpSpreadsheet to file.
@@ -74,25 +68,20 @@ interface IWriter
 
     /**
      * Get use disk caching where possible?
-     *
-     * @return bool
      */
-    public function getUseDiskCaching();
+    public function getUseDiskCaching(): bool;
 
     /**
      * Set use disk caching where possible?
      *
-     * @param bool $useDiskCache
-     * @param string $cacheDirectory Disk caching directory
+     * @param ?string $cacheDirectory Disk caching directory
      *
-     * @return IWriter
+     * @return $this
      */
-    public function setUseDiskCaching($useDiskCache, $cacheDirectory = null);
+    public function setUseDiskCaching(bool $useDiskCache, ?string $cacheDirectory = null): self;
 
     /**
      * Get disk caching directory.
-     *
-     * @return string
      */
-    public function getDiskCachingDirectory();
+    public function getDiskCachingDirectory(): string;
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php
index 827c43b..638c16f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods.php
@@ -12,52 +12,28 @@ use PhpOffice\PhpSpreadsheet\Writer\Ods\Settings;
 use PhpOffice\PhpSpreadsheet\Writer\Ods\Styles;
 use PhpOffice\PhpSpreadsheet\Writer\Ods\Thumbnails;
 use ZipStream\Exception\OverflowException;
-use ZipStream\Option\Archive;
 use ZipStream\ZipStream;
 
 class Ods extends BaseWriter
 {
     /**
      * Private PhpSpreadsheet.
-     *
-     * @var Spreadsheet
      */
-    private $spreadSheet;
+    private Spreadsheet $spreadSheet;
 
-    /**
-     * @var Content
-     */
-    private $writerPartContent;
+    private Content $writerPartContent;
 
-    /**
-     * @var Meta
-     */
-    private $writerPartMeta;
+    private Meta $writerPartMeta;
 
-    /**
-     * @var MetaInf
-     */
-    private $writerPartMetaInf;
+    private MetaInf $writerPartMetaInf;
 
-    /**
-     * @var Mimetype
-     */
-    private $writerPartMimetype;
+    private Mimetype $writerPartMimetype;
 
-    /**
-     * @var Settings
-     */
-    private $writerPartSettings;
+    private Settings $writerPartSettings;
 
-    /**
-     * @var Styles
-     */
-    private $writerPartStyles;
+    private Styles $writerPartStyles;
 
-    /**
-     * @var Thumbnails
-     */
-    private $writerPartThumbnails;
+    private Thumbnails $writerPartThumbnails;
 
     /**
      * Create a new Ods.
@@ -117,10 +93,6 @@ class Ods extends BaseWriter
      */
     public function save($filename, int $flags = 0): void
     {
-        if (!$this->spreadSheet) {
-            throw new WriterException('PhpSpreadsheet object unassigned.');
-        }
-
         $this->processFlags($flags);
 
         // garbage collect
@@ -142,7 +114,7 @@ class Ods extends BaseWriter
         // Close file
         try {
             $zip->finish();
-        } catch (OverflowException $e) {
+        } catch (OverflowException) {
             throw new WriterException('Could not close resource.');
         }
 
@@ -151,10 +123,8 @@ class Ods extends BaseWriter
 
     /**
      * Create zip object.
-     *
-     * @return ZipStream
      */
-    private function createZip()
+    private function createZip(): ZipStream
     {
         // Try opening the ZIP file
         if (!is_resource($this->fileHandle)) {
@@ -162,25 +132,15 @@ class Ods extends BaseWriter
         }
 
         // Create new ZIP stream
-        $options = new Archive();
-        $options->setEnableZip64(false);
-        $options->setOutputStream($this->fileHandle);
-
-        return new ZipStream(null, $options);
+        return ZipStream0::newZipStream($this->fileHandle);
     }
 
     /**
      * Get Spreadsheet object.
-     *
-     * @return Spreadsheet
      */
-    public function getSpreadsheet()
+    public function getSpreadsheet(): Spreadsheet
     {
-        if ($this->spreadSheet !== null) {
-            return $this->spreadSheet;
-        }
-
-        throw new WriterException('No PhpSpreadsheet assigned.');
+        return $this->spreadSheet;
     }
 
     /**
@@ -190,7 +150,7 @@ class Ods extends BaseWriter
      *
      * @return $this
      */
-    public function setSpreadsheet(Spreadsheet $spreadsheet)
+    public function setSpreadsheet(Spreadsheet $spreadsheet): static
     {
         $this->spreadSheet = $spreadsheet;
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php
index 996ec1a..6d757e9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php
@@ -9,15 +9,9 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class AutoFilters
 {
-    /**
-     * @var XMLWriter
-     */
-    private $objWriter;
+    private XMLWriter $objWriter;
 
-    /**
-     * @var Spreadsheet
-     */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet)
     {
@@ -25,12 +19,9 @@ class AutoFilters
         $this->spreadsheet = $spreadsheet;
     }
 
-    /** @var mixed */
-    private static $scrutinizerFalse = false;
-
     public function write(): void
     {
-        $wrapperWritten = self::$scrutinizerFalse;
+        $wrapperWritten = false;
         $sheetCount = $this->spreadsheet->getSheetCount();
         for ($i = 0; $i < $sheetCount; ++$i) {
             $worksheet = $this->spreadsheet->getSheet($i);
@@ -56,7 +47,7 @@ class AutoFilters
         }
     }
 
-    protected function formatRange(Worksheet $worksheet, Autofilter $autofilter): string
+    protected function formatRange(Worksheet $worksheet, AutoFilter $autofilter): string
     {
         $title = $worksheet->getTitle();
         $range = $autofilter->getRange();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php
index b0829bf..f0b7d57 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Comment.php
@@ -24,7 +24,22 @@ class Comment
         $objWriter->writeAttribute('svg:x', $comment->getMarginLeft());
         $objWriter->writeAttribute('svg:y', $comment->getMarginTop());
         $objWriter->writeElement('dc:creator', $comment->getAuthor());
-        $objWriter->writeElement('text:p', $comment->getText()->getPlainText());
+
+        $objWriter->startElement('text:p');
+        $text = $comment->getText()->getPlainText();
+        $textElements = explode("\n", $text);
+        $newLineOwed = false;
+        foreach ($textElements as $textSegment) {
+            if ($newLineOwed) {
+                $objWriter->writeElement('text:line-break');
+            }
+            $newLineOwed = true;
+            if ($textSegment !== '') {
+                $objWriter->writeElement('text:span', $textSegment);
+            }
+        }
+        $objWriter->endElement(); // text:p
+
         $objWriter->endElement();
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php
index 1bf2c46..7573230 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Cell/Style.php
@@ -5,6 +5,8 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Ods\Cell;
 use PhpOffice\PhpSpreadsheet\Helper\Dimension;
 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
 use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
 use PhpOffice\PhpSpreadsheet\Style\Fill;
 use PhpOffice\PhpSpreadsheet\Style\Font;
 use PhpOffice\PhpSpreadsheet\Style\Style as CellStyle;
@@ -19,7 +21,7 @@ class Style
     public const ROW_STYLE_PREFIX = 'ro';
     public const TABLE_STYLE_PREFIX = 'ta';
 
-    private $writer;
+    private XMLWriter $writer;
 
     public function __construct(XMLWriter $writer)
     {
@@ -28,34 +30,22 @@ class Style
 
     private function mapHorizontalAlignment(string $horizontalAlignment): string
     {
-        switch ($horizontalAlignment) {
-            case Alignment::HORIZONTAL_CENTER:
-            case Alignment::HORIZONTAL_CENTER_CONTINUOUS:
-            case Alignment::HORIZONTAL_DISTRIBUTED:
-                return 'center';
-            case Alignment::HORIZONTAL_RIGHT:
-                return 'end';
-            case Alignment::HORIZONTAL_FILL:
-            case Alignment::HORIZONTAL_JUSTIFY:
-                return 'justify';
-        }
-
-        return 'start';
+        return match ($horizontalAlignment) {
+            Alignment::HORIZONTAL_CENTER, Alignment::HORIZONTAL_CENTER_CONTINUOUS, Alignment::HORIZONTAL_DISTRIBUTED => 'center',
+            Alignment::HORIZONTAL_RIGHT => 'end',
+            Alignment::HORIZONTAL_FILL, Alignment::HORIZONTAL_JUSTIFY => 'justify',
+            default => 'start',
+        };
     }
 
     private function mapVerticalAlignment(string $verticalAlignment): string
     {
-        switch ($verticalAlignment) {
-            case Alignment::VERTICAL_TOP:
-                return 'top';
-            case Alignment::VERTICAL_CENTER:
-                return 'middle';
-            case Alignment::VERTICAL_DISTRIBUTED:
-            case Alignment::VERTICAL_JUSTIFY:
-                return 'automatic';
-        }
-
-        return 'bottom';
+        return match ($verticalAlignment) {
+            Alignment::VERTICAL_TOP => 'top',
+            Alignment::VERTICAL_CENTER => 'middle',
+            Alignment::VERTICAL_DISTRIBUTED, Alignment::VERTICAL_JUSTIFY => 'automatic',
+            default => 'bottom',
+        };
     }
 
     private function writeFillStyle(Fill $fill): void
@@ -77,6 +67,80 @@ class Style
         }
     }
 
+    private function writeBordersStyle(Borders $borders): void
+    {
+        $this->writeBorderStyle('bottom', $borders->getBottom());
+        $this->writeBorderStyle('left', $borders->getLeft());
+        $this->writeBorderStyle('right', $borders->getRight());
+        $this->writeBorderStyle('top', $borders->getTop());
+    }
+
+    private function writeBorderStyle(string $direction, Border $border): void
+    {
+        if ($border->getBorderStyle() === Border::BORDER_NONE) {
+            return;
+        }
+
+        $this->writer->writeAttribute('fo:border-' . $direction, sprintf(
+            '%s %s #%s',
+            $this->mapBorderWidth($border),
+            $this->mapBorderStyle($border),
+            $border->getColor()->getRGB(),
+        ));
+    }
+
+    private function mapBorderWidth(Border $border): string
+    {
+        switch ($border->getBorderStyle()) {
+            case Border::BORDER_THIN:
+            case Border::BORDER_DASHED:
+            case Border::BORDER_DASHDOT:
+            case Border::BORDER_DASHDOTDOT:
+            case Border::BORDER_DOTTED:
+            case Border::BORDER_HAIR:
+                return '0.75pt';
+            case Border::BORDER_MEDIUM:
+            case Border::BORDER_MEDIUMDASHED:
+            case Border::BORDER_MEDIUMDASHDOT:
+            case Border::BORDER_MEDIUMDASHDOTDOT:
+            case Border::BORDER_SLANTDASHDOT:
+                return '1.75pt';
+            case Border::BORDER_DOUBLE:
+            case Border::BORDER_THICK:
+                return '2.5pt';
+        }
+
+        return '1pt';
+    }
+
+    private function mapBorderStyle(Border $border): string
+    {
+        switch ($border->getBorderStyle()) {
+            case Border::BORDER_DOTTED:
+            case Border::BORDER_MEDIUMDASHDOTDOT:
+                return Border::BORDER_DOTTED;
+
+            case Border::BORDER_DASHED:
+            case Border::BORDER_DASHDOT:
+            case Border::BORDER_DASHDOTDOT:
+            case Border::BORDER_MEDIUMDASHDOT:
+            case Border::BORDER_MEDIUMDASHED:
+            case Border::BORDER_SLANTDASHDOT:
+                return Border::BORDER_DASHED;
+
+            case Border::BORDER_DOUBLE:
+                return Border::BORDER_DOUBLE;
+
+            case Border::BORDER_HAIR:
+            case Border::BORDER_MEDIUM:
+            case Border::BORDER_THICK:
+            case Border::BORDER_THIN:
+                return 'solid';
+        }
+
+        return 'solid';
+    }
+
     private function writeCellProperties(CellStyle $style): void
     {
         // Align
@@ -97,9 +161,10 @@ class Style
         $this->writer->writeAttribute('style:rotation-align', 'none');
 
         // Fill
-        if ($fill = $style->getFill()) {
-            $this->writeFillStyle($fill);
-        }
+        $this->writeFillStyle($style->getFill());
+
+        // Border
+        $this->writeBordersStyle($style->getBorders());
 
         $this->writer->endElement();
 
@@ -113,16 +178,11 @@ class Style
 
     protected function mapUnderlineStyle(Font $font): string
     {
-        switch ($font->getUnderline()) {
-            case Font::UNDERLINE_DOUBLE:
-            case Font::UNDERLINE_DOUBLEACCOUNTING:
-                return'double';
-            case Font::UNDERLINE_SINGLE:
-            case Font::UNDERLINE_SINGLEACCOUNTING:
-                return'single';
-        }
-
-        return 'none';
+        return match ($font->getUnderline()) {
+            Font::UNDERLINE_DOUBLE, Font::UNDERLINE_DOUBLEACCOUNTING => 'double',
+            Font::UNDERLINE_SINGLE, Font::UNDERLINE_SINGLEACCOUNTING => 'single',
+            default => 'none',
+        };
     }
 
     protected function writeTextProperties(CellStyle $style): void
@@ -142,9 +202,7 @@ class Style
             $this->writer->writeAttribute('fo:font-style', 'italic');
         }
 
-        if ($color = $font->getColor()) {
-            $this->writer->writeAttribute('fo:color', sprintf('#%s', $color->getRGB()));
-        }
+        $this->writer->writeAttribute('fo:color', sprintf('#%s', $font->getColor()->getRGB()));
 
         if ($family = $font->getName()) {
             $this->writer->writeAttribute('fo:font-family', $family);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php
index 00ab064..7ffcd46 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Content.php
@@ -2,15 +2,15 @@
 
 namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
 
+use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
+use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
 use PhpOffice\PhpSpreadsheet\Spreadsheet;
-use PhpOffice\PhpSpreadsheet\Worksheet\Row;
 use PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
-use PhpOffice\PhpSpreadsheet\Writer\Exception;
 use PhpOffice\PhpSpreadsheet\Writer\Ods;
 use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Comment;
 use PhpOffice\PhpSpreadsheet\Writer\Ods\Cell\Style;
@@ -23,7 +23,7 @@ class Content extends WriterPart
     const NUMBER_COLS_REPEATED_MAX = 1024;
     const NUMBER_ROWS_REPEATED_MAX = 1048576;
 
-    private $formulaConvertor;
+    private Formula $formulaConvertor;
 
     /**
      * Set parent Ods writer.
@@ -118,14 +118,23 @@ class Content extends WriterPart
      */
     private function writeSheets(XMLWriter $objWriter): void
     {
-        $spreadsheet = $this->getParentWriter()->getSpreadsheet(); /** @var Spreadsheet $spreadsheet */
+        $spreadsheet = $this->getParentWriter()->getSpreadsheet();
         $sheetCount = $spreadsheet->getSheetCount();
         for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) {
             $objWriter->startElement('table:table');
             $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle());
             $objWriter->writeAttribute('table:style-name', Style::TABLE_STYLE_PREFIX . (string) ($sheetIndex + 1));
             $objWriter->writeElement('office:forms');
+            $lastColumn = 0;
             foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) {
+                $thisColumn = $columnDimension->getColumnNumeric();
+                $emptyColumns = $thisColumn - $lastColumn - 1;
+                if ($emptyColumns > 0) {
+                    $objWriter->startElement('table:table-column');
+                    $objWriter->writeAttribute('table:number-columns-repeated', (string) $emptyColumns);
+                    $objWriter->endElement();
+                }
+                $lastColumn = $thisColumn;
                 $objWriter->startElement('table:table-column');
                 $objWriter->writeAttribute(
                     'table:style-name',
@@ -155,14 +164,14 @@ class Content extends WriterPart
                 $objWriter->startElement('table:table-row');
                 if ($span_row) {
                     if ($span_row > 1) {
-                        $objWriter->writeAttribute('table:number-rows-repeated', $span_row);
+                        $objWriter->writeAttribute('table:number-rows-repeated', (string) $span_row);
                     }
                     $objWriter->startElement('table:table-cell');
                     $objWriter->writeAttribute('table:number-columns-repeated', (string) self::NUMBER_COLS_REPEATED_MAX);
                     $objWriter->endElement();
                     $span_row = 0;
                 } else {
-                    if ($sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
+                    if ($sheet->rowDimensionExists($row->getRowIndex()) && $sheet->getRowDimension($row->getRowIndex())->getRowHeight() > 0) {
                         $objWriter->writeAttribute(
                             'table:style-name',
                             sprintf('%s_%d_%d', Style::ROW_STYLE_PREFIX, $sheetIndex, $row->getRowIndex())
@@ -185,7 +194,7 @@ class Content extends WriterPart
         $numberColsRepeated = self::NUMBER_COLS_REPEATED_MAX;
         $prevColumn = -1;
         foreach ($cells as $cell) {
-            /** @var \PhpOffice\PhpSpreadsheet\Cell\Cell $cell */
+            /** @var Cell $cell */
             $column = Coordinate::columnIndexFromString($cell->getColumn()) - 1;
 
             $this->writeCellSpan($objWriter, $column, $prevColumn);
@@ -201,8 +210,8 @@ class Content extends WriterPart
             switch ($cell->getDataType()) {
                 case DataType::TYPE_BOOL:
                     $objWriter->writeAttribute('office:value-type', 'boolean');
-                    $objWriter->writeAttribute('office:value', $cell->getValue());
-                    $objWriter->writeElement('text:p', $cell->getValue());
+                    $objWriter->writeAttribute('office:boolean-value', $cell->getValue() ? 'true' : 'false');
+                    $objWriter->writeElement('text:p', Calculation::getInstance()->getLocaleBoolean($cell->getValue() ? 'TRUE' : 'FALSE'));
 
                     break;
                 case DataType::TYPE_ERROR:
@@ -213,15 +222,15 @@ class Content extends WriterPart
 
                     break;
                 case DataType::TYPE_FORMULA:
-                    $formulaValue = $cell->getValue();
+                    $formulaValue = $cell->getValueString();
                     if ($this->getParentWriter()->getPreCalculateFormulas()) {
                         try {
-                            $formulaValue = $cell->getCalculatedValue();
-                        } catch (Exception $e) {
+                            $formulaValue = $cell->getCalculatedValueString();
+                        } catch (CalculationException $e) {
                             // don't do anything
                         }
                     }
-                    $objWriter->writeAttribute('table:formula', $this->formulaConvertor->convertFormula($cell->getValue()));
+                    $objWriter->writeAttribute('table:formula', $this->formulaConvertor->convertFormula($cell->getValueString()));
                     if (is_numeric($formulaValue)) {
                         $objWriter->writeAttribute('office:value-type', 'float');
                     } else {
@@ -233,15 +242,31 @@ class Content extends WriterPart
                     break;
                 case DataType::TYPE_NUMERIC:
                     $objWriter->writeAttribute('office:value-type', 'float');
-                    $objWriter->writeAttribute('office:value', $cell->getValue());
-                    $objWriter->writeElement('text:p', $cell->getValue());
+                    $objWriter->writeAttribute('office:value', $cell->getValueString());
+                    $objWriter->writeElement('text:p', $cell->getValueString());
 
                     break;
                 case DataType::TYPE_INLINE:
                     // break intentionally omitted
                 case DataType::TYPE_STRING:
                     $objWriter->writeAttribute('office:value-type', 'string');
-                    $objWriter->writeElement('text:p', $cell->getValue());
+                    $url = $cell->getHyperlink()->getUrl();
+                    if (empty($url)) {
+                        $objWriter->writeElement('text:p', $cell->getValueString());
+                    } else {
+                        $objWriter->startElement('text:p');
+                        $objWriter->startElement('text:a');
+                        $sheets = 'sheet://';
+                        $lensheets = strlen($sheets);
+                        if (substr($url, 0, $lensheets) === $sheets) {
+                            $url = '#' . substr($url, $lensheets);
+                        }
+                        $objWriter->writeAttribute('xlink:href', $url);
+                        $objWriter->writeAttribute('xlink:type', 'simple');
+                        $objWriter->text($cell->getValueString());
+                        $objWriter->endElement(); // text:a
+                        $objWriter->endElement(); // text:p
+                    }
 
                     break;
             }
@@ -254,7 +279,7 @@ class Content extends WriterPart
         if ($numberColsRepeated > 0) {
             if ($numberColsRepeated > 1) {
                 $objWriter->startElement('table:table-cell');
-                $objWriter->writeAttribute('table:number-columns-repeated', $numberColsRepeated);
+                $objWriter->writeAttribute('table:number-columns-repeated', (string) $numberColsRepeated);
                 $objWriter->endElement();
             } else {
                 $objWriter->writeElement('table:table-cell');
@@ -264,18 +289,15 @@ class Content extends WriterPart
 
     /**
      * Write span.
-     *
-     * @param int $curColumn
-     * @param int $prevColumn
      */
-    private function writeCellSpan(XMLWriter $objWriter, $curColumn, $prevColumn): void
+    private function writeCellSpan(XMLWriter $objWriter, int $curColumn, int $prevColumn): void
     {
         $diff = $curColumn - $prevColumn - 1;
         if (1 === $diff) {
             $objWriter->writeElement('table:table-cell');
         } elseif ($diff > 1) {
             $objWriter->startElement('table:table-cell');
-            $objWriter->writeAttribute('table:number-columns-repeated', $diff);
+            $objWriter->writeAttribute('table:number-columns-repeated', (string) $diff);
             $objWriter->endElement();
         }
     }
@@ -322,7 +344,7 @@ class Content extends WriterPart
             return;
         }
 
-        $mergeRange = Coordinate::splitRange($cell->getMergeRange());
+        $mergeRange = Coordinate::splitRange((string) $cell->getMergeRange());
         [$startCell, $endCell] = $mergeRange[0];
         $start = Coordinate::coordinateFromString($startCell);
         $end = Coordinate::coordinateFromString($endCell);
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php
index db766fb..9385607 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Formula.php
@@ -7,7 +7,7 @@ use PhpOffice\PhpSpreadsheet\DefinedName;
 
 class Formula
 {
-    private $definedNames = [];
+    private array $definedNames = [];
 
     /**
      * @param DefinedName[] $definedNames
@@ -24,7 +24,7 @@ class Formula
         $formula = $this->convertCellReferences($formula, $worksheetName);
         $formula = $this->convertDefinedNames($formula);
 
-        if (substr($formula, 0, 1) !== '=') {
+        if (!str_starts_with($formula, '=')) {
             $formula = '=' . $formula;
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php
index 16f7c8b..b47b255 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Meta.php
@@ -65,12 +65,12 @@ class Meta extends WriterPart
         //<meta:document-statistic meta:table-count="XXX" meta:cell-count="XXX" meta:object-count="XXX"/>
         $objWriter->startElement('meta:user-defined');
         $objWriter->writeAttribute('meta:name', 'Company');
-        $objWriter->writeRaw($spreadsheet->getProperties()->getCompany());
+        $objWriter->writeRawData($spreadsheet->getProperties()->getCompany());
         $objWriter->endElement();
 
         $objWriter->startElement('meta:user-defined');
         $objWriter->writeAttribute('meta:name', 'category');
-        $objWriter->writeRaw($spreadsheet->getProperties()->getCategory());
+        $objWriter->writeRawData($spreadsheet->getProperties()->getCategory());
         $objWriter->endElement();
 
         self::writeDocPropsCustom($objWriter, $spreadsheet);
@@ -85,7 +85,7 @@ class Meta extends WriterPart
     private static function writeDocPropsCustom(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
     {
         $customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
-        foreach ($customPropertyList as $key => $customProperty) {
+        foreach ($customPropertyList as $customProperty) {
             $propertyValue = $spreadsheet->getProperties()->getCustomPropertyValue($customProperty);
             $propertyType = $spreadsheet->getProperties()->getCustomPropertyType($customProperty);
 
@@ -96,7 +96,7 @@ class Meta extends WriterPart
                 case Properties::PROPERTY_TYPE_INTEGER:
                 case Properties::PROPERTY_TYPE_FLOAT:
                     $objWriter->writeAttribute('meta:value-type', 'float');
-                    $objWriter->writeRawData($propertyValue);
+                    $objWriter->writeRawData((string) $propertyValue);
 
                     break;
                 case Properties::PROPERTY_TYPE_BOOLEAN:
@@ -106,12 +106,12 @@ class Meta extends WriterPart
                     break;
                 case Properties::PROPERTY_TYPE_DATE:
                     $objWriter->writeAttribute('meta:value-type', 'date');
-                    $dtobj = Date::dateTimeFromTimestamp($propertyValue ?? 0);
+                    $dtobj = Date::dateTimeFromTimestamp((string) ($propertyValue ?? 0));
                     $objWriter->writeRawData($dtobj->format(DATE_W3C));
 
                     break;
                 default:
-                    $objWriter->writeRawData($propertyValue);
+                    $objWriter->writeRawData((string) $propertyValue);
 
                     break;
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php
index e309dab..72b1dd0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/NamedExpressions.php
@@ -10,14 +10,11 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class NamedExpressions
 {
-    /** @var XMLWriter */
-    private $objWriter;
+    private XMLWriter $objWriter;
 
-    /** @var Spreadsheet */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
-    /** @var Formula */
-    private $formulaConvertor;
+    private Formula $formulaConvertor;
 
     public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet, Formula $formulaConvertor)
     {
@@ -131,7 +128,7 @@ class NamedExpressions
             $address = substr($address, 0, $offset) . $newRange . substr($address, $offset + $length);
         }
 
-        if (substr($address, 0, 1) === '=') {
+        if (str_starts_with($address, '=')) {
             $address = substr($address, 1);
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php
index 0644559..cfe1b59 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/Settings.php
@@ -124,7 +124,7 @@ class Settings extends WriterPart
 
     private function writeFreezePane(XMLWriter $objWriter, Worksheet $worksheet): void
     {
-        $freezePane = CellAddress::fromCellAddress($worksheet->getFreezePane());
+        $freezePane = CellAddress::fromCellAddress($worksheet->getFreezePane() ?? '');
         if ($freezePane->cellAddress() === 'A1') {
             return;
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php
index 17d5d16..366fdd9 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Ods/WriterPart.php
@@ -8,17 +8,13 @@ abstract class WriterPart
 {
     /**
      * Parent Ods object.
-     *
-     * @var Ods
      */
-    private $parentWriter;
+    private Ods $parentWriter;
 
     /**
      * Get Ods writer.
-     *
-     * @return Ods
      */
-    public function getParentWriter()
+    public function getParentWriter(): Ods
     {
         return $this->parentWriter;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php
index 493bbba..17e1905 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf.php
@@ -11,38 +11,28 @@ abstract class Pdf extends Html
 {
     /**
      * Temporary storage directory.
-     *
-     * @var string
      */
-    protected $tempDir = '';
+    protected string $tempDir;
 
     /**
      * Font.
-     *
-     * @var string
      */
-    protected $font = 'freesans';
+    protected string $font = 'freesans';
 
     /**
      * Orientation (Over-ride).
-     *
-     * @var ?string
      */
-    protected $orientation;
+    protected ?string $orientation = null;
 
     /**
      * Paper size (Over-ride).
-     *
-     * @var ?int
      */
-    protected $paperSize;
+    protected ?int $paperSize = null;
 
     /**
      * Paper Sizes xRef List.
-     *
-     * @var array
      */
-    protected static $paperSizes = [
+    protected static array $paperSizes = [
         PageSetup::PAPERSIZE_LETTER => 'LETTER', //    (8.5 in. by 11 in.)
         PageSetup::PAPERSIZE_LETTER_SMALL => 'LETTER', //    (8.5 in. by 11 in.)
         PageSetup::PAPERSIZE_TABLOID => [792.00, 1224.00], //    (11 in. by 17 in.)
@@ -126,10 +116,8 @@ abstract class Pdf extends Html
 
     /**
      * Get Font.
-     *
-     * @return string
      */
-    public function getFont()
+    public function getFont(): string
     {
         return $this->font;
     }
@@ -141,11 +129,9 @@ abstract class Pdf extends Html
      *      'arialunicid0-korean'
      *      'arialunicid0-japanese'.
      *
-     * @param string $fontName
-     *
      * @return $this
      */
-    public function setFont($fontName)
+    public function setFont(string $fontName)
     {
         $this->font = $fontName;
 
@@ -154,10 +140,8 @@ abstract class Pdf extends Html
 
     /**
      * Get Paper Size.
-     *
-     * @return ?int
      */
-    public function getPaperSize()
+    public function getPaperSize(): ?int
     {
         return $this->paperSize;
     }
@@ -166,10 +150,8 @@ abstract class Pdf extends Html
      * Set Paper Size.
      *
      * @param int $paperSize Paper size see PageSetup::PAPERSIZE_*
-     *
-     * @return self
      */
-    public function setPaperSize($paperSize)
+    public function setPaperSize(int $paperSize): self
     {
         $this->paperSize = $paperSize;
 
@@ -188,10 +170,8 @@ abstract class Pdf extends Html
      * Set Orientation.
      *
      * @param string $orientation Page orientation see PageSetup::ORIENTATION_*
-     *
-     * @return self
      */
-    public function setOrientation($orientation)
+    public function setOrientation(string $orientation): self
     {
         $this->orientation = $orientation;
 
@@ -200,10 +180,8 @@ abstract class Pdf extends Html
 
     /**
      * Get temporary storage directory.
-     *
-     * @return string
      */
-    public function getTempDir()
+    public function getTempDir(): string
     {
         return $this->tempDir;
     }
@@ -212,10 +190,8 @@ abstract class Pdf extends Html
      * Set temporary storage directory.
      *
      * @param string $temporaryDirectory Temporary storage directory
-     *
-     * @return self
      */
-    public function setTempDir($temporaryDirectory)
+    public function setTempDir(string $temporaryDirectory): self
     {
         if (is_dir($temporaryDirectory)) {
             $this->tempDir = $temporaryDirectory;
@@ -229,7 +205,7 @@ abstract class Pdf extends Html
     /**
      * Save Spreadsheet to PDF file, pre-save.
      *
-     * @param string $filename Name of the file to save as
+     * @param resource|string $filename Name of the file to save as
      *
      * @return resource
      */
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php
index 690b0c5..6569980 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php
@@ -9,17 +9,15 @@ class Dompdf extends Pdf
 {
     /**
      * embed images, or link to images.
-     *
-     * @var bool
      */
-    protected $embedImages = true;
+    protected bool $embedImages = true;
 
     /**
      * Gets the implementation of external PDF library that should be used.
      *
      * @return \Dompdf\Dompdf implementation
      */
-    protected function createExternalWriterInstance()
+    protected function createExternalWriterInstance(): \Dompdf\Dompdf
     {
         return new \Dompdf\Dompdf();
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php
index d0ce9ed..ca031b4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php
@@ -3,13 +3,19 @@
 namespace PhpOffice\PhpSpreadsheet\Writer\Pdf;
 
 use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
-use PhpOffice\PhpSpreadsheet\Writer\Html;
 use PhpOffice\PhpSpreadsheet\Writer\Pdf;
 
 class Mpdf extends Pdf
 {
-    /** @var bool */
-    protected $isMPdf = true;
+    public const SIMULATED_BODY_START = '<!-- simulated body start -->';
+    private const BODY_TAG = '<body>';
+
+    /**
+     * Is the current writer creating mPDF?
+     *
+     * @deprecated 2.0.1 use instanceof Mpdf instead
+     */
+    protected bool $isMPdf = true;
 
     /**
      * Gets the implementation of external PDF library that should be used.
@@ -18,7 +24,7 @@ class Mpdf extends Pdf
      *
      * @return \Mpdf\Mpdf implementation
      */
-    protected function createExternalWriterInstance($config)
+    protected function createExternalWriterInstance(array $config): \Mpdf\Mpdf
     {
         return new \Mpdf\Mpdf($config);
     }
@@ -61,16 +67,22 @@ class Mpdf extends Pdf
         $pdf->SetCreator($this->spreadsheet->getProperties()->getCreator());
 
         $html = $this->generateHTMLAll();
-        $bodyLocation = strpos($html, Html::BODY_LINE);
+        $bodyLocation = strpos($html, self::SIMULATED_BODY_START);
+        if ($bodyLocation === false) {
+            $bodyLocation = strpos($html, self::BODY_TAG);
+            if ($bodyLocation !== false) {
+                $bodyLocation += strlen(self::BODY_TAG);
+            }
+        }
         // Make sure first data presented to Mpdf includes body tag
+        //   (and any htmlpageheader/htmlpagefooter tags)
         //   so that Mpdf doesn't parse it as content. Issue 2432.
         if ($bodyLocation !== false) {
-            $bodyLocation += strlen(Html::BODY_LINE);
             $pdf->WriteHTML(substr($html, 0, $bodyLocation));
             $html = substr($html, $bodyLocation);
         }
-        foreach (\array_chunk(\explode(PHP_EOL, $html), 1000) as $lines) {
-            $pdf->WriteHTML(\implode(PHP_EOL, $lines));
+        foreach (explode("\n", $html) as $line) {
+            $pdf->WriteHTML("$line\n");
         }
 
         //  Write to file
@@ -81,12 +93,8 @@ class Mpdf extends Pdf
 
     /**
      * Convert inches to mm.
-     *
-     * @param float $inches
-     *
-     * @return float
      */
-    private function inchesToMm($inches)
+    private function inchesToMm(float $inches): float
     {
         return $inches * 25.4;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php
index aefc6b5..747ebbd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php
@@ -28,7 +28,7 @@ class Tcpdf extends Pdf
      *
      * @return \TCPDF implementation
      */
-    protected function createExternalWriterInstance($orientation, $unit, $paperSize)
+    protected function createExternalWriterInstance(string $orientation, string $unit, $paperSize): \TCPDF
     {
         return new \TCPDF($orientation, $unit, $paperSize);
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php
index b740b6f..401fad1 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls.php
@@ -31,76 +31,55 @@ class Xls extends BaseWriter
 {
     /**
      * PhpSpreadsheet object.
-     *
-     * @var Spreadsheet
      */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     /**
      * Total number of shared strings in workbook.
-     *
-     * @var int
      */
-    private $strTotal = 0;
+    private int $strTotal = 0;
 
     /**
      * Number of unique shared strings in workbook.
-     *
-     * @var int
      */
-    private $strUnique = 0;
+    private int $strUnique = 0;
 
     /**
      * Array of unique shared strings in workbook.
-     *
-     * @var array
      */
-    private $strTable = [];
+    private array $strTable = [];
 
     /**
      * Color cache. Mapping between RGB value and color index.
-     *
-     * @var array
      */
-    private $colors;
+    private array $colors;
 
     /**
      * Formula parser.
-     *
-     * @var Parser
      */
-    private $parser;
+    private Parser $parser;
 
     /**
      * Identifier clusters for drawings. Used in MSODRAWINGGROUP record.
-     *
-     * @var array
      */
-    private $IDCLs;
+    private array $IDCLs;
 
     /**
      * Basic OLE object summary information.
-     *
-     * @var string
      */
-    private $summaryInformation;
+    private string $summaryInformation;
 
     /**
      * Extended OLE object document summary information.
-     *
-     * @var string
      */
-    private $documentSummaryInformation;
+    private string $documentSummaryInformation;
 
-    /**
-     * @var Workbook
-     */
-    private $writerWorkbook;
+    private Workbook $writerWorkbook;
 
     /**
      * @var Worksheet[]
      */
-    private $writerWorksheets;
+    private array $writerWorksheets;
 
     /**
      * Create a new Xls Writer.
@@ -111,7 +90,7 @@ class Xls extends BaseWriter
     {
         $this->spreadsheet = $spreadsheet;
 
-        $this->parser = new Xls\Parser($spreadsheet);
+        $this->parser = new Parser($spreadsheet);
     }
 
     /**
@@ -135,12 +114,12 @@ class Xls extends BaseWriter
         $this->colors = [];
 
         // Initialise workbook writer
-        $this->writerWorkbook = new Xls\Workbook($this->spreadsheet, $this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser);
+        $this->writerWorkbook = new Workbook($this->spreadsheet, $this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser);
 
         // Initialise worksheet writers
         $countSheets = $this->spreadsheet->getSheetCount();
         for ($i = 0; $i < $countSheets; ++$i) {
-            $this->writerWorksheets[$i] = new Xls\Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->spreadsheet->getSheet($i));
+            $this->writerWorksheets[$i] = new Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->spreadsheet->getSheet($i), $this->writerWorkbook);
         }
 
         // build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook.
@@ -170,7 +149,9 @@ class Xls extends BaseWriter
                     foreach ($elements as $element) {
                         if ($element instanceof Run) {
                             $font = $element->getFont();
-                            $this->writerWorksheets[$i]->fontHashIndex[$font->getHashCode()] = $this->writerWorkbook->addFont($font);
+                            if ($font !== null) {
+                                $this->writerWorksheets[$i]->fontHashIndex[$font->getHashCode()] = $this->writerWorkbook->addFont($font);
+                            }
                         }
                     }
                 }
@@ -199,14 +180,14 @@ class Xls extends BaseWriter
 
         $this->documentSummaryInformation = $this->writeDocumentSummaryInformation();
         // initialize OLE Document Summary Information
-        if (isset($this->documentSummaryInformation) && !empty($this->documentSummaryInformation)) {
+        if (!empty($this->documentSummaryInformation)) {
             $OLE_DocumentSummaryInformation = new File(OLE::ascToUcs(chr(5) . 'DocumentSummaryInformation'));
             $OLE_DocumentSummaryInformation->append($this->documentSummaryInformation);
         }
 
         $this->summaryInformation = $this->writeSummaryInformation();
         // initialize OLE Summary Information
-        if (isset($this->summaryInformation) && !empty($this->summaryInformation)) {
+        if (!empty($this->summaryInformation)) {
             $OLE_SummaryInformation = new File(OLE::ascToUcs(chr(5) . 'SummaryInformation'));
             $OLE_SummaryInformation->append($this->summaryInformation);
         }
@@ -245,7 +226,7 @@ class Xls extends BaseWriter
 
         foreach ($this->spreadsheet->getAllsheets() as $sheet) {
             // sheet index
-            $sheetIndex = $sheet->getParent()->getIndex($sheet);
+            $sheetIndex = $sheet->getParentOrThrow()->getIndex($sheet);
 
             // check if there are any shapes for this sheet
             $filterRange = $sheet->getAutoFilter()->getRange();
@@ -260,7 +241,7 @@ class Xls extends BaseWriter
             $dgContainer = new DgContainer();
 
             // set the drawing index (we use sheet index + 1)
-            $dgId = $sheet->getParent()->getIndex($sheet) + 1;
+            $dgId = $sheet->getParentOrThrow()->getIndex($sheet) + 1;
             $dgContainer->setDgId($dgId);
             $escher->setDgContainer($dgContainer);
 
@@ -272,7 +253,7 @@ class Xls extends BaseWriter
             $spContainer = new SpContainer();
             $spContainer->setSpgr(true);
             $spContainer->setSpType(0);
-            $spContainer->setSpId(($sheet->getParent()->getIndex($sheet) + 1) << 10);
+            $spContainer->setSpId(($sheet->getParentOrThrow()->getIndex($sheet) + 1) << 10);
             $spgrContainer->addChild($spContainer);
 
             // add the shapes
@@ -294,7 +275,7 @@ class Xls extends BaseWriter
 
                 // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
                 $reducedSpId = $countShapes[$sheetIndex];
-                $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
+                $spId = $reducedSpId | ($sheet->getParentOrThrow()->getIndex($sheet) + 1) << 10;
                 $spContainer->setSpId($spId);
 
                 // keep track of last reducedSpId
@@ -315,14 +296,16 @@ class Xls extends BaseWriter
 
                 $twoAnchor = \PhpOffice\PhpSpreadsheet\Shared\Xls::oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height);
 
-                $spContainer->setStartCoordinates($twoAnchor['startCoordinates']);
-                $spContainer->setStartOffsetX($twoAnchor['startOffsetX']);
-                $spContainer->setStartOffsetY($twoAnchor['startOffsetY']);
-                $spContainer->setEndCoordinates($twoAnchor['endCoordinates']);
-                $spContainer->setEndOffsetX($twoAnchor['endOffsetX']);
-                $spContainer->setEndOffsetY($twoAnchor['endOffsetY']);
+                if (is_array($twoAnchor)) {
+                    $spContainer->setStartCoordinates($twoAnchor['startCoordinates']);
+                    $spContainer->setStartOffsetX($twoAnchor['startOffsetX']);
+                    $spContainer->setStartOffsetY($twoAnchor['startOffsetY']);
+                    $spContainer->setEndCoordinates($twoAnchor['endCoordinates']);
+                    $spContainer->setEndOffsetX($twoAnchor['endOffsetX']);
+                    $spContainer->setEndOffsetY($twoAnchor['endOffsetY']);
 
-                $spgrContainer->addChild($spContainer);
+                    $spgrContainer->addChild($spContainer);
+                }
             }
 
             // AutoFilters
@@ -351,7 +334,7 @@ class Xls extends BaseWriter
 
                     // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
                     $reducedSpId = $countShapes[$sheetIndex];
-                    $spId = $reducedSpId | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
+                    $spId = $reducedSpId | ($sheet->getParentOrThrow()->getIndex($sheet) + 1) << 10;
                     $spContainer->setSpId($spId);
 
                     // keep track of last reducedSpId
@@ -414,7 +397,7 @@ class Xls extends BaseWriter
         ob_end_clean();
 
         $blip = new Blip();
-        $blip->setData($blipData);
+        $blip->setData("$blipData");
 
         $BSE = new BSE();
         $BSE->setBlipType($blipType);
@@ -423,13 +406,16 @@ class Xls extends BaseWriter
         $bstoreContainer->addBSE($BSE);
     }
 
+    private static int $two = 2; // phpstan silliness
+
     private function processDrawing(BstoreContainer &$bstoreContainer, Drawing $drawing): void
     {
-        $blipType = null;
+        $blipType = 0;
         $blipData = '';
         $filename = $drawing->getPath();
 
-        [$imagesx, $imagesy, $imageFormat] = getimagesize($filename);
+        $imageSize = getimagesize($filename);
+        $imageFormat = empty($imageSize) ? 0 : ($imageSize[self::$two] ?? 0);
 
         switch ($imageFormat) {
             case 1: // GIF, not supported by BIFF8, we convert to PNG
@@ -564,10 +550,8 @@ class Xls extends BaseWriter
 
     /**
      * Build the OLE Part for DocumentSummary Information.
-     *
-     * @return string
      */
-    private function writeDocumentSummaryInformation()
+    private function writeDocumentSummaryInformation(): string
     {
         // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
         $data = pack('v', 0xFFFE);
@@ -746,11 +730,6 @@ class Xls extends BaseWriter
                 $dataSection_Content .= $dataProp['data']['data'];
 
                 $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
-            // Condition below can never be true
-            //} elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
-            //    $dataSection_Content .= $dataProp['data']['data'];
-
-            //    $dataSection_Content_Offset += 4 + 8;
             } else {
                 $dataSection_Content .= $dataProp['data']['data'];
 
@@ -773,10 +752,7 @@ class Xls extends BaseWriter
         return $data;
     }
 
-    /**
-     * @param float|int $dataProp
-     */
-    private function writeSummaryPropOle($dataProp, int &$dataSection_NumProps, array &$dataSection, int $sumdata, int $typdata): void
+    private function writeSummaryPropOle(float|int $dataProp, int &$dataSection_NumProps, array &$dataSection, int $sumdata, int $typdata): void
     {
         if ($dataProp) {
             $dataSection[] = [
@@ -804,10 +780,8 @@ class Xls extends BaseWriter
 
     /**
      * Build the OLE Part for Summary Information.
-     *
-     * @return string
      */
-    private function writeSummaryInformation()
+    private function writeSummaryInformation(): string
     {
         // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
         $data = pack('v', 0xFFFE);
@@ -843,14 +817,14 @@ class Xls extends BaseWriter
         ++$dataSection_NumProps;
 
         $props = $this->spreadsheet->getProperties();
-        $this->writeSummaryProp($props->getTitle(), $dataSection_NumProps, $dataSection, 0x02, 0x1e);
-        $this->writeSummaryProp($props->getSubject(), $dataSection_NumProps, $dataSection, 0x03, 0x1e);
-        $this->writeSummaryProp($props->getCreator(), $dataSection_NumProps, $dataSection, 0x04, 0x1e);
-        $this->writeSummaryProp($props->getKeywords(), $dataSection_NumProps, $dataSection, 0x05, 0x1e);
-        $this->writeSummaryProp($props->getDescription(), $dataSection_NumProps, $dataSection, 0x06, 0x1e);
-        $this->writeSummaryProp($props->getLastModifiedBy(), $dataSection_NumProps, $dataSection, 0x08, 0x1e);
-        $this->writeSummaryPropOle($props->getCreated(), $dataSection_NumProps, $dataSection, 0x0c, 0x40);
-        $this->writeSummaryPropOle($props->getModified(), $dataSection_NumProps, $dataSection, 0x0d, 0x40);
+        $this->writeSummaryProp($props->getTitle(), $dataSection_NumProps, $dataSection, 0x02, 0x1E);
+        $this->writeSummaryProp($props->getSubject(), $dataSection_NumProps, $dataSection, 0x03, 0x1E);
+        $this->writeSummaryProp($props->getCreator(), $dataSection_NumProps, $dataSection, 0x04, 0x1E);
+        $this->writeSummaryProp($props->getKeywords(), $dataSection_NumProps, $dataSection, 0x05, 0x1E);
+        $this->writeSummaryProp($props->getDescription(), $dataSection_NumProps, $dataSection, 0x06, 0x1E);
+        $this->writeSummaryProp($props->getLastModifiedBy(), $dataSection_NumProps, $dataSection, 0x08, 0x1E);
+        $this->writeSummaryPropOle($props->getCreated(), $dataSection_NumProps, $dataSection, 0x0C, 0x40);
+        $this->writeSummaryPropOle($props->getModified(), $dataSection_NumProps, $dataSection, 0x0D, 0x40);
 
         //    Security
         $dataSection[] = [
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
index 4a7e065..8db570d 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/BIFFwriter.php
@@ -41,33 +41,25 @@ class BIFFwriter
 {
     /**
      * The byte order of this architecture. 0 => little endian, 1 => big endian.
-     *
-     * @var int
      */
-    private static $byteOrder;
+    private static ?int $byteOrder;
 
     /**
      * The string containing the data of the BIFF stream.
-     *
-     * @var null|string
      */
-    public $_data;
+    public ?string $_data;
 
     /**
      * The size of the data in bytes. Should be the same as strlen($this->_data).
-     *
-     * @var int
      */
-    public $_datasize;
+    public int $_datasize;
 
     /**
      * The maximum length for a BIFF record (excluding record header and length field). See addContinue().
      *
-     * @var int
-     *
      * @see addContinue()
      */
-    private $limit = 8224;
+    private int $limit = 8224;
 
     /**
      * Constructor.
@@ -81,10 +73,8 @@ class BIFFwriter
     /**
      * Determine the byte order and store it as class data to avoid
      * recalculating it for each call to new().
-     *
-     * @return int
      */
-    public static function getByteOrder()
+    public static function getByteOrder(): int
     {
         if (!isset(self::$byteOrder)) {
             // Check if "pack" gives the required IEEE 64bit float
@@ -109,7 +99,7 @@ class BIFFwriter
      *
      * @param string $data binary data to append
      */
-    protected function append($data): void
+    protected function append(string $data): void
     {
         if (strlen($data) - 4 > $this->limit) {
             $data = $this->addContinue($data);
@@ -122,10 +112,8 @@ class BIFFwriter
      * General storage function like append, but returns string instead of modifying $this->_data.
      *
      * @param string $data binary data to write
-     *
-     * @return string
      */
-    public function writeData($data)
+    public function writeData(string $data): string
     {
         if (strlen($data) - 4 > $this->limit) {
             $data = $this->addContinue($data);
@@ -142,7 +130,7 @@ class BIFFwriter
      * @param int $type type of BIFF file to write: 0x0005 Workbook,
      *                       0x0010 Worksheet
      */
-    protected function storeBof($type): void
+    protected function storeBof(int $type): void
     {
         $record = 0x0809; // Record identifier    (BIFF5-BIFF8)
         $length = 0x0010;
@@ -175,7 +163,7 @@ class BIFFwriter
     /**
      * Writes Excel EOF record to indicate the end of a BIFF stream.
      */
-    public function writeEof()
+    public function writeEof(): string
     {
         $record = 0x000A; // Record identifier
         $length = 0x0000; // Number of bytes to follow
@@ -196,7 +184,7 @@ class BIFFwriter
      *
      * @return string A very convenient string of continue blocks
      */
-    private function addContinue($data)
+    private function addContinue(string $data): string
     {
         $limit = $this->limit;
         $record = 0x003C; // Record identifier
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php
index 7e9b3cf..fee123f 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php
@@ -9,7 +9,7 @@ class CellDataValidation
     /**
      * @var array<string, int>
      */
-    protected static $validationTypeMap = [
+    protected static array $validationTypeMap = [
         DataValidation::TYPE_NONE => 0x00,
         DataValidation::TYPE_WHOLE => 0x01,
         DataValidation::TYPE_DECIMAL => 0x02,
@@ -23,7 +23,7 @@ class CellDataValidation
     /**
      * @var array<string, int>
      */
-    protected static $errorStyleMap = [
+    protected static array $errorStyleMap = [
         DataValidation::STYLE_STOP => 0x00,
         DataValidation::STYLE_WARNING => 0x01,
         DataValidation::STYLE_INFORMATION => 0x02,
@@ -32,7 +32,7 @@ class CellDataValidation
     /**
      * @var array<string, int>
      */
-    protected static $operatorMap = [
+    protected static array $operatorMap = [
         DataValidation::OPERATOR_BETWEEN => 0x00,
         DataValidation::OPERATOR_NOTBETWEEN => 0x01,
         DataValidation::OPERATOR_EQUAL => 0x02,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php
index 0f78b8c..c33d0d0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ConditionalHelper.php
@@ -9,40 +9,23 @@ class ConditionalHelper
 {
     /**
      * Formula parser.
-     *
-     * @var Parser
      */
-    protected $parser;
+    protected Parser $parser;
 
-    /**
-     * @var mixed
-     */
-    protected $condition;
+    protected mixed $condition;
 
-    /**
-     * @var string
-     */
-    protected $cellRange;
+    protected string $cellRange;
 
-    /**
-     * @var null|string
-     */
-    protected $tokens;
+    protected ?string $tokens = null;
 
-    /**
-     * @var int
-     */
-    protected $size;
+    protected int $size;
 
     public function __construct(Parser $parser)
     {
         $this->parser = $parser;
     }
 
-    /**
-     * @param mixed $condition
-     */
-    public function processCondition($condition, string $cellRange): void
+    public function processCondition(mixed $condition, string $cellRange): void
     {
         $this->condition = $condition;
         $this->cellRange = $cellRange;
@@ -52,11 +35,13 @@ class ConditionalHelper
             $this->tokens = pack('Cv', 0x1E, $condition);
         } else {
             try {
-                $formula = Wizard\WizardAbstract::reverseAdjustCellRef((string) $condition, $cellRange);
+                /** @var float|int|string */
+                $conditionx = $condition;
+                $formula = Wizard\WizardAbstract::reverseAdjustCellRef((string) $conditionx, $cellRange);
                 $this->parser->parse($formula);
                 $this->tokens = $this->parser->toReversePolish();
                 $this->size = strlen($this->tokens ?? '');
-            } catch (PhpSpreadsheetException $e) {
+            } catch (PhpSpreadsheetException) {
                 // In the event of a parser error with a formula value, we set the expression to ptgInt + 0
                 $this->tokens = pack('Cv', 0x1E, 0);
                 $this->size = 3;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php
index 7a864f5..e74f4f4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php
@@ -7,7 +7,7 @@ class ErrorCode
     /**
      * @var array<string, int>
      */
-    protected static $errorCodeMap = [
+    protected static array $errorCodeMap = [
         '#NULL!' => 0x00,
         '#DIV/0!' => 0x07,
         '#VALUE!' => 0x0F,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php
index d5d60c6..c3993be 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Escher.php
@@ -3,6 +3,7 @@
 namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
 
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\Escher as SharedEscher;
 use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
 use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
 use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
@@ -16,49 +17,41 @@ class Escher
     /**
      * The object we are writing.
      */
-    private $object;
+    private Blip|BSE|BstoreContainer|DgContainer|DggContainer|Escher|SpContainer|SpgrContainer|SharedEscher $object;
 
     /**
      * The written binary data.
      */
-    private $data;
+    private string $data;
 
     /**
      * Shape offsets. Positions in binary stream where a new shape record begins.
-     *
-     * @var array
      */
-    private $spOffsets;
+    private array $spOffsets;
 
     /**
      * Shape types.
-     *
-     * @var array
      */
-    private $spTypes;
+    private array $spTypes;
 
     /**
      * Constructor.
-     *
-     * @param mixed $object
      */
-    public function __construct($object)
+    public function __construct(Blip|BSE|BstoreContainer|DgContainer|DggContainer|self|SpContainer|SpgrContainer|SharedEscher $object)
     {
         $this->object = $object;
     }
 
     /**
      * Process the object to be written.
-     *
-     * @return string
      */
-    public function close()
+    public function close(): string
     {
         // initialize
         $this->data = '';
 
-        switch (get_class($this->object)) {
-            case \PhpOffice\PhpSpreadsheet\Shared\Escher::class:
+        switch ($this->object::class) {
+            case SharedEscher::class:
                 if ($dggContainer = $this->object->getDggContainer()) {
                     $writer = new self($dggContainer);
                     $this->data = $writer->close();
@@ -85,8 +78,8 @@ class Escher
                 $recVerInstance |= $recInstance << 4;
 
                 // dgg data
-                $dggData =
-                    pack(
+                $dggData
+                    = pack(
                         'VVVV',
                         $this->object->getSpIdMax(), // maximum shape identifier increased by one
                         $this->object->getCDgSaved() + 1, // number of file identifier clusters increased by one
@@ -282,7 +275,7 @@ class Escher
                 $header = pack('vvV', $recVerInstance, $recType, $length);
 
                 // number of shapes in this drawing (including group shape)
-                $countShapes = count($this->object->getSpgrContainer()->getChildren());
+                $countShapes = count($this->object->getSpgrContainerOrThrow()->getChildren());
                 $innerData .= $header . pack('VV', $countShapes, $this->object->getLastSpId());
 
                 // write the spgrContainer
@@ -488,20 +481,16 @@ class Escher
 
     /**
      * Gets the shape offsets.
-     *
-     * @return array
      */
-    public function getSpOffsets()
+    public function getSpOffsets(): array
     {
         return $this->spOffsets;
     }
 
     /**
      * Gets the shape types.
-     *
-     * @return array
      */
-    public function getSpTypes()
+    public function getSpTypes(): array
     {
         return $this->spTypes;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php
index 1923200..9a1aa8a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Font.php
@@ -8,17 +8,13 @@ class Font
 {
     /**
      * Color index.
-     *
-     * @var int
      */
-    private $colorIndex;
+    private int $colorIndex;
 
     /**
      * Font.
-     *
-     * @var \PhpOffice\PhpSpreadsheet\Style\Font
      */
-    private $font;
+    private \PhpOffice\PhpSpreadsheet\Style\Font $font;
 
     /**
      * Constructor.
@@ -31,23 +27,18 @@ class Font
 
     /**
      * Set the color index.
-     *
-     * @param int $colorIndex
      */
-    public function setColorIndex($colorIndex): void
+    public function setColorIndex(int $colorIndex): void
     {
         $this->colorIndex = $colorIndex;
     }
 
-    /** @var int */
-    private static $notImplemented = 0;
+    private static int $notImplemented = 0;
 
     /**
      * Get font record data.
-     *
-     * @return string
      */
-    public function writeFont()
+    public function writeFont(): string
     {
         $font_outline = self::$notImplemented;
         $font_shadow = self::$notImplemented;
@@ -120,7 +111,7 @@ class Font
      *
      * @var int[]
      */
-    private static $mapUnderline = [
+    private static array $mapUnderline = [
         \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE => 0x00,
         \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE => 0x01,
         \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE => 0x02,
@@ -130,12 +121,8 @@ class Font
 
     /**
      * Map underline.
-     *
-     * @param string $underline
-     *
-     * @return int
      */
-    private static function mapUnderline($underline)
+    private static function mapUnderline(string $underline): int
     {
         if (isset(self::$mapUnderline[$underline])) {
             return self::$mapUnderline[$underline];
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php
index 3fe9e87..9e9b6fe 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php
@@ -49,59 +49,43 @@ class Parser
 
     /**
      * The index of the character we are currently looking at.
-     *
-     * @var int
      */
-    public $currentCharacter;
+    public int $currentCharacter;
 
     /**
      * The token we are working on.
-     *
-     * @var string
      */
-    public $currentToken;
+    public string $currentToken;
 
     /**
      * The formula to parse.
-     *
-     * @var string
      */
-    private $formula;
+    private string $formula;
 
     /**
      * The character ahead of the current char.
-     *
-     * @var string
      */
-    public $lookAhead;
+    public string $lookAhead;
 
     /**
      * The parse tree to be generated.
-     *
-     * @var array|string
      */
-    public $parseTree;
+    public array|string $parseTree;
 
     /**
      * Array of external sheets.
-     *
-     * @var array
      */
-    private $externalSheets;
+    private array $externalSheets;
 
     /**
      * Array of sheet references in the form of REF structures.
-     *
-     * @var array
      */
-    public $references;
+    public array $references;
 
     /**
      * The Excel ptg indices.
-     *
-     * @var array
      */
-    private $ptg = [
+    private array $ptg = [
         'ptgExp' => 0x01,
         'ptgTbl' => 0x02,
         'ptgAdd' => 0x03,
@@ -212,10 +196,8 @@ class Parser
      *           -1  is a variable  number of arguments.
      * class: The reference, value or array class of the function args.
      * vol:   The function is volatile.
-     *
-     * @var array
      */
-    private $functions = [
+    private array $functions = [
         // function                  ptg  args  class  vol
         'COUNT' => [0, -1, 0, 0],
         'IF' => [1, -1, 1, 0],
@@ -469,8 +451,7 @@ class Parser
         'BAHTTEXT' => [368, 1, 0, 0],
     ];
 
-    /** @var Spreadsheet */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     /**
      * The class constructor.
@@ -491,51 +472,68 @@ class Parser
     /**
      * Convert a token to the proper ptg value.
      *
-     * @param mixed $token the token to convert
+     * @param string $token the token to convert
      *
-     * @return mixed the converted token on success
+     * @return string the converted token on success
      */
-    private function convert($token)
+    private function convert(string $token): string
     {
         if (preg_match('/"([^"]|""){0,255}"/', $token)) {
             return $this->convertString($token);
-        } elseif (is_numeric($token)) {
+        }
+        if (is_numeric($token)) {
             return $this->convertNumber($token);
+        }
         // match references like A1 or $A$1
-        } elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/', $token)) {
+        if (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/', $token)) {
             return $this->convertRef2d($token);
+        }
         // match external references like Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1
-        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?(\\d+)$/u', $token)) {
+        if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?(\\d+)$/u', $token)) {
             return $this->convertRef3d($token);
+        }
         // match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1
-        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\\d+)$/u", $token)) {
+        if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\\d+)$/u", $token)) {
             return $this->convertRef3d($token);
+        }
         // match ranges like A1:B2 or $A$1:$B$2
-        } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $token)) {
+        if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $token)) {
             return $this->convertRange2d($token);
+        }
         // match external ranges like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
-        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)\\:\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)$/u', $token)) {
+        if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)\\:\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)$/u', $token)) {
             return $this->convertRange3d($token);
+        }
         // match external ranges like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
-        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)\\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)$/u", $token)) {
+        if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)\\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)$/u", $token)) {
             return $this->convertRange3d($token);
+        }
         // operators (including parentheses)
-        } elseif (isset($this->ptg[$token])) {
+        if (isset($this->ptg[$token])) {
             return pack('C', $this->ptg[$token]);
+        }
         // match error codes
-        } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token == '#N/A') {
+        if (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token == '#N/A') {
             return $this->convertError($token);
-        } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
+        }
+        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
             return $this->convertDefinedName($token);
+        }
         // commented so argument number can be processed correctly. See toReversePolish().
-            /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token))
-            {
-                return($this->convertFunction($token, $this->_func_args));
-            }*/
+        /*if (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token))
+        {
+            return($this->convertFunction($token, $this->_func_args));
+        }*/
         // if it's an argument, ignore the token (the argument remains)
-        } elseif ($token == 'arg') {
+        if ($token == 'arg') {
             return '';
         }
+        if (preg_match('/^true$/i', $token)) {
+            return $this->convertBool(1);
+        }
+        if (preg_match('/^false$/i', $token)) {
+            return $this->convertBool(0);
+        }
 
         // TODO: use real error codes
         throw new WriterException("Unknown token $token");
@@ -544,33 +542,36 @@ class Parser
     /**
      * Convert a number token to ptgInt or ptgNum.
      *
-     * @param mixed $num an integer or double for conversion to its ptg value
-     *
-     * @return string
+     * @param float|int|string $num an integer or double for conversion to its ptg value
      */
-    private function convertNumber($num)
+    private function convertNumber(mixed $num): string
     {
         // Integer in the range 0..2**16-1
-        if ((preg_match('/^\\d+$/', $num)) && ($num <= 65535)) {
+        if ((preg_match('/^\\d+$/', (string) $num)) && ($num <= 65535)) {
             return pack('Cv', $this->ptg['ptgInt'], $num);
         }
 
         // A float
         if (BIFFwriter::getByteOrder()) { // if it's Big Endian
-            $num = strrev($num);
+            $num = strrev((string) $num);
         }
 
         return pack('Cd', $this->ptg['ptgNum'], $num);
     }
 
+    private function convertBool(int $num): string
+    {
+        return pack('CC', $this->ptg['ptgBool'], $num);
+    }
+
     /**
      * Convert a string token to ptgStr.
      *
      * @param string $string a string for conversion to its ptg value
      *
-     * @return mixed the converted token on success
+     * @return string the converted token
      */
-    private function convertString($string)
+    private function convertString(string $string): string
     {
         // chop away beggining and ending quotes
         $string = substr($string, 1, -1);
@@ -590,7 +591,7 @@ class Parser
      *
      * @return string The packed ptg for the function
      */
-    private function convertFunction($token, $num_args)
+    private function convertFunction(string $token, int $num_args): string
     {
         $args = $this->functions[$token][1];
 
@@ -607,11 +608,8 @@ class Parser
      * Convert an Excel range such as A1:D4 to a ptgRefV.
      *
      * @param string $range An Excel range in the A1:A2
-     * @param int $class
-     *
-     * @return string
      */
-    private function convertRange2d($range, $class = 0)
+    private function convertRange2d(string $range, int $class = 0): string
     {
         // TODO: possible class value 0,1,2 check Formula.pm
         // Split the range into 2 cell refs
@@ -621,7 +619,6 @@ class Parser
             // TODO: use real error codes
             throw new WriterException('Unknown range separator');
         }
-
         // Convert the cell references
         [$row1, $col1] = $this->cellToPackedRowcol($cell1);
         [$row2, $col2] = $this->cellToPackedRowcol($cell2);
@@ -647,18 +644,18 @@ class Parser
      *
      * @param string $token an Excel range in the Sheet1!A1:A2 format
      *
-     * @return mixed the packed ptgArea3d token on success
+     * @return string the packed ptgArea3d token on success
      */
-    private function convertRange3d($token)
+    private function convertRange3d(string $token): string
     {
         // Split the ref at the ! symbol
         [$ext_ref, $range] = PhpspreadsheetWorksheet::extractSheetTitle($token, true);
 
         // Convert the external reference part (different for BIFF8)
-        $ext_ref = $this->getRefIndex($ext_ref);
+        $ext_ref = $this->getRefIndex($ext_ref ?? '');
 
         // Split the range into 2 cell refs
-        [$cell1, $cell2] = explode(':', $range);
+        [$cell1, $cell2] = explode(':', $range ?? '');
 
         // Convert the cell references
         if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\\d+)$/', $cell1)) {
@@ -681,7 +678,7 @@ class Parser
      *
      * @return string The cell in packed() format with the corresponding ptg
      */
-    private function convertRef2d($cell)
+    private function convertRef2d(string $cell): string
     {
         // Convert the cell reference
         $cell_array = $this->cellToPackedRowcol($cell);
@@ -699,18 +696,18 @@ class Parser
      *
      * @param string $cell An Excel cell reference
      *
-     * @return mixed the packed ptgRef3d token on success
+     * @return string the packed ptgRef3d token on success
      */
-    private function convertRef3d($cell)
+    private function convertRef3d(string $cell): string
     {
         // Split the ref at the ! symbol
         [$ext_ref, $cell] = PhpspreadsheetWorksheet::extractSheetTitle($cell, true);
 
         // Convert the external reference part (different for BIFF8)
-        $ext_ref = $this->getRefIndex($ext_ref);
+        $ext_ref = $this->getRefIndex($ext_ref ?? '');
 
         // Convert the cell reference part
-        [$row, $col] = $this->cellToPackedRowcol($cell);
+        [$row, $col] = $this->cellToPackedRowcol($cell ?? '');
 
         // The ptg value depends on the class of the ptg.
         $ptgRef = pack('C', $this->ptg['ptgRef3dA']);
@@ -725,49 +722,45 @@ class Parser
      *
      * @return string The error code ptgErr
      */
-    private function convertError($errorCode)
+    private function convertError(string $errorCode): string
     {
-        switch ($errorCode) {
-            case '#NULL!':
-                return pack('C', 0x00);
-            case '#DIV/0!':
-                return pack('C', 0x07);
-            case '#VALUE!':
-                return pack('C', 0x0F);
-            case '#REF!':
-                return pack('C', 0x17);
-            case '#NAME?':
-                return pack('C', 0x1D);
-            case '#NUM!':
-                return pack('C', 0x24);
-            case '#N/A':
-                return pack('C', 0x2A);
-        }
-
-        return pack('C', 0xFF);
+        return match ($errorCode) {
+            '#NULL!' => pack('C', 0x00),
+            '#DIV/0!' => pack('C', 0x07),
+            '#VALUE!' => pack('C', 0x0F),
+            '#REF!' => pack('C', 0x17),
+            '#NAME?' => pack('C', 0x1D),
+            '#NUM!' => pack('C', 0x24),
+            '#N/A' => pack('C', 0x2A),
+            default => pack('C', 0xFF),
+        };
     }
 
+    private bool $tryDefinedName = false;
+
     private function convertDefinedName(string $name): string
     {
         if (strlen($name) > 255) {
             throw new WriterException('Defined Name is too long');
         }
 
-        throw new WriterException('Cannot yet write formulae with defined names to Xls');
-        /*
-        $nameReference = 1;
-        foreach ($this->spreadsheet->getDefinedNames() as $definedName) {
-            if ($name === $definedName->getName()) {
-                break;
+        if ($this->tryDefinedName) {
+            // @codeCoverageIgnoreStart
+            $nameReference = 1;
+            foreach ($this->spreadsheet->getDefinedNames() as $definedName) {
+                if ($name === $definedName->getName()) {
+                    break;
+                }
+                ++$nameReference;
             }
-            ++$nameReference;
+
+            $ptgRef = pack('Cvxx', $this->ptg['ptgName'], $nameReference);
+
+            return $ptgRef;
+            // @codeCoverageIgnoreEnd
         }
 
-        $ptgRef = pack('Cvxx', $this->ptg['ptgName'], $nameReference);
-
-
-        return $ptgRef;
-        */
+        throw new WriterException('Cannot yet write formulae with defined names to Xls');
     }
 
     /**
@@ -777,9 +770,9 @@ class Parser
      *
      * @param string $ext_ref The name of the external reference
      *
-     * @return mixed The reference index in packed() format on success
+     * @return string The reference index in packed() format on success
      */
-    private function getRefIndex($ext_ref)
+    private function getRefIndex(string $ext_ref): string
     {
         $ext_ref = (string) preg_replace(["/^'/", "/'$/"], ['', ''], $ext_ref); // Remove leading and trailing ' if any.
         $ext_ref = str_replace('\'\'', '\'', $ext_ref); // Replace escaped '' with '
@@ -839,7 +832,7 @@ class Parser
      *
      * @return int The sheet index, -1 if the sheet was not found
      */
-    private function getSheetIndex($sheet_name)
+    private function getSheetIndex(string $sheet_name): int
     {
         if (!isset($this->externalSheets[$sheet_name])) {
             return -1;
@@ -856,9 +849,9 @@ class Parser
      * @param string $name The name of the worksheet being added
      * @param int $index The index of the worksheet being added
      *
-     * @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::addWorksheet()
+     * @see Workbook::addWorksheet
      */
-    public function setExtSheet($name, $index): void
+    public function setExtSheet(string $name, int $index): void
     {
         $this->externalSheets[$name] = $index;
     }
@@ -870,7 +863,7 @@ class Parser
      *
      * @return array Array containing the row and column in packed() format
      */
-    private function cellToPackedRowcol($cell)
+    private function cellToPackedRowcol(string $cell): array
     {
         $cell = strtoupper($cell);
         [$row, $col, $row_rel, $col_rel] = $this->cellToRowcol($cell);
@@ -899,7 +892,7 @@ class Parser
      *
      * @return array Array containing (row1,col1,row2,col2) in packed() format
      */
-    private function rangeToPackedRange($range)
+    private function rangeToPackedRange(string $range): array
     {
         preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match);
         // return absolute rows if there is a $ in the ref
@@ -937,10 +930,8 @@ class Parser
      * whether the row or column are relative references.
      *
      * @param string $cell the Excel cell reference in A1 format
-     *
-     * @return array
      */
-    private function cellToRowcol($cell)
+    private function cellToRowcol(string $cell): array
     {
         preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/', $cell, $match);
         // return absolute column if there is a $ in the ref
@@ -1008,17 +999,16 @@ class Parser
             }
             ++$i;
         }
-        //die("Lexical error ".$this->currentCharacter);
     }
 
     /**
      * Checks if it's a valid token.
      *
-     * @param mixed $token the token to check
+     * @param string $token the token to check
      *
-     * @return mixed The checked token or false on failure
+     * @return string The checked token or empty string on failure
      */
-    private function match($token)
+    private function match(string $token): string
     {
         switch ($token) {
             case '+':
@@ -1052,48 +1042,64 @@ class Parser
                 }
 
                 return $token;
-
-            default:
-                // if it's a reference A1 or $A$1 or $A1 or A$1
-                if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.') && ($this->lookAhead !== '!')) {
-                    return $token;
-                } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
-                    // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
-                    return $token;
-                } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
-                    // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
-                    return $token;
-                } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) {
-                    // if it's a range A1:A2 or $A$1:$A$2
-                    return $token;
-                } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead)) {
-                    // If it's an external range like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
-                    return $token;
-                } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead)) {
-                    // If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
-                    return $token;
-                } elseif (is_numeric($token) && (!is_numeric($token . $this->lookAhead) || ($this->lookAhead == '')) && ($this->lookAhead !== '!') && ($this->lookAhead !== ':')) {
-                    // If it's a number (check that it's not a sheet name or range)
-                    return $token;
-                } elseif (preg_match('/"([^"]|""){0,255}"/', $token) && $this->lookAhead !== '"' && (substr_count($token, '"') % 2 == 0)) {
-                    // If it's a string (of maximum 255 characters)
-                    return $token;
-                } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token === '#N/A') {
-                    // If it's an error code
-                    return $token;
-                } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) && ($this->lookAhead === '(')) {
-                    // if it's a function call
-                    return $token;
-                } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
-                    return $token;
-                } elseif (substr($token, -1) === ')') {
-                    //    It's an argument of some description (e.g. a named range),
-                    //        precise nature yet to be determined
-                    return $token;
-                }
-
-                return '';
         }
+
+        // if it's a reference A1 or $A$1 or $A1 or A$1
+        if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.') && ($this->lookAhead !== '!')) {
+            return $token;
+        }
+        // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
+        if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
+            return $token;
+        }
+        // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
+        if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
+            return $token;
+        }
+        // if it's a range A1:A2 or $A$1:$A$2
+        if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) {
+            return $token;
+        }
+        // If it's an external range like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
+        if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead)) {
+            return $token;
+        }
+        // If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
+        if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead)) {
+            return $token;
+        }
+        // If it's a number (check that it's not a sheet name or range)
+        if (is_numeric($token) && (!is_numeric($token . $this->lookAhead) || ($this->lookAhead == '')) && ($this->lookAhead !== '!') && ($this->lookAhead !== ':')) {
+            return $token;
+        }
+        if (preg_match('/"([^"]|""){0,255}"/', $token) && $this->lookAhead !== '"' && (substr_count($token, '"') % 2 == 0)) {
+            // If it's a string (of maximum 255 characters)
+            return $token;
+        }
+        // If it's an error code
+        if (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token === '#N/A') {
+            return $token;
+        }
+        // if it's a function call
+        if (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) && ($this->lookAhead === '(')) {
+            return $token;
+        }
+        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
+            return $token;
+        }
+        if (preg_match('/^true$/i', $token) && ($this->lookAhead === ')' || $this->lookAhead === ',')) {
+            return $token;
+        }
+        if (preg_match('/^false$/i', $token) && ($this->lookAhead === ')' || $this->lookAhead === ',')) {
+            return $token;
+        }
+        if (str_ends_with($token, ')')) {
+            //    It's an argument of some description (e.g. a named range),
+            //        precise nature yet to be determined
+            return $token;
+        }
+
+        return '';
     }
 
     /**
@@ -1102,9 +1108,9 @@ class Parser
      * @param string $formula the formula to parse, without the initial equal
      *                        sign (=)
      *
-     * @return mixed true on success
+     * @return bool true on success
      */
-    public function parse($formula)
+    public function parse(string $formula): bool
     {
         $this->currentCharacter = 0;
         $this->formula = (string) $formula;
@@ -1119,9 +1125,9 @@ class Parser
      * It parses a condition. It assumes the following rule:
      * Cond -> Expr [(">" | "<") Expr].
      *
-     * @return mixed The parsed ptg'd tree on success
+     * @return array The parsed ptg'd tree on success
      */
-    private function condition()
+    private function condition(): array
     {
         $result = $this->expression();
         if ($this->currentToken == '<') {
@@ -1161,9 +1167,9 @@ class Parser
      *      -> "+" Term : Positive value
      *      -> Error code.
      *
-     * @return mixed The parsed ptg'd tree on success
+     * @return array The parsed ptg'd tree on success
      */
-    private function expression()
+    private function expression(): array
     {
         // If it's a string return a string node
         if (preg_match('/"([^"]|""){0,255}"/', $this->currentToken)) {
@@ -1176,21 +1182,18 @@ class Parser
             $this->advance();
 
             return $result;
-        // If it's an error code
-        } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $this->currentToken) || $this->currentToken == '#N/A') {
+        } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $this->currentToken) || $this->currentToken == '#N/A') { // error code
             $result = $this->createTree($this->currentToken, 'ptgErr', '');
             $this->advance();
 
             return $result;
-        // If it's a negative value
-        } elseif ($this->currentToken == '-') {
+        } elseif ($this->currentToken == '-') { // negative value
             // catch "-" Term
             $this->advance();
             $result2 = $this->expression();
 
             return $this->createTree('ptgUminus', $result2, '');
-        // If it's a positive value
-        } elseif ($this->currentToken == '+') {
+        } elseif ($this->currentToken == '+') { // positive value
             // catch "+" Term
             $this->advance();
             $result2 = $this->expression();
@@ -1204,9 +1207,9 @@ class Parser
             $result = $this->createTree('ptgConcat', $result, $result2);
         }
         while (
-            ($this->currentToken == '+') ||
-            ($this->currentToken == '-') ||
-            ($this->currentToken == '^')
+            ($this->currentToken == '+')
+            || ($this->currentToken == '-')
+            || ($this->currentToken == '^')
         ) {
             if ($this->currentToken == '+') {
                 $this->advance();
@@ -1234,7 +1237,7 @@ class Parser
      *
      * @see fact()
      */
-    private function parenthesizedExpression()
+    private function parenthesizedExpression(): array
     {
         return $this->createTree('ptgParen', $this->expression(), '');
     }
@@ -1243,14 +1246,14 @@ class Parser
      * It parses a term. It assumes the following rule:
      * Term -> Fact [("*" | "/") Fact].
      *
-     * @return mixed The parsed ptg'd tree on success
+     * @return array The parsed ptg'd tree on success
      */
-    private function term()
+    private function term(): array
     {
         $result = $this->fact();
         while (
-            ($this->currentToken == '*') ||
-            ($this->currentToken == '/')
+            ($this->currentToken == '*')
+            || ($this->currentToken == '/')
         ) {
             if ($this->currentToken == '*') {
                 $this->advance();
@@ -1274,9 +1277,9 @@ class Parser
      *       | Number
      *       | Function.
      *
-     * @return mixed The parsed ptg'd tree on success
+     * @return array The parsed ptg'd tree on success
      */
-    private function fact()
+    private function fact(): array
     {
         $currentToken = $this->currentToken;
         if ($currentToken === '(') {
@@ -1295,21 +1298,24 @@ class Parser
             $this->advance();
 
             return $result;
-        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $this->currentToken)) {
+        }
+        if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $this->currentToken)) {
             // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
             $result = $this->createTree($this->currentToken, '', '');
             $this->advance();
 
             return $result;
-        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $this->currentToken)) {
+        }
+        if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $this->currentToken)) {
             // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
             $result = $this->createTree($this->currentToken, '', '');
             $this->advance();
 
             return $result;
-        } elseif (
-            preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken) ||
-            preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken)
+        }
+        if (
+            preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken)
+            || preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken)
         ) {
             // if it's a range A1:B2 or $A$1:$B$2
             // must be an error?
@@ -1317,21 +1323,24 @@ class Parser
             $this->advance();
 
             return $result;
-        } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $this->currentToken)) {
+        }
+        if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $this->currentToken)) {
             // If it's an external range (Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2)
             // must be an error?
             $result = $this->createTree($this->currentToken, '', '');
             $this->advance();
 
             return $result;
-        } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $this->currentToken)) {
+        }
+        if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $this->currentToken)) {
             // If it's an external range ('Sheet1'!A1:B2 or 'Sheet1'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1'!$A$1:$B$2)
             // must be an error?
             $result = $this->createTree($this->currentToken, '', '');
             $this->advance();
 
             return $result;
-        } elseif (is_numeric($this->currentToken)) {
+        }
+        if (is_numeric($this->currentToken)) {
             // If it's a number or a percent
             if ($this->lookAhead === '%') {
                 $result = $this->createTree('ptgPercent', $this->currentToken, '');
@@ -1342,15 +1351,23 @@ class Parser
             $this->advance();
 
             return $result;
-        } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $this->currentToken) && ($this->lookAhead === '(')) {
+        }
+        if (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $this->currentToken) && ($this->lookAhead === '(')) {
             // if it's a function call
             return $this->func();
-        } elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $this->currentToken) && $this->spreadsheet->getDefinedName($this->currentToken) !== null) {
+        }
+        if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $this->currentToken) && $this->spreadsheet->getDefinedName($this->currentToken) !== null) {
             $result = $this->createTree('ptgName', $this->currentToken, '');
             $this->advance();
 
             return $result;
         }
+        if (preg_match('/^true|false$/i', $this->currentToken)) {
+            $result = $this->createTree($this->currentToken, '', '');
+            $this->advance();
+
+            return $result;
+        }
 
         throw new WriterException('Syntax error: ' . $this->currentToken . ', lookahead: ' . $this->lookAhead . ', current char: ' . $this->currentCharacter);
     }
@@ -1359,9 +1376,9 @@ class Parser
      * It parses a function call. It assumes the following rule:
      * Func -> ( Expr [,Expr]* ).
      *
-     * @return mixed The parsed ptg'd tree on success
+     * @return array The parsed ptg'd tree on success
      */
-    private function func()
+    private function func(): array
     {
         $num_args = 0; // number of arguments received
         $function = strtoupper($this->currentToken);
@@ -1408,7 +1425,7 @@ class Parser
      *
      * @return array A tree
      */
-    private function createTree($value, $left, $right)
+    private function createTree(mixed $value, mixed $left, mixed $right): array
     {
         return ['value' => $value, 'left' => $left, 'right' => $right];
     }
@@ -1440,7 +1457,7 @@ class Parser
      *
      * @return string The tree in reverse polish notation
      */
-    public function toReversePolish($tree = [])
+    public function toReversePolish(array $tree = []): string
     {
         $polish = ''; // the string we are going to return
         if (empty($tree)) { // If it's the first call use parseTree
@@ -1466,11 +1483,11 @@ class Parser
         }
         // if it's a function convert it here (so we can set it's arguments)
         if (
-            preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/", $tree['value']) &&
-            !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/', $tree['value']) &&
-            !preg_match('/^[A-Ia-i]?[A-Za-z](\\d+)\\.\\.[A-Ia-i]?[A-Za-z](\\d+)$/', $tree['value']) &&
-            !is_numeric($tree['value']) &&
-            !isset($this->ptg[$tree['value']])
+            preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/", $tree['value'])
+            && !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/', $tree['value'])
+            && !preg_match('/^[A-Ia-i]?[A-Za-z](\\d+)\\.\\.[A-Ia-i]?[A-Za-z](\\d+)$/', $tree['value'])
+            && !is_numeric($tree['value'])
+            && !isset($this->ptg[$tree['value']])
         ) {
             // left subtree for a function is always an array.
             if ($tree['left'] != '') {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php
index 711d88d..81da635 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php
@@ -9,7 +9,7 @@ class CellAlignment
     /**
      * @var array<string, int>
      */
-    private static $horizontalMap = [
+    private static array $horizontalMap = [
         Alignment::HORIZONTAL_GENERAL => 0,
         Alignment::HORIZONTAL_LEFT => 1,
         Alignment::HORIZONTAL_RIGHT => 3,
@@ -21,7 +21,7 @@ class CellAlignment
     /**
      * @var array<string, int>
      */
-    private static $verticalMap = [
+    private static array $verticalMap = [
         Alignment::VERTICAL_BOTTOM => 2,
         Alignment::VERTICAL_TOP => 0,
         Alignment::VERTICAL_CENTER => 1,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php
index 8d47d6a..e97a1f7 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php
@@ -9,7 +9,7 @@ class CellBorder
     /**
      * @var array<string, int>
      */
-    protected static $styleMap = [
+    protected static array $styleMap = [
         Border::BORDER_NONE => 0x00,
         Border::BORDER_THIN => 0x01,
         Border::BORDER_MEDIUM => 0x02,
@@ -24,6 +24,7 @@ class CellBorder
         Border::BORDER_DASHDOTDOT => 0x0B,
         Border::BORDER_MEDIUMDASHDOTDOT => 0x0C,
         Border::BORDER_SLANTDASHDOT => 0x0D,
+        Border::BORDER_OMIT => 0x00,
     ];
 
     public static function style(Border $border): int
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php
index f5a8c47..a0a932a 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php
@@ -9,7 +9,7 @@ class CellFill
     /**
      * @var array<string, int>
      */
-    protected static $fillStyleMap = [
+    protected static array $fillStyleMap = [
         Fill::FILL_NONE => 0x00,
         Fill::FILL_SOLID => 0x01,
         Fill::FILL_PATTERN_MEDIUMGRAY => 0x02,
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php
index caf85c0..209d13b 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php
@@ -4,12 +4,20 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xls\Style;
 
 use PhpOffice\PhpSpreadsheet\Style\Color;
 
+/*
+ * Static array incorrectly used by Xls Writer for Conditional Styles.
+ *
+ * @deprecated since version 2.2
+ *
+ * @codecoverageignore
+ */
+
 class ColorMap
 {
     /**
      * @var array<string, int>
      */
-    private static $colorMap = [
+    private static array $colorMap = [
         '#000000' => 0x08,
         '#FFFFFF' => 0x09,
         '#FF0000' => 0x0A,
@@ -70,7 +78,7 @@ class ColorMap
 
     public static function lookup(Color $color, int $defaultIndex = 0x00): int
     {
-        $colorRgb = $color->getRGB();
+        $colorRgb = strtoupper($color->getRGB());
         if (is_string($colorRgb) && array_key_exists("#{$colorRgb}", self::$colorMap)) {
             return self::$colorMap["#{$colorRgb}"];
         }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
index fa7349d..a1a5faf 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
@@ -49,132 +49,99 @@ class Workbook extends BIFFwriter
 {
     /**
      * Formula parser.
-     *
-     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
      */
-    private $parser;
+    private Parser $parser;
 
-    /**
-     * The BIFF file size for the workbook.
-     *
-     * @var int
+    /*
+     * The BIFF file size for the workbook. Not currently used.
      *
      * @see calcSheetOffsets()
      */
-    private $biffSize;
+    //private int $biffSize;
 
     /**
      * XF Writers.
      *
-     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Xf[]
+     * @var Xf[]
      */
-    private $xfWriters = [];
+    private array $xfWriters = [];
 
     /**
      * Array containing the colour palette.
-     *
-     * @var array
      */
-    private $palette;
+    private array $palette;
 
     /**
      * The codepage indicates the text encoding used for strings.
-     *
-     * @var int
      */
-    private $codepage;
+    private int $codepage;
 
     /**
      * The country code used for localization.
-     *
-     * @var int
      */
-    private $countryCode;
+    private int $countryCode;
 
     /**
      * Workbook.
-     *
-     * @var Spreadsheet
      */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     /**
      * Fonts writers.
      *
      * @var Font[]
      */
-    private $fontWriters = [];
+    private array $fontWriters = [];
 
     /**
      * Added fonts. Maps from font's hash => index in workbook.
-     *
-     * @var array
      */
-    private $addedFonts = [];
+    private array $addedFonts = [];
 
     /**
      * Shared number formats.
-     *
-     * @var array
      */
-    private $numberFormats = [];
+    private array $numberFormats = [];
 
     /**
      * Added number formats. Maps from numberFormat's hash => index in workbook.
-     *
-     * @var array
      */
-    private $addedNumberFormats = [];
+    private array $addedNumberFormats = [];
 
     /**
      * Sizes of the binary worksheet streams.
-     *
-     * @var array
      */
-    private $worksheetSizes = [];
+    private array $worksheetSizes = [];
 
     /**
      * Offsets of the binary worksheet streams relative to the start of the global workbook stream.
-     *
-     * @var array
      */
-    private $worksheetOffsets = [];
+    private array $worksheetOffsets = [];
 
     /**
      * Total number of shared strings in workbook.
-     *
-     * @var int
      */
-    private $stringTotal;
+    private int $stringTotal;
 
     /**
      * Number of unique shared strings in workbook.
-     *
-     * @var int
      */
-    private $stringUnique;
+    private int $stringUnique;
 
     /**
      * Array of unique shared strings in workbook.
-     *
-     * @var array
      */
-    private $stringTable;
+    private array $stringTable;
 
     /**
      * Color cache.
      */
-    private $colors;
+    private array $colors;
 
     /**
      * Escher object corresponding to MSODRAWINGGROUP.
-     *
-     * @var null|\PhpOffice\PhpSpreadsheet\Shared\Escher
      */
-    private $escher;
-
-    /** @var mixed */
-    private static $scrutinizerFalse = false;
+    private ?\PhpOffice\PhpSpreadsheet\Shared\Escher $escher = null;
 
     /**
      * Class constructor.
@@ -186,13 +153,13 @@ class Workbook extends BIFFwriter
      * @param array $colors Colour Table
      * @param Parser $parser The formula parser created for the Workbook
      */
-    public function __construct(Spreadsheet $spreadsheet, &$str_total, &$str_unique, &$str_table, &$colors, Parser $parser)
+    public function __construct(Spreadsheet $spreadsheet, int &$str_total, int &$str_unique, array &$str_table, array &$colors, Parser $parser)
     {
         // It needs to call its parent's constructor explicitly
         parent::__construct();
 
         $this->parser = $parser;
-        $this->biffSize = 0;
+        //$this->biffSize = 0;
         $this->palette = [];
         $this->countryCode = -1;
 
@@ -231,7 +198,7 @@ class Workbook extends BIFFwriter
      *
      * @return int Index to XF record
      */
-    public function addXfWriter(Style $style, $isStyleXf = false)
+    public function addXfWriter(Style $style, bool $isStyleXf = false): int
     {
         $xfWriter = new Xf($style);
         $xfWriter->setIsStyleXf($isStyleXf);
@@ -252,7 +219,7 @@ class Workbook extends BIFFwriter
         $xfWriter->setDiagColor($this->addColor($style->getBorders()->getDiagonal()->getColor()->getRGB()));
 
         // Add the number format if it is not a built-in one and not already added
-        if ($style->getNumberFormat()->getBuiltInFormatCode() === self::$scrutinizerFalse) {
+        if ($style->getNumberFormat()->getBuiltInFormatCode() === false) {
             $numberFormatHashCode = $style->getNumberFormat()->getHashCode();
 
             if (isset($this->addedNumberFormats[$numberFormatHashCode])) {
@@ -279,7 +246,7 @@ class Workbook extends BIFFwriter
      *
      * @return int Index to FONT record
      */
-    public function addFont(\PhpOffice\PhpSpreadsheet\Style\Font $font)
+    public function addFont(\PhpOffice\PhpSpreadsheet\Style\Font $font): int
     {
         $fontHashCode = $font->getHashCode();
         if (isset($this->addedFonts[$fontHashCode])) {
@@ -305,11 +272,11 @@ class Workbook extends BIFFwriter
      *
      * @return int Color index
      */
-    private function addColor($rgb)
+    public function addColor(string $rgb, int $default = 0): int
     {
         if (!isset($this->colors[$rgb])) {
-            $color =
-                [
+            $color
+                = [
                     hexdec(substr($rgb, 0, 2)),
                     hexdec(substr($rgb, 2, 2)),
                     hexdec(substr($rgb, 4)),
@@ -331,7 +298,7 @@ class Workbook extends BIFFwriter
                     $this->colors[$rgb] = $colorIndex;
                 } else {
                     // no room for more custom colors, just map to black
-                    $colorIndex = 0;
+                    $colorIndex = $default;
                 }
             }
         } else {
@@ -349,51 +316,51 @@ class Workbook extends BIFFwriter
     {
         $this->palette = [
             0x08 => [0x00, 0x00, 0x00, 0x00],
-            0x09 => [0xff, 0xff, 0xff, 0x00],
-            0x0A => [0xff, 0x00, 0x00, 0x00],
-            0x0B => [0x00, 0xff, 0x00, 0x00],
-            0x0C => [0x00, 0x00, 0xff, 0x00],
-            0x0D => [0xff, 0xff, 0x00, 0x00],
-            0x0E => [0xff, 0x00, 0xff, 0x00],
-            0x0F => [0x00, 0xff, 0xff, 0x00],
+            0x09 => [0xFF, 0xFF, 0xFF, 0x00],
+            0x0A => [0xFF, 0x00, 0x00, 0x00],
+            0x0B => [0x00, 0xFF, 0x00, 0x00],
+            0x0C => [0x00, 0x00, 0xFF, 0x00],
+            0x0D => [0xFF, 0xFF, 0x00, 0x00],
+            0x0E => [0xFF, 0x00, 0xFF, 0x00],
+            0x0F => [0x00, 0xFF, 0xFF, 0x00],
             0x10 => [0x80, 0x00, 0x00, 0x00],
             0x11 => [0x00, 0x80, 0x00, 0x00],
             0x12 => [0x00, 0x00, 0x80, 0x00],
             0x13 => [0x80, 0x80, 0x00, 0x00],
             0x14 => [0x80, 0x00, 0x80, 0x00],
             0x15 => [0x00, 0x80, 0x80, 0x00],
-            0x16 => [0xc0, 0xc0, 0xc0, 0x00],
+            0x16 => [0xC0, 0xC0, 0xC0, 0x00],
             0x17 => [0x80, 0x80, 0x80, 0x00],
-            0x18 => [0x99, 0x99, 0xff, 0x00],
+            0x18 => [0x99, 0x99, 0xFF, 0x00],
             0x19 => [0x99, 0x33, 0x66, 0x00],
-            0x1A => [0xff, 0xff, 0xcc, 0x00],
-            0x1B => [0xcc, 0xff, 0xff, 0x00],
+            0x1A => [0xFF, 0xFF, 0xCC, 0x00],
+            0x1B => [0xCC, 0xFF, 0xFF, 0x00],
             0x1C => [0x66, 0x00, 0x66, 0x00],
-            0x1D => [0xff, 0x80, 0x80, 0x00],
-            0x1E => [0x00, 0x66, 0xcc, 0x00],
-            0x1F => [0xcc, 0xcc, 0xff, 0x00],
+            0x1D => [0xFF, 0x80, 0x80, 0x00],
+            0x1E => [0x00, 0x66, 0xCC, 0x00],
+            0x1F => [0xCC, 0xCC, 0xFF, 0x00],
             0x20 => [0x00, 0x00, 0x80, 0x00],
-            0x21 => [0xff, 0x00, 0xff, 0x00],
-            0x22 => [0xff, 0xff, 0x00, 0x00],
-            0x23 => [0x00, 0xff, 0xff, 0x00],
+            0x21 => [0xFF, 0x00, 0xFF, 0x00],
+            0x22 => [0xFF, 0xFF, 0x00, 0x00],
+            0x23 => [0x00, 0xFF, 0xFF, 0x00],
             0x24 => [0x80, 0x00, 0x80, 0x00],
             0x25 => [0x80, 0x00, 0x00, 0x00],
             0x26 => [0x00, 0x80, 0x80, 0x00],
-            0x27 => [0x00, 0x00, 0xff, 0x00],
-            0x28 => [0x00, 0xcc, 0xff, 0x00],
-            0x29 => [0xcc, 0xff, 0xff, 0x00],
-            0x2A => [0xcc, 0xff, 0xcc, 0x00],
-            0x2B => [0xff, 0xff, 0x99, 0x00],
-            0x2C => [0x99, 0xcc, 0xff, 0x00],
-            0x2D => [0xff, 0x99, 0xcc, 0x00],
-            0x2E => [0xcc, 0x99, 0xff, 0x00],
-            0x2F => [0xff, 0xcc, 0x99, 0x00],
-            0x30 => [0x33, 0x66, 0xff, 0x00],
-            0x31 => [0x33, 0xcc, 0xcc, 0x00],
-            0x32 => [0x99, 0xcc, 0x00, 0x00],
-            0x33 => [0xff, 0xcc, 0x00, 0x00],
-            0x34 => [0xff, 0x99, 0x00, 0x00],
-            0x35 => [0xff, 0x66, 0x00, 0x00],
+            0x27 => [0x00, 0x00, 0xFF, 0x00],
+            0x28 => [0x00, 0xCC, 0xFF, 0x00],
+            0x29 => [0xCC, 0xFF, 0xFF, 0x00],
+            0x2A => [0xCC, 0xFF, 0xCC, 0x00],
+            0x2B => [0xFF, 0xFF, 0x99, 0x00],
+            0x2C => [0x99, 0xCC, 0xFF, 0x00],
+            0x2D => [0xFF, 0x99, 0xCC, 0x00],
+            0x2E => [0xCC, 0x99, 0xFF, 0x00],
+            0x2F => [0xFF, 0xCC, 0x99, 0x00],
+            0x30 => [0x33, 0x66, 0xFF, 0x00],
+            0x31 => [0x33, 0xCC, 0xCC, 0x00],
+            0x32 => [0x99, 0xCC, 0x00, 0x00],
+            0x33 => [0xFF, 0xCC, 0x00, 0x00],
+            0x34 => [0xFF, 0x99, 0x00, 0x00],
+            0x35 => [0xFF, 0x66, 0x00, 0x00],
             0x36 => [0x66, 0x66, 0x99, 0x00],
             0x37 => [0x96, 0x96, 0x96, 0x00],
             0x38 => [0x00, 0x33, 0x66, 0x00],
@@ -415,7 +382,7 @@ class Workbook extends BIFFwriter
      *
      * @return string Binary data for workbook stream
      */
-    public function writeWorkbook(array $worksheetSizes)
+    public function writeWorkbook(array $worksheetSizes): string
     {
         $this->worksheetSizes = $worksheetSizes;
 
@@ -485,7 +452,7 @@ class Workbook extends BIFFwriter
             $this->worksheetOffsets[$i] = $offset;
             $offset += $this->worksheetSizes[$i];
         }
-        $this->biffSize = $offset;
+        //$this->biffSize = $offset;
     }
 
     /**
@@ -581,7 +548,7 @@ class Workbook extends BIFFwriter
      * Writes all the DEFINEDNAME records (BIFF8).
      * So far this is only used for repeating rows/columns (print titles) and print areas.
      */
-    private function writeAllDefinedNamesBiff8()
+    private function writeAllDefinedNamesBiff8(): string
     {
         $chunk = '';
 
@@ -603,18 +570,15 @@ class Workbook extends BIFFwriter
                     }
 
                     if ($definedName->getLocalOnly()) {
-                        /**
-                         * local scope.
-                         *
-                         * @phpstan-ignore-next-line
-                         */
-                        $scope = $this->spreadsheet->getIndex($definedName->getScope()) + 1;
+                        // local scope
+                        $scopeWs = $definedName->getScope();
+                        $scope = ($scopeWs === null) ? 0 : ($this->spreadsheet->getIndex($scopeWs) + 1);
                     } else {
                         // global scope
                         $scope = 0;
                     }
                     $chunk .= $this->writeData($this->writeDefinedNameBiff8($definedName->getName(), $formulaData, $scope, false));
-                } catch (PhpSpreadsheetException $e) {
+                } catch (PhpSpreadsheetException) {
                     // do nothing
                 }
             }
@@ -644,9 +608,8 @@ class Workbook extends BIFFwriter
 
                 // store the DEFINEDNAME record
                 $chunk .= $this->writeData($this->writeDefinedNameBiff8(pack('C', 0x07), $formulaData, $i + 1, true));
-
-            // (exclusive) either repeatColumns or repeatRows
             } elseif ($sheetSetup->isColumnsToRepeatAtLeftSet() || $sheetSetup->isRowsToRepeatAtTopSet()) {
+                // (exclusive) either repeatColumns or repeatRows.
                 // Columns to repeat
                 if ($sheetSetup->isColumnsToRepeatAtLeftSet()) {
                     $repeat = $sheetSetup->getColumnsToRepeatAtLeft();
@@ -733,7 +696,7 @@ class Workbook extends BIFFwriter
      *
      * @return string Complete binary record data
      */
-    private function writeDefinedNameBiff8($name, $formulaData, $sheetIndex = 0, $isBuiltIn = false)
+    private function writeDefinedNameBiff8(string $name, string $formulaData, int $sheetIndex = 0, bool $isBuiltIn = false): string
     {
         $record = 0x0018;
 
@@ -762,14 +725,12 @@ class Workbook extends BIFFwriter
     /**
      * Write a short NAME record.
      *
-     * @param string $name
      * @param int $sheetIndex 1-based sheet index the defined name applies to. 0 = global
      * @param int[][] $rangeBounds range boundaries
-     * @param bool $isHidden
      *
      * @return string Complete binary record data
      * */
-    private function writeShortNameBiff8($name, $sheetIndex, $rangeBounds, $isHidden = false)
+    private function writeShortNameBiff8(string $name, int $sheetIndex, array $rangeBounds, bool $isHidden = false): string
     {
         $record = 0x0018;
 
@@ -848,30 +809,16 @@ class Workbook extends BIFFwriter
      *
      * @param int $offset Location of worksheet BOF
      */
-    private function writeBoundSheet(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet, $offset): void
+    private function writeBoundSheet(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet, int $offset): void
     {
         $sheetname = $sheet->getTitle();
         $record = 0x0085; // Record identifier
-
-        // sheet state
-        switch ($sheet->getSheetState()) {
-            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VISIBLE:
-                $ss = 0x00;
-
-                break;
-            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN:
-                $ss = 0x01;
-
-                break;
-            case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VERYHIDDEN:
-                $ss = 0x02;
-
-                break;
-            default:
-                $ss = 0x00;
-
-                break;
-        }
+        $ss = match ($sheet->getSheetState()) {
+            \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VISIBLE => 0x00,
+            \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN => 0x01,
+            \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_VERYHIDDEN => 0x02,
+            default => 0x00,
+        };
 
         // sheet type
         $st = 0x00;
@@ -889,7 +836,7 @@ class Workbook extends BIFFwriter
     /**
      * Write Internal SUPBOOK record.
      */
-    private function writeSupbookInternal()
+    private function writeSupbookInternal(): string
     {
         $record = 0x01AE; // Record identifier
         $length = 0x0004; // Bytes to follow
@@ -904,7 +851,7 @@ class Workbook extends BIFFwriter
      * Writes the Excel BIFF EXTERNSHEET record. These references are used by
      * formulas.
      */
-    private function writeExternalsheetBiff8()
+    private function writeExternalsheetBiff8(): string
     {
         $totalReferences = count($this->parser->references);
         $record = 0x0017; // Record identifier
@@ -930,7 +877,7 @@ class Workbook extends BIFFwriter
 
         $ixfe = 0x8000; // Index to cell style XF
         $BuiltIn = 0x00; // Built-in style
-        $iLevel = 0xff; // Outline style level
+        $iLevel = 0xFF; // Outline style level
 
         $header = pack('vv', $record, $length);
         $data = pack('vCC', $ixfe, $BuiltIn, $iLevel);
@@ -943,7 +890,7 @@ class Workbook extends BIFFwriter
      * @param string $format Custom format string
      * @param int $ifmt Format index code
      */
-    private function writeNumberFormat($format, $ifmt): void
+    private function writeNumberFormat(string $format, int $ifmt): void
     {
         $record = 0x041E; // Record identifier
 
@@ -963,9 +910,9 @@ class Workbook extends BIFFwriter
         $record = 0x0022; // Record identifier
         $length = 0x0002; // Bytes to follow
 
-        $f1904 = (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904)
-            ? 1
-            : 0; // Flag for 1904 date system
+        $f1904 = ($this->spreadsheet->getExcelCalendar() === Date::CALENDAR_MAC_1904)
+            ? 1  // Flag for 1904 date system
+            : 0; // Flag for 1900 date system
 
         $header = pack('vv', $record, $length);
         $data = pack('v', $f1904);
@@ -974,10 +921,8 @@ class Workbook extends BIFFwriter
 
     /**
      * Stores the COUNTRY record for localization.
-     *
-     * @return string
      */
-    private function writeCountry()
+    private function writeCountry(): string
     {
         $record = 0x008C; // Record identifier
         $length = 4; // Number of bytes to follow
@@ -991,10 +936,8 @@ class Workbook extends BIFFwriter
 
     /**
      * Write the RECALCID record.
-     *
-     * @return string
      */
-    private function writeRecalcId()
+    private function writeRecalcId(): string
     {
         $record = 0x01C1; // Record identifier
         $length = 8; // Number of bytes to follow
@@ -1044,7 +987,7 @@ class Workbook extends BIFFwriter
      *
      * @return string Binary data
      */
-    private function writeSharedStringsTable()
+    private function writeSharedStringsTable(): string
     {
         // maximum size of record data (excluding record header)
         $continue_limit = 8224;
@@ -1063,7 +1006,7 @@ class Workbook extends BIFFwriter
             $headerinfo = unpack('vlength/Cencoding', $string);
 
             // currently, this is always 1 = uncompressed
-            $encoding = $headerinfo['encoding'];
+            $encoding = $headerinfo['encoding'] ?? 1;
 
             // initialize finished writing current $string
             $finished = false;
@@ -1103,16 +1046,15 @@ class Workbook extends BIFFwriter
                     // 2. space remaining is greater than or equal to minimum space needed
                     //        here we write as much as we can in the current block, then move to next record data block
 
-                    // 1. space remaining is less than minimum space needed
                     if ($space_remaining < $min_space_needed) {
+                        // 1. space remaining is less than minimum space needed.
                         // we close the block, store the block data
                         $recordDatas[] = $recordData;
 
                         // and start new record data block where we start writing the string
                         $recordData = '';
-
-                    // 2. space remaining is greater than or equal to minimum space needed
                     } else {
+                        // 2. space remaining is greater than or equal to minimum space needed.
                         // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below
                         $effective_space_remaining = $space_remaining;
 
@@ -1136,7 +1078,7 @@ class Workbook extends BIFFwriter
 
         // Store the last record data block unless it is empty
         // if there was no need for any continue records, this will be the for SST record data block itself
-        if (strlen($recordData) > 0) {
+        if ($recordData !== '') {
             $recordDatas[] = $recordData;
         }
 
@@ -1158,7 +1100,7 @@ class Workbook extends BIFFwriter
     /**
      * Writes the MSODRAWINGGROUP record if needed. Possibly split using CONTINUE records.
      */
-    private function writeMsoDrawingGroup()
+    private function writeMsoDrawingGroup(): string
     {
         // write the Escher stream if necessary
         if (isset($this->escher)) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
index 1657eca..1254ca2 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Worksheet.php
@@ -12,7 +12,7 @@ use PhpOffice\PhpSpreadsheet\RichText\Run;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Shared\Xls;
 use PhpOffice\PhpSpreadsheet\Style\Border;
-use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
 use PhpOffice\PhpSpreadsheet\Style\Conditional;
 use PhpOffice\PhpSpreadsheet\Style\Protection;
 use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
@@ -55,166 +55,107 @@ use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
 // */
 class Worksheet extends BIFFwriter
 {
-    /** @var int */
-    private static $always0 = 0;
+    private static int $always0 = 0;
 
-    /** @var int */
-    private static $always1 = 1;
+    private static int $always1 = 1;
 
     /**
      * Formula parser.
-     *
-     * @var \PhpOffice\PhpSpreadsheet\Writer\Xls\Parser
      */
-    private $parser;
-
-    /**
-     * Maximum number of characters for a string (LABEL record in BIFF5).
-     *
-     * @var int
-     */
-    private $xlsStringMaxLength;
+    private Parser $parser;
 
     /**
      * Array containing format information for columns.
-     *
-     * @var array
      */
-    private $columnInfo;
-
-    /**
-     * Array containing the selected area for the worksheet.
-     *
-     * @var array
-     */
-    private $selection;
+    private array $columnInfo;
 
     /**
      * The active pane for the worksheet.
-     *
-     * @var int
      */
-    private $activePane;
+    private int $activePane;
 
     /**
      * Whether to use outline.
-     *
-     * @var bool
      */
-    private $outlineOn;
+    private bool $outlineOn;
 
     /**
      * Auto outline styles.
-     *
-     * @var bool
      */
-    private $outlineStyle;
+    private bool $outlineStyle;
 
     /**
      * Whether to have outline summary below.
-     *
-     * @var bool
+     * Not currently used.
      */
-    private $outlineBelow;
+    private bool $outlineBelow; //* @phpstan-ignore-line
 
     /**
      * Whether to have outline summary at the right.
-     *
-     * @var bool
+     * Not currently used.
      */
-    private $outlineRight;
+    private bool $outlineRight; //* @phpstan-ignore-line
 
     /**
      * Reference to the total number of strings in the workbook.
-     *
-     * @var int
      */
-    private $stringTotal;
+    private int $stringTotal;
 
     /**
      * Reference to the number of unique strings in the workbook.
-     *
-     * @var int
      */
-    private $stringUnique;
+    private int $stringUnique;
 
     /**
      * Reference to the array containing all the unique strings in the workbook.
-     *
-     * @var array
      */
-    private $stringTable;
+    private array $stringTable;
 
     /**
      * Color cache.
      */
-    private $colors;
+    private array $colors;
 
     /**
      * Index of first used row (at least 0).
-     *
-     * @var int
      */
-    private $firstRowIndex;
+    private int $firstRowIndex;
 
     /**
      * Index of last used row. (no used rows means -1).
-     *
-     * @var int
      */
-    private $lastRowIndex;
+    private int $lastRowIndex;
 
     /**
      * Index of first used column (at least 0).
-     *
-     * @var int
      */
-    private $firstColumnIndex;
+    private int $firstColumnIndex;
 
     /**
      * Index of last used column (no used columns means -1).
-     *
-     * @var int
      */
-    private $lastColumnIndex;
+    private int $lastColumnIndex;
 
     /**
      * Sheet object.
-     *
-     * @var \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
      */
-    public $phpSheet;
-
-    /**
-     * Count cell style Xfs.
-     *
-     * @var int
-     */
-    private $countCellStyleXfs;
+    public \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet;
 
     /**
      * Escher object corresponding to MSODRAWING.
-     *
-     * @var null|\PhpOffice\PhpSpreadsheet\Shared\Escher
      */
-    private $escher;
+    private ?\PhpOffice\PhpSpreadsheet\Shared\Escher $escher = null;
 
     /**
      * Array of font hashes associated to FONT records index.
-     *
-     * @var array
      */
-    public $fontHashIndex;
+    public array $fontHashIndex;
 
-    /**
-     * @var bool
-     */
-    private $preCalculateFormulas;
+    private bool $preCalculateFormulas;
 
-    /**
-     * @var int
-     */
-    private $printHeaders;
+    private int $printHeaders;
+
+    private ?Workbook $writerWorkbook;
 
     /**
      * Constructor.
@@ -227,7 +168,7 @@ class Worksheet extends BIFFwriter
      * @param bool $preCalculateFormulas Flag indicating whether formulas should be calculated or just written
      * @param \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet The worksheet to write
      */
-    public function __construct(&$str_total, &$str_unique, &$str_table, &$colors, Parser $parser, $preCalculateFormulas, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet)
+    public function __construct(int &$str_total, int &$str_unique, array &$str_table, array &$colors, Parser $parser, bool $preCalculateFormulas, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $phpSheet, ?Workbook $writerWorkbook = null)
     {
         // It needs to call its parent's constructor explicitly
         parent::__construct();
@@ -241,9 +182,7 @@ class Worksheet extends BIFFwriter
 
         $this->phpSheet = $phpSheet;
 
-        $this->xlsStringMaxLength = 255;
         $this->columnInfo = [];
-        $this->selection = [0, 0, 0, 0];
         $this->activePane = 3;
 
         $this->printHeaders = 0;
@@ -272,15 +211,14 @@ class Worksheet extends BIFFwriter
         if ($this->lastColumnIndex > 255) {
             $this->lastColumnIndex = 255;
         }
-
-        $this->countCellStyleXfs = count($phpSheet->getParent()->getCellStyleXfCollection());
+        $this->writerWorkbook = $writerWorkbook;
     }
 
     /**
      * Add data to the beginning of the workbook (note the reverse order)
      * and to the end of the workbook.
      *
-     * @see \PhpOffice\PhpSpreadsheet\Writer\Xls\Workbook::storeWorkbook()
+     * @see Workbook::storeWorkbook
      */
     public function close(): void
     {
@@ -288,7 +226,7 @@ class Worksheet extends BIFFwriter
 
         // Storing selected cells and active sheet because it changes while parsing cells with formulas.
         $selectedCells = $this->phpSheet->getSelectedCells();
-        $activeSheetIndex = $this->phpSheet->getParent()->getActiveSheetIndex();
+        $activeSheetIndex = $this->phpSheet->getParentOrThrow()->getActiveSheetIndex();
 
         // Write BOF record
         $this->storeBof(0x0010);
@@ -307,7 +245,7 @@ class Worksheet extends BIFFwriter
 
         // Column dimensions
         if (($defaultWidth = $phpSheet->getDefaultColumnDimension()->getWidth()) < 0) {
-            $defaultWidth = \PhpOffice\PhpSpreadsheet\Shared\Font::getDefaultColumnWidthByFont($phpSheet->getParent()->getDefaultStyle()->getFont());
+            $defaultWidth = \PhpOffice\PhpSpreadsheet\Shared\Font::getDefaultColumnWidthByFont($phpSheet->getParentOrThrow()->getDefaultStyle()->getFont());
         }
 
         $columnDimensions = $phpSheet->getColumnDimensions();
@@ -428,10 +366,12 @@ class Worksheet extends BIFFwriter
                 $elements = $cVal->getRichTextElements();
                 foreach ($elements as $element) {
                     // FONT Index
+                    $str_fontidx = 0;
                     if ($element instanceof Run) {
-                        $str_fontidx = $this->fontHashIndex[$element->getFont()->getHashCode()];
-                    } else {
-                        $str_fontidx = 0;
+                        $getFont = $element->getFont();
+                        if ($getFont !== null) {
+                            $str_fontidx = $this->fontHashIndex[$getFont->getHashCode()];
+                        }
                     }
                     $arrcRun[] = ['strlen' => $str_pos, 'fontidx' => $str_fontidx];
                     // Position FROM
@@ -446,48 +386,37 @@ class Worksheet extends BIFFwriter
                         if ($cVal === '' || $cVal === null) {
                             $this->writeBlank($row, $column, $xfIndex);
                         } else {
-                            $this->writeString($row, $column, $cVal, $xfIndex);
+                            $this->writeString($row, $column, $cell->getValueString(), $xfIndex);
                         }
 
                         break;
                     case DataType::TYPE_NUMERIC:
-                        $this->writeNumber($row, $column, $cVal, $xfIndex);
+                        $this->writeNumber($row, $column, is_numeric($cVal) ? ($cVal + 0) : 0, $xfIndex);
 
                         break;
                     case DataType::TYPE_FORMULA:
-                        $calculatedValue = $this->preCalculateFormulas ?
-                            $cell->getCalculatedValue() : null;
-                        if (self::WRITE_FORMULA_EXCEPTION == $this->writeFormula($row, $column, $cVal, $xfIndex, $calculatedValue)) {
+                        $calculatedValue = $this->preCalculateFormulas ? $cell->getCalculatedValue() : null;
+                        $calculatedValueString = $this->preCalculateFormulas ? $cell->getCalculatedValueString() : '';
+                        if (self::WRITE_FORMULA_EXCEPTION == $this->writeFormula($row, $column, $cell->getValueString(), $xfIndex, $calculatedValue)) {
                             if ($calculatedValue === null) {
                                 $calculatedValue = $cell->getCalculatedValue();
                             }
                             $calctype = gettype($calculatedValue);
-                            switch ($calctype) {
-                                case 'integer':
-                                case 'double':
-                                    $this->writeNumber($row, $column, (float) $calculatedValue, $xfIndex);
-
-                                    break;
-                                case 'string':
-                                    $this->writeString($row, $column, $calculatedValue, $xfIndex);
-
-                                    break;
-                                case 'boolean':
-                                    $this->writeBoolErr($row, $column, (int) $calculatedValue, 0, $xfIndex);
-
-                                    break;
-                                default:
-                                    $this->writeString($row, $column, $cVal, $xfIndex);
-                            }
+                            match ($calctype) {
+                                'integer', 'double' => $this->writeNumber($row, $column, is_numeric($calculatedValue) ? ((float) $calculatedValue) : 0.0, $xfIndex),
+                                'string' => $this->writeString($row, $column, $calculatedValueString, $xfIndex),
+                                'boolean' => $this->writeBoolErr($row, $column, (int) $calculatedValueString, 0, $xfIndex),
+                                default => $this->writeString($row, $column, $cell->getValueString(), $xfIndex),
+                            };
                         }
 
                         break;
                     case DataType::TYPE_BOOL:
-                        $this->writeBoolErr($row, $column, $cVal, 0, $xfIndex);
+                        $this->writeBoolErr($row, $column, (int) $cell->getValueString(), 0, $xfIndex);
 
                         break;
                     case DataType::TYPE_ERROR:
-                        $this->writeBoolErr($row, $column, ErrorCode::error($cVal), 1, $xfIndex);
+                        $this->writeBoolErr($row, $column, ErrorCode::error($cell->getValueString()), 1, $xfIndex);
 
                         break;
                 }
@@ -498,7 +427,7 @@ class Worksheet extends BIFFwriter
         $this->writeMsoDrawing();
 
         // Restoring active sheet.
-        $this->phpSheet->getParent()->setActiveSheetIndex($activeSheetIndex);
+        $this->phpSheet->getParentOrThrow()->setActiveSheetIndex($activeSheetIndex);
 
         // Write WINDOW2 record
         $this->writeWindow2();
@@ -522,16 +451,23 @@ class Worksheet extends BIFFwriter
         $this->writeMergedCells();
 
         // Hyperlinks
+        $phpParent = $phpSheet->getParent();
+        $hyperlinkbase = ($phpParent === null) ? '' : $phpParent->getProperties()->getHyperlinkBase();
         foreach ($phpSheet->getHyperLinkCollection() as $coordinate => $hyperlink) {
             [$column, $row] = Coordinate::indexesFromString($coordinate);
 
             $url = $hyperlink->getUrl();
 
-            if (strpos($url, 'sheet://') !== false) {
+            if (str_contains($url, 'sheet://')) {
                 // internal to current workbook
                 $url = str_replace('sheet://', 'internal:', $url);
             } elseif (preg_match('/^(http:|https:|ftp:|mailto:)/', $url)) {
                 // URL
+            } elseif (!empty($hyperlinkbase) && preg_match('~^([A-Za-z]:)?[/\\\\]~', $url) !== 1) {
+                $url = "$hyperlinkbase$url";
+                if (preg_match('/^(http:|https:|ftp:|mailto:)/', $url) !== 1) {
+                    $url = 'external:' . $url;
+                }
             } else {
                 // external (local file)
                 $url = 'external:' . $url;
@@ -557,27 +493,28 @@ class Worksheet extends BIFFwriter
     {
         $conditionalFormulaHelper = new ConditionalHelper($this->parser);
 
-        $arrConditionalStyles = $this->phpSheet->getConditionalStylesCollection();
+        $arrConditionalStyles = [];
+        foreach ($this->phpSheet->getConditionalStylesCollection() as $key => $value) {
+            $keyExplode = explode(',', Coordinate::resolveUnionAndIntersection($key));
+            foreach ($keyExplode as $exploded) {
+                $arrConditionalStyles[$exploded] = $value;
+            }
+        }
         if (!empty($arrConditionalStyles)) {
-            $arrConditional = [];
-
             // Write ConditionalFormattingTable records
             foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) {
                 $cfHeaderWritten = false;
                 foreach ($conditionalStyles as $conditional) {
                     /** @var Conditional $conditional */
                     if (
-                        $conditional->getConditionType() === Conditional::CONDITION_EXPRESSION ||
-                        $conditional->getConditionType() === Conditional::CONDITION_CELLIS
+                        $conditional->getConditionType() === Conditional::CONDITION_EXPRESSION
+                        || $conditional->getConditionType() === Conditional::CONDITION_CELLIS
                     ) {
                         // Write CFHEADER record (only if there are Conditional Styles that we are able to write)
                         if ($cfHeaderWritten === false) {
                             $cfHeaderWritten = $this->writeCFHeader($cellCoordinate, $conditionalStyles);
                         }
-                        if ($cfHeaderWritten === true && !isset($arrConditional[$conditional->getHashCode()])) {
-                            // This hash code has been handled
-                            $arrConditional[$conditional->getHashCode()] = true;
-
+                        if ($cfHeaderWritten === true) {
                             // Write CFRULE record
                             $this->writeCFRule($conditionalFormulaHelper, $conditional, $cellCoordinate);
                         }
@@ -596,7 +533,7 @@ class Worksheet extends BIFFwriter
      *
      * @return string Binary data
      */
-    private function writeBIFF8CellRangeAddressFixed($range)
+    private function writeBIFF8CellRangeAddressFixed(string $range): string
     {
         $explodes = explode(':', $range);
 
@@ -622,7 +559,7 @@ class Worksheet extends BIFFwriter
      *
      * @return string The data
      */
-    public function getData()
+    public function getData(): string
     {
         // Return data stored in memory
         if (isset($this->_data)) {
@@ -641,7 +578,7 @@ class Worksheet extends BIFFwriter
      *
      * @param int $print Whether to print the headers or not. Defaults to 1 (print).
      */
-    public function printRowColHeaders($print = 1): void
+    public function printRowColHeaders(int $print = 1): void
     {
         $this->printHeaders = $print;
     }
@@ -649,13 +586,8 @@ class Worksheet extends BIFFwriter
     /**
      * This method sets the properties for outlining and grouping. The defaults
      * correspond to Excel's defaults.
-     *
-     * @param bool $visible
-     * @param bool $symbols_below
-     * @param bool $symbols_right
-     * @param bool $auto_style
      */
-    public function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false): void
+    public function setOutline(bool $visible = true, bool $symbols_below = true, bool $symbols_right = true, bool $auto_style = false): void
     {
         $this->outlineOn = $visible;
         $this->outlineBelow = $symbols_below;
@@ -674,11 +606,9 @@ class Worksheet extends BIFFwriter
      * @param int $row Zero indexed row
      * @param int $col Zero indexed column
      * @param float $num The number to write
-     * @param mixed $xfIndex The optional XF format
-     *
-     * @return int
+     * @param int $xfIndex The optional XF format
      */
-    private function writeNumber($row, $col, $num, $xfIndex)
+    private function writeNumber(int $row, int $col, float $num, int $xfIndex): int
     {
         $record = 0x0203; // Record identifier
         $length = 0x000E; // Number of bytes to follow
@@ -703,7 +633,7 @@ class Worksheet extends BIFFwriter
      * @param string $str The string
      * @param int $xfIndex Index to XF record
      */
-    private function writeString($row, $col, $str, $xfIndex): void
+    private function writeString(int $row, int $col, string $str, int $xfIndex): void
     {
         $this->writeLabelSst($row, $col, $str, $xfIndex);
     }
@@ -718,7 +648,7 @@ class Worksheet extends BIFFwriter
      * @param int $xfIndex The XF format index for the cell
      * @param array $arrcRun Index to Font record and characters beginning
      */
-    private function writeRichTextString($row, $col, $str, $xfIndex, $arrcRun): void
+    private function writeRichTextString(int $row, int $col, string $str, int $xfIndex, array $arrcRun): void
     {
         $record = 0x00FD; // Record identifier
         $length = 0x000A; // Bytes to follow
@@ -743,9 +673,9 @@ class Worksheet extends BIFFwriter
      * @param int $row Zero indexed row
      * @param int $col Zero indexed column
      * @param string $str The string to write
-     * @param mixed $xfIndex The XF format index for the cell
+     * @param int $xfIndex The XF format index for the cell
      */
-    private function writeLabelSst($row, $col, $str, $xfIndex): void
+    private function writeLabelSst(int $row, int $col, string $str, int $xfIndex): void
     {
         $record = 0x00FD; // Record identifier
         $length = 0x000A; // Bytes to follow
@@ -777,11 +707,9 @@ class Worksheet extends BIFFwriter
      *
      * @param int $row Zero indexed row
      * @param int $col Zero indexed column
-     * @param mixed $xfIndex The XF format index
-     *
-     * @return int
+     * @param int $xfIndex The XF format index
      */
-    public function writeBlank($row, $col, $xfIndex)
+    public function writeBlank(int $row, int $col, int $xfIndex): int
     {
         $record = 0x0201; // Record identifier
         $length = 0x0006; // Number of bytes to follow
@@ -798,13 +726,9 @@ class Worksheet extends BIFFwriter
      *
      * @param int $row Row index (0-based)
      * @param int $col Column index (0-based)
-     * @param int $value
-     * @param bool $isError Error or Boolean?
-     * @param int $xfIndex
-     *
-     * @return int
+     * @param int $isError Error or Boolean?
      */
-    private function writeBoolErr($row, $col, $value, $isError, $xfIndex)
+    private function writeBoolErr(int $row, int $col, int $value, int $isError, int $xfIndex): int
     {
         $record = 0x0205;
         $length = 8;
@@ -821,6 +745,18 @@ class Worksheet extends BIFFwriter
     const WRITE_FORMULA_RANGE = -2;
     const WRITE_FORMULA_EXCEPTION = -3;
 
+    private static bool $allowThrow = false;
+
+    public static function setAllowThrow(bool $allowThrow): void
+    {
+        self::$allowThrow = $allowThrow;
+    }
+
+    public static function getAllowThrow(): bool
+    {
+        return self::$allowThrow;
+    }
+
     /**
      * Write a formula to the specified row and column (zero indexed).
      * The textual representation of the formula is passed to the parser in
@@ -834,12 +770,10 @@ class Worksheet extends BIFFwriter
      * @param int $row Zero indexed row
      * @param int $col Zero indexed column
      * @param string $formula The formula text string
-     * @param mixed $xfIndex The XF format index
+     * @param int $xfIndex The XF format index
      * @param mixed $calculatedValue Calculated value
-     *
-     * @return int
      */
-    private function writeFormula($row, $col, $formula, $xfIndex, $calculatedValue)
+    private function writeFormula(int $row, int $col, string $formula, int $xfIndex, mixed $calculatedValue): int
     {
         $record = 0x0006; // Record identifier
         // Initialize possible additional value for STRING record that should be written after the FORMULA record?
@@ -911,16 +845,18 @@ class Worksheet extends BIFFwriter
 
             return self::WRITE_FORMULA_NORMAL;
         } catch (PhpSpreadsheetException $e) {
+            if (self::$allowThrow) {
+                throw $e;
+            }
+
             return self::WRITE_FORMULA_EXCEPTION;
         }
     }
 
     /**
      * Write a STRING record. This.
-     *
-     * @param string $stringValue
      */
-    private function writeStringRecord($stringValue): void
+    private function writeStringRecord(string $stringValue): void
     {
         $record = 0x0207; // Record identifier
         $data = StringHelper::UTF8toBIFF8UnicodeLong($stringValue);
@@ -946,7 +882,7 @@ class Worksheet extends BIFFwriter
      * @param int $col Column
      * @param string $url URL string
      */
-    private function writeUrl($row, $col, $url): void
+    private function writeUrl(int $row, int $col, string $url): void
     {
         // Add start row and col to arg list
         $this->writeUrlRange($row, $col, $row, $col, $url);
@@ -966,7 +902,7 @@ class Worksheet extends BIFFwriter
      *
      * @see writeUrl()
      */
-    private function writeUrlRange($row1, $col1, $row2, $col2, $url): void
+    private function writeUrlRange(int $row1, int $col1, int $row2, int $col2, string $url): void
     {
         // Check for internal/external sheet links or default to web link
         if (preg_match('[^internal:]', $url)) {
@@ -992,7 +928,7 @@ class Worksheet extends BIFFwriter
      *
      * @see writeUrl()
      */
-    public function writeUrlWeb($row1, $col1, $row2, $col2, $url): void
+    public function writeUrlWeb(int $row1, int $col1, int $row2, int $col2, string $url): void
     {
         $record = 0x01B8; // Record identifier
 
@@ -1034,7 +970,7 @@ class Worksheet extends BIFFwriter
      *
      * @see writeUrl()
      */
-    private function writeUrlInternal($row1, $col1, $row2, $col2, $url): void
+    private function writeUrlInternal(int $row1, int $col1, int $row2, int $col2, string $url): void
     {
         $record = 0x01B8; // Record identifier
 
@@ -1082,7 +1018,7 @@ class Worksheet extends BIFFwriter
      *
      * @see writeUrl()
      */
-    private function writeUrlExternal($row1, $col1, $row2, $col2, $url): void
+    private function writeUrlExternal(int $row1, int $col1, int $row2, int $col2, string $url): void
     {
         // Network drives are different. We will handle them separately
         // MS/Novell network drives and shares start with \\
@@ -1140,15 +1076,15 @@ class Worksheet extends BIFFwriter
         //$unknown4 = pack('v', 0x03);
 
         // Pack the main data stream
-        $data = pack('vvvv', $row1, $row2, $col1, $col2) .
-            $unknown1 .
-            $link_type .
-            $unknown2 .
-            $up_count .
-            $dir_short_len .
-            $dir_short .
-            $unknown3 .
-            $stream_len; /*.
+        $data = pack('vvvv', $row1, $row2, $col1, $col2)
+            . $unknown1
+            . $link_type
+            . $unknown2
+            . $up_count
+            . $dir_short_len
+            . $dir_short
+            . $unknown3
+            . $stream_len; /*.
                           $dir_long_len .
                           $unknown4     .
                           $dir_long     .
@@ -1173,7 +1109,7 @@ class Worksheet extends BIFFwriter
      * @param bool $hidden The optional hidden attribute
      * @param int $level The optional outline level for row, in range [0,7]
      */
-    private function writeRow($row, $height, $xfIndex, $hidden = false, $level = 0): void
+    private function writeRow(int $row, int $height, int $xfIndex, bool $hidden = false, int $level = 0): void
     {
         $record = 0x0208; // Record identifier
         $length = 0x0010; // Number of bytes to follow
@@ -1193,7 +1129,7 @@ class Worksheet extends BIFFwriter
         if ($height !== null) {
             $miyRw = $height * 20; // row height
         } else {
-            $miyRw = 0xff; // default row height is 256
+            $miyRw = 0xFF; // default row height is 256
         }
 
         // Set the options flags. fUnsynced is used to show that the font and row
@@ -1255,7 +1191,7 @@ class Worksheet extends BIFFwriter
         $fDspGuts = $this->outlineOn; // 7
         $fFrozenNoSplit = 0; // 0 - bit
         // no support in PhpSpreadsheet for selected sheet, therefore sheet is only selected if it is the active sheet
-        $fSelected = ($this->phpSheet === $this->phpSheet->getParent()->getActiveSheet()) ? 1 : 0;
+        $fSelected = ($this->phpSheet === $this->phpSheet->getParentOrThrow()->getActiveSheet()) ? 1 : 0;
         $fPageBreakPreview = $this->phpSheet->getSheetView()->getView() === SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW;
 
         $grbit = $fDspFmla;
@@ -1335,7 +1271,7 @@ class Worksheet extends BIFFwriter
      *                4 => Option flags.
      *                5 => Optional outline level
      */
-    private function writeColinfo($col_array): void
+    private function writeColinfo(array $col_array): void
     {
         $colFirst = $col_array[0] ?? null;
         $colLast = $col_array[1] ?? null;
@@ -1565,7 +1501,8 @@ class Worksheet extends BIFFwriter
      */
     private function writeRangeProtection(): void
     {
-        foreach ($this->phpSheet->getProtectedCells() as $range => $password) {
+        foreach ($this->phpSheet->getProtectedCellRanges() as $range => $protectedCells) {
+            $password = $protectedCells->getPassword();
             // number of ranges, e.g. 'A1:B3 C20:D25'
             $cellRanges = explode(' ', $range);
             $cref = count($cellRanges);
@@ -1618,7 +1555,7 @@ class Worksheet extends BIFFwriter
             return;
         }
 
-        [$column, $row] = Coordinate::indexesFromString($this->phpSheet->getFreezePane() ?? '');
+        [$column, $row] = Coordinate::indexesFromString($this->phpSheet->getFreezePane());
         $x = $column - 1;
         $y = $row - 1;
 
@@ -1632,7 +1569,7 @@ class Worksheet extends BIFFwriter
 
         // Determine which pane should be active. There is also the undocumented
         // option to override this should it be necessary: may be removed later.
-        $pnnAct = null;
+        $pnnAct = 0;
         if ($x != 0 && $y != 0) {
             $pnnAct = 0; // Bottom right
         }
@@ -1677,7 +1614,7 @@ class Worksheet extends BIFFwriter
 
         // Order of printing pages
         $fLeftToRight = $this->phpSheet->getPageSetup()->getPageOrder() === PageSetup::PAGEORDER_DOWN_THEN_OVER
-            ? 0x1 : 0x0;
+            ? 0x0 : 0x1;
         // Page orientation
         $fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE)
             ? 0x0 : 0x1;
@@ -1872,7 +1809,7 @@ class Worksheet extends BIFFwriter
      */
     private function writePrintHeaders(): void
     {
-        $record = 0x002a; // Record identifier
+        $record = 0x002A; // Record identifier
         $length = 0x0002; // Bytes to follow
 
         $fPrintRwCol = $this->printHeaders; // Boolean flag
@@ -1888,7 +1825,7 @@ class Worksheet extends BIFFwriter
      */
     private function writePrintGridlines(): void
     {
-        $record = 0x002b; // Record identifier
+        $record = 0x002B; // Record identifier
         $length = 0x0002; // Bytes to follow
 
         $fPrintGrid = $this->phpSheet->getPrintGridlines() ? 1 : 0; // Boolean flag
@@ -2022,27 +1959,15 @@ class Worksheet extends BIFFwriter
         $vbreaks = [];
         $hbreaks = [];
 
-        foreach ($this->phpSheet->getBreaks() as $cell => $breakType) {
+        foreach ($this->phpSheet->getRowBreaks() as $cell => $break) {
             // Fetch coordinates
             $coordinates = Coordinate::coordinateFromString($cell);
-
-            // Decide what to do by the type of break
-            switch ($breakType) {
-                case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_COLUMN:
-                    // Add to list of vertical breaks
-                    $vbreaks[] = Coordinate::columnIndexFromString($coordinates[0]) - 1;
-
-                    break;
-                case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW:
-                    // Add to list of horizontal breaks
-                    $hbreaks[] = $coordinates[1];
-
-                    break;
-                case \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_NONE:
-                default:
-                    // Nothing to do
-                    break;
-            }
+            $hbreaks[] = $coordinates[1];
+        }
+        foreach ($this->phpSheet->getColumnBreaks() as $cell => $break) {
+            // Fetch coordinates
+            $coordinates = Coordinate::indexesFromString($cell);
+            $vbreaks[] = $coordinates[0] - 1;
         }
 
         //horizontal page breaks
@@ -2053,7 +1978,7 @@ class Worksheet extends BIFFwriter
                 array_shift($hbreaks);
             }
 
-            $record = 0x001b; // Record identifier
+            $record = 0x001B; // Record identifier
             $cbrk = count($hbreaks); // Number of page breaks
             $length = 2 + 6 * $cbrk; // Bytes to follow
 
@@ -2062,7 +1987,7 @@ class Worksheet extends BIFFwriter
 
             // Append each page break
             foreach ($hbreaks as $hbreak) {
-                $data .= pack('vvv', $hbreak, 0x0000, 0x00ff);
+                $data .= pack('vvv', $hbreak, 0x0000, 0x00FF);
             }
 
             $this->append($header . $data);
@@ -2080,7 +2005,7 @@ class Worksheet extends BIFFwriter
                 array_shift($vbreaks);
             }
 
-            $record = 0x001a; // Record identifier
+            $record = 0x001A; // Record identifier
             $cbrk = count($vbreaks); // Number of page breaks
             $length = 2 + 6 * $cbrk; // Bytes to follow
 
@@ -2089,7 +2014,7 @@ class Worksheet extends BIFFwriter
 
             // Append each page break
             foreach ($vbreaks as $vbreak) {
-                $data .= pack('vvv', $vbreak, 0x0000, 0xffff);
+                $data .= pack('vvv', $vbreak, 0x0000, 0xFFFF);
             }
 
             $this->append($header . $data);
@@ -2191,17 +2116,17 @@ class Worksheet extends BIFFwriter
      *
      * @param int $row The row we are going to insert the bitmap into
      * @param int $col The column we are going to insert the bitmap into
-     * @param mixed $bitmap The bitmap filename or GD-image resource
+     * @param GdImage|string $bitmap The bitmap filename or GD-image resource
      * @param int $x the horizontal position (offset) of the image inside the cell
      * @param int $y the vertical position (offset) of the image inside the cell
      * @param float $scale_x The horizontal scale
      * @param float $scale_y The vertical scale
      */
-    public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1): void
+    public function insertBitmap(int $row, int $col, GdImage|string $bitmap, int $x = 0, int $y = 0, float $scale_x = 1, float $scale_y = 1): void
     {
-        $bitmap_array = (is_resource($bitmap) || $bitmap instanceof GdImage
+        $bitmap_array = $bitmap instanceof GdImage
             ? $this->processBitmapGd($bitmap)
-            : $this->processBitmap($bitmap));
+            : $this->processBitmap($bitmap);
         [$width, $height, $size, $data] = $bitmap_array;
 
         // Scale the frame of the image.
@@ -2209,10 +2134,10 @@ class Worksheet extends BIFFwriter
         $height *= $scale_y;
 
         // Calculate the vertices of the image and write the OBJ record
-        $this->positionImage($col, $row, $x, $y, $width, $height);
+        $this->positionImage($col, $row, $x, $y, (int) $width, (int) $height);
 
         // Write the IMDATA record to store the bitmap data
-        $record = 0x007f;
+        $record = 0x007F;
         $length = 8 + $size;
         $cf = 0x09;
         $env = 0x01;
@@ -2272,7 +2197,7 @@ class Worksheet extends BIFFwriter
      * @param int $width Width of image frame
      * @param int $height Height of image frame
      */
-    public function positionImage($col_start, $row_start, $x1, $y1, $width, $height): void
+    public function positionImage(int $col_start, int $row_start, int $x1, int $y1, int $width, int $height): void
     {
         // Initialise end cell to the same as the start cell
         $col_end = $col_start; // Col containing lower right corner of object
@@ -2333,16 +2258,16 @@ class Worksheet extends BIFFwriter
      * @param int $colL Column containing upper left corner of object
      * @param int $dxL Distance from left side of cell
      * @param int $rwT Row containing top left corner of object
-     * @param int $dyT Distance from top of cell
+     * @param float|int $dyT Distance from top of cell
      * @param int $colR Column containing lower right corner of object
      * @param int $dxR Distance from right of cell
      * @param int $rwB Row containing bottom right corner of object
      * @param int $dyB Distance from bottom of cell
      */
-    private function writeObjPicture($colL, $dxL, $rwT, $dyT, $colR, $dxR, $rwB, $dyB): void
+    private function writeObjPicture(int $colL, int $dxL, int $rwT, int|float $dyT, int $colR, int $dxR, int $rwB, int $dyB): void
     {
-        $record = 0x005d; // Record identifier
-        $length = 0x003c; // Bytes to follow
+        $record = 0x005D; // Record identifier
+        $length = 0x003C; // Bytes to follow
 
         $cObj = 0x0001; // Count of objects in file (set to 1)
         $OT = 0x0008; // Object type. 8 = Picture
@@ -2358,7 +2283,7 @@ class Worksheet extends BIFFwriter
         $fls = 0x00; // Fill pattern
         $fAuto = 0x00; // Automatic fill
         $icv = 0x08; // Line colour
-        $lns = 0xff; // Line style
+        $lns = 0xFF; // Line style
         $lnw = 0x01; // Line weight
         $fAutoB = 0x00; // Automatic border
         $frs = 0x0000; // Frame style
@@ -2407,16 +2332,16 @@ class Worksheet extends BIFFwriter
     /**
      * Convert a GD-image into the internal format.
      *
-     * @param GdImage|resource $image The image to process
+     * @param GdImage $image The image to process
      *
      * @return array Array with data and properties of the bitmap
      */
-    public function processBitmapGd($image)
+    public function processBitmapGd(GdImage $image): array
     {
         $width = imagesx($image);
         $height = imagesy($image);
 
-        $data = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18);
+        $data = pack('Vvvvv', 0x000C, $width, $height, 0x01, 0x18);
         for ($j = $height; --$j;) {
             for ($i = 0; $i < $width; ++$i) {
                 /** @phpstan-ignore-next-line */
@@ -2445,16 +2370,16 @@ class Worksheet extends BIFFwriter
      *
      * @return array Array with data and properties of the bitmap
      */
-    public function processBitmap($bitmap)
+    public function processBitmap(string $bitmap): array
     {
         // Open file.
         $bmp_fd = @fopen($bitmap, 'rb');
-        if ($bmp_fd === false) {
+        if ($bmp_fd === false || 0 === (int) filesize($bitmap)) {
             throw new WriterException("Couldn't import $bitmap");
         }
 
         // Slurp the file into a string.
-        $data = fread($bmp_fd, filesize($bitmap));
+        $data = (string) fread($bmp_fd, (int) filesize($bitmap));
 
         // Check that the file is big enough to be a bitmap.
         if (strlen($data) <= 0x36) {
@@ -2463,9 +2388,8 @@ class Worksheet extends BIFFwriter
 
         // The first 2 bytes are used to identify the bitmap.
 
-        /** @phpstan-ignore-next-line */
         $identity = unpack('A2ident', $data);
-        if ($identity['ident'] != 'BM') {
+        if ($identity === false || $identity['ident'] != 'BM') {
             throw new WriterException("$bitmap doesn't appear to be a valid bitmap image.\n");
         }
 
@@ -2475,7 +2399,7 @@ class Worksheet extends BIFFwriter
         // Read and remove the bitmap size. This is more reliable than reading
         // the data size at offset 0x22.
         //
-        $size_array = unpack('Vsa', substr($data, 0, 4));
+        $size_array = unpack('Vsa', substr($data, 0, 4)) ?: [];
         $size = $size_array['sa'];
         $data = substr($data, 4);
         $size -= 0x36; // Subtract size of bitmap header.
@@ -2485,7 +2409,7 @@ class Worksheet extends BIFFwriter
         $data = substr($data, 12);
 
         // Read and remove the bitmap width and height. Verify the sizes.
-        $width_and_height = unpack('V2', substr($data, 0, 8));
+        $width_and_height = unpack('V2', substr($data, 0, 8)) ?: [];
         $width = $width_and_height[1];
         $height = $width_and_height[2];
         $data = substr($data, 8);
@@ -2499,7 +2423,7 @@ class Worksheet extends BIFFwriter
         // Read and remove the bitmap planes and bpp data. Verify them.
         $planes_and_bitcount = unpack('v2', substr($data, 0, 4));
         $data = substr($data, 4);
-        if ($planes_and_bitcount[2] != 24) { // Bitcount
+        if ($planes_and_bitcount === false || $planes_and_bitcount[2] != 24) { // Bitcount
             throw new WriterException("$bitmap isn't a 24bit true color bitmap.\n");
         }
         if ($planes_and_bitcount[1] != 1) {
@@ -2510,7 +2434,7 @@ class Worksheet extends BIFFwriter
         $compression = unpack('Vcomp', substr($data, 0, 4));
         $data = substr($data, 4);
 
-        if ($compression['comp'] != 0) {
+        if ($compression === false || $compression['comp'] != 0) {
             throw new WriterException("$bitmap: compression not supported in bitmap image.\n");
         }
 
@@ -2518,7 +2442,7 @@ class Worksheet extends BIFFwriter
         $data = substr($data, 20);
 
         // Add the BITMAPCOREHEADER data
-        $header = pack('Vvvvv', 0x000c, $width, $height, 0x01, 0x18);
+        $header = pack('Vvvvv', 0x000C, $width, $height, 0x01, 0x18);
         $data = $header . $data;
 
         return [$width, $height, $size, $data];
@@ -2594,8 +2518,8 @@ class Worksheet extends BIFFwriter
                 // ftCmo
                 if ($spTypes[$i] == 0x00C9) {
                     // Add ftCmo (common object data) subobject
-                    $objData .=
-                        pack(
+                    $objData
+                        .= pack(
                             'vvvvvVVV',
                             0x0015, // 0x0015 = ftCmo
                             0x0012, // length of ftCmo data
@@ -2615,8 +2539,8 @@ class Worksheet extends BIFFwriter
                     $objData .= pack('H*', '00000000010001030000020008005700');
                 } else {
                     // Add ftCmo (common object data) subobject
-                    $objData .=
-                        pack(
+                    $objData
+                        .= pack(
                             'vvvvvVVV',
                             0x0015, // 0x0015 = ftCmo
                             0x0012, // length of ftCmo data
@@ -2630,8 +2554,8 @@ class Worksheet extends BIFFwriter
                 }
 
                 // ftEnd
-                $objData .=
-                    pack(
+                $objData
+                    .= pack(
                         'vv',
                         0x0000, // 0x0000 = ftEnd
                         0x0000  // length of ftEnd data
@@ -2709,23 +2633,23 @@ class Worksheet extends BIFFwriter
                 $data = pack('V', $options);
 
                 // prompt title
-                $promptTitle = $dataValidation->getPromptTitle() !== '' ?
-                    $dataValidation->getPromptTitle() : chr(0);
+                $promptTitle = $dataValidation->getPromptTitle() !== ''
+                    ? $dataValidation->getPromptTitle() : chr(0);
                 $data .= StringHelper::UTF8toBIFF8UnicodeLong($promptTitle);
 
                 // error title
-                $errorTitle = $dataValidation->getErrorTitle() !== '' ?
-                    $dataValidation->getErrorTitle() : chr(0);
+                $errorTitle = $dataValidation->getErrorTitle() !== ''
+                    ? $dataValidation->getErrorTitle() : chr(0);
                 $data .= StringHelper::UTF8toBIFF8UnicodeLong($errorTitle);
 
                 // prompt text
-                $prompt = $dataValidation->getPrompt() !== '' ?
-                    $dataValidation->getPrompt() : chr(0);
+                $prompt = $dataValidation->getPrompt() !== ''
+                    ? $dataValidation->getPrompt() : chr(0);
                 $data .= StringHelper::UTF8toBIFF8UnicodeLong($prompt);
 
                 // error text
-                $error = $dataValidation->getError() !== '' ?
-                    $dataValidation->getError() : chr(0);
+                $error = $dataValidation->getError() !== ''
+                    ? $dataValidation->getError() : chr(0);
                 $data .= StringHelper::UTF8toBIFF8UnicodeLong($error);
 
                 // formula 1
@@ -2753,7 +2677,7 @@ class Worksheet extends BIFFwriter
                     $this->parser->parse($formula2);
                     $formula2 = $this->parser->toReversePolish();
                     $sz2 = strlen($formula2);
-                } catch (PhpSpreadsheetException $e) {
+                } catch (PhpSpreadsheetException) {
                     $sz2 = 0;
                     $formula2 = '';
                 }
@@ -2805,6 +2729,8 @@ class Worksheet extends BIFFwriter
 
     /**
      * Write CFRule Record.
+     *
+     * @see https://www.openoffice.org/sc/excelfileformat.pdf Search for CFHEADER followed by CFRULE
      */
     private function writeCFRule(
         ConditionalHelper $conditionalFormulaHelper,
@@ -2883,7 +2809,7 @@ class Worksheet extends BIFFwriter
 
         // $flags : Option flags
         // Alignment
-        $bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() === null ? 1 : 0);
+        /*$bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() === null ? 1 : 0);
         $bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() === null ? 1 : 0);
         $bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() === false ? 1 : 0);
         $bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() === null ? 1 : 0);
@@ -2893,25 +2819,26 @@ class Worksheet extends BIFFwriter
             $bFormatAlign = 1;
         } else {
             $bFormatAlign = 0;
-        }
+        }*/
         // Protection
-        $bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() == null ? 1 : 0);
-        $bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() == null ? 1 : 0);
+        /*$bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() === null ? 1 : 0);
+        $bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() === null ? 1 : 0);
         if ($bProtLocked == 0 || $bProtHidden == 0) {
             $bFormatProt = 1;
         } else {
             $bFormatProt = 0;
-        }
+        }*/
         // Border
-        $bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getColor()->getARGB() == Color::COLOR_BLACK
-        && $conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
-        $bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getColor()->getARGB() == Color::COLOR_BLACK
-        && $conditional->getStyle()->getBorders()->getRight()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
-        $bBorderTop = ($conditional->getStyle()->getBorders()->getTop()->getColor()->getARGB() == Color::COLOR_BLACK
-        && $conditional->getStyle()->getBorders()->getTop()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
-        $bBorderBottom = ($conditional->getStyle()->getBorders()->getBottom()->getColor()->getARGB() == Color::COLOR_BLACK
-        && $conditional->getStyle()->getBorders()->getBottom()->getBorderStyle() == Border::BORDER_NONE ? 1 : 0);
-        if ($bBorderLeft == 0 || $bBorderRight == 0 || $bBorderTop == 0 || $bBorderBottom == 0) {
+        $bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() !== Border::BORDER_OMIT) ? 1 : 0;
+        $bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle() !== Border::BORDER_OMIT) ? 1 : 0;
+        $bBorderTop = ($conditional->getStyle()->getBorders()->getTop()->getBorderStyle() !== Border::BORDER_OMIT) ? 1 : 0;
+        $bBorderBottom = ($conditional->getStyle()->getBorders()->getBottom()->getBorderStyle() !== Border::BORDER_OMIT) ? 1 : 0;
+        //$diagonalDirection = $conditional->getStyle()->getBorders()->getDiagonalDirection();
+        // Excel does not support conditional diagonal border even for xlsx
+        $bBorderDiagTop = self::$always0; //$diagonalDirection === Borders::DIAGONAL_DOWN || $diagonalDirection === Borders::DIAGONAL_BOTH;
+        $bBorderDiagBottom = self::$always0; //$diagonalDirection === Borders::DIAGONAL_UP || $diagonalDirection === Borders::DIAGONAL_BOTH;
+
+        if ($bBorderLeft === 1 || $bBorderRight === 1 || $bBorderTop === 1 || $bBorderBottom === 1 || $bBorderDiagTop === 1 || $bBorderDiagBottom === 1) {
             $bFormatBorder = 1;
         } else {
             $bFormatBorder = 0;
@@ -2920,7 +2847,7 @@ class Worksheet extends BIFFwriter
         $bFillStyle = ($conditional->getStyle()->getFill()->getFillType() === null ? 0 : 1);
         $bFillColor = ($conditional->getStyle()->getFill()->getStartColor()->getARGB() === null ? 0 : 1);
         $bFillColorBg = ($conditional->getStyle()->getFill()->getEndColor()->getARGB() === null ? 0 : 1);
-        if ($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0) {
+        if ($bFillStyle == 1 || $bFillColor == 1 || $bFillColorBg == 1) {
             $bFormatFill = 1;
         } else {
             $bFormatFill = 0;
@@ -2943,26 +2870,26 @@ class Worksheet extends BIFFwriter
         }
         // Alignment
         $flags = 0;
-        $flags |= (1 == $bAlignHz ? 0x00000001 : 0);
-        $flags |= (1 == $bAlignVt ? 0x00000002 : 0);
-        $flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
-        $flags |= (1 == $bTxRotation ? 0x00000008 : 0);
+        //$flags |= (1 == $bAlignHz ? 0x00000001 : 0);
+        //$flags |= (1 == $bAlignVt ? 0x00000002 : 0);
+        //$flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
+        //$flags |= (1 == $bTxRotation ? 0x00000008 : 0);
         // Justify last line flag
         $flags |= (1 == self::$always1 ? 0x00000010 : 0);
-        $flags |= (1 == $bIndent ? 0x00000020 : 0);
-        $flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
+        //$flags |= (1 == $bIndent ? 0x00000020 : 0);
+        //$flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
         // Default
         $flags |= (1 == self::$always1 ? 0x00000080 : 0);
         // Protection
-        $flags |= (1 == $bProtLocked ? 0x00000100 : 0);
-        $flags |= (1 == $bProtHidden ? 0x00000200 : 0);
-        // Border
-        $flags |= (1 == $bBorderLeft ? 0x00000400 : 0);
-        $flags |= (1 == $bBorderRight ? 0x00000800 : 0);
-        $flags |= (1 == $bBorderTop ? 0x00001000 : 0);
-        $flags |= (1 == $bBorderBottom ? 0x00002000 : 0);
-        $flags |= (1 == self::$always1 ? 0x00004000 : 0); // Top left to Bottom right border
-        $flags |= (1 == self::$always1 ? 0x00008000 : 0); // Bottom left to Top right border
+        //$flags |= (1 == $bProtLocked ? 0x00000100 : 0);
+        //$flags |= (1 == $bProtHidden ? 0x00000200 : 0);
+        // Border, note that flags are opposite of what you might expect
+        $flags |= (0 == $bBorderLeft ? 0x00000400 : 0);
+        $flags |= (0 == $bBorderRight ? 0x00000800 : 0);
+        $flags |= (0 == $bBorderTop ? 0x00001000 : 0);
+        $flags |= (0 == $bBorderBottom ? 0x00002000 : 0);
+        $flags |= (0 === $bBorderDiagTop ? 0x00004000 : 0); // Top left to Bottom right border
+        $flags |= (0 === $bBorderDiagBottom ? 0x00008000 : 0); // Bottom left to Top right border
         // Pattern
         $flags |= (1 == $bFillStyle ? 0x00010000 : 0);
         $flags |= (1 == $bFillColor ? 0x00020000 : 0);
@@ -2971,18 +2898,18 @@ class Worksheet extends BIFFwriter
         // Font
         $flags |= (1 == $bFormatFont ? 0x04000000 : 0);
         // Alignment:
-        $flags |= (1 == $bFormatAlign ? 0x08000000 : 0);
+        //$flags |= (1 == $bFormatAlign ? 0x08000000 : 0);
         // Border
         $flags |= (1 == $bFormatBorder ? 0x10000000 : 0);
         // Pattern
         $flags |= (1 == $bFormatFill ? 0x20000000 : 0);
         // Protection
-        $flags |= (1 == $bFormatProt ? 0x40000000 : 0);
+        //$flags |= (1 == $bFormatProt ? 0x40000000 : 0);
         // Text direction
         $flags |= (1 == self::$always0 ? 0x80000000 : 0);
 
         $dataBlockFont = null;
-        $dataBlockAlign = null;
+        //$dataBlockAlign = null;
         $dataBlockBorder = null;
         $dataBlockFill = null;
 
@@ -3002,10 +2929,19 @@ class Worksheet extends BIFFwriter
                 $dataBlockFont .= pack('V', 20 * $conditional->getStyle()->getFont()->getSize());
             }
             // Font Options
-            $dataBlockFont .= pack('V', 0);
+            $italicStrike = 0;
+            if ($conditional->getStyle()->getFont()->getItalic() === true) {
+                $italicStrike |= 2;
+            }
+            if ($conditional->getStyle()->getFont()->getStrikethrough() === true) {
+                $italicStrike |= 0x80;
+            }
+            $dataBlockFont .= pack('V', $italicStrike);
             // Font weight
             if ($conditional->getStyle()->getFont()->getBold() === true) {
                 $dataBlockFont .= pack('v', 0x02BC);
+            } elseif ($conditional->getStyle()->getFont()->getBold() === null) {
+                $dataBlockFont .= pack('v', 0x0000);
             } else {
                 $dataBlockFont .= pack('v', 0x0190);
             }
@@ -3056,19 +2992,17 @@ class Worksheet extends BIFFwriter
             // Not used (3)
             $dataBlockFont .= pack('vC', 0x0000, 0x00);
             // Font color index
-            $colorIdx = Style\ColorMap::lookup($conditional->getStyle()->getFont()->getColor(), 0x00);
-
+            $colorIdx = $this->workbookColorIndex($conditional->getStyle()->getFont()->getColor()->getRgb(), 0);
             $dataBlockFont .= pack('V', $colorIdx);
             // Not used (4)
             $dataBlockFont .= pack('V', 0x00000000);
             // Options flags for modified font attributes
             $optionsFlags = 0;
-            $optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() === null ? 1 : 0);
-            $optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0);
+            $optionsFlags |= ($conditional->getStyle()->getFont()->getBold() === null && $conditional->getStyle()->getFont()->getItalic() === null) ? 2 : 0;
             $optionsFlags |= (1 == self::$always1 ? 0x00000008 : 0);
             $optionsFlags |= (1 == self::$always1 ? 0x00000010 : 0);
             $optionsFlags |= (1 == self::$always0 ? 0x00000020 : 0);
-            $optionsFlags |= (1 == self::$always1 ? 0x00000080 : 0);
+            $optionsFlags |= ($conditional->getStyle()->getFont()->getStrikethrough() === null) ? 0x80 : 0;
             $dataBlockFont .= pack('V', $optionsFlags);
             // Escapement type
             $dataBlockFont .= pack('V', $fontEscapement);
@@ -3083,7 +3017,7 @@ class Worksheet extends BIFFwriter
             // Always
             $dataBlockFont .= pack('v', 0x0001);
         }
-        if ($bFormatAlign === 1) {
+        /*if ($bFormatAlign === 1) {
             // Alignment and text break
             $blockAlign = Style\CellAlignment::horizontal($conditional->getStyle()->getAlignment());
             $blockAlign |= Style\CellAlignment::wrap($conditional->getStyle()->getAlignment()) << 3;
@@ -3106,31 +3040,52 @@ class Worksheet extends BIFFwriter
             $blockIndentRelative = 255;
 
             $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000);
-        }
+        }*/
         if ($bFormatBorder === 1) {
             $blockLineStyle = Style\CellBorder::style($conditional->getStyle()->getBorders()->getLeft());
             $blockLineStyle |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getRight()) << 4;
             $blockLineStyle |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getTop()) << 8;
             $blockLineStyle |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getBottom()) << 12;
 
-            // TODO writeCFRule() => $blockLineStyle => Index Color for left line
-            // TODO writeCFRule() => $blockLineStyle => Index Color for right line
-            // TODO writeCFRule() => $blockLineStyle => Top-left to bottom-right on/off
-            // TODO writeCFRule() => $blockLineStyle => Bottom-left to top-right on/off
+            if ($bBorderLeft !== 0) {
+                $colorIdx = $this->workbookColorIndex($conditional->getStyle()->getBorders()->getLeft()->getColor()->getRgb(), 0);
+                $blockLineStyle |= $colorIdx << 16;
+            }
+            if ($bBorderRight !== 0) {
+                $colorIdx = $this->workbookColorIndex($conditional->getStyle()->getBorders()->getRight()->getColor()->getRgb(), 0);
+                $blockLineStyle |= $colorIdx << 23;
+            }
             $blockColor = 0;
-            // TODO writeCFRule() => $blockColor => Index Color for top line
-            // TODO writeCFRule() => $blockColor => Index Color for bottom line
-            // TODO writeCFRule() => $blockColor => Index Color for diagonal line
-            $blockColor |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getDiagonal()) << 21;
-            $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor);
+            if ($bBorderTop !== 0) {
+                $colorIdx = $this->workbookColorIndex($conditional->getStyle()->getBorders()->getTop()->getColor()->getRgb(), 0);
+                $blockColor |= $colorIdx;
+            }
+            if ($bBorderBottom !== 0) {
+                $colorIdx = $this->workbookColorIndex($conditional->getStyle()->getBorders()->getBottom()->getColor()->getRgb(), 0);
+                $blockColor |= $colorIdx << 7;
+            }
+            /* Excel does not support condtional diagonal borders even for xlsx
+            if ($bBorderDiagTop !== 0 || $bBorderDiagBottom !== 0) {
+                $colorIdx = $this->workbookColorIndex($conditional->getStyle()->getBorders()->getDiagonal()->getColor()->getRgb(), 0);
+                $blockColor |= $colorIdx << 14;
+                $blockColor |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getDiagonal()) << 21;
+                if ($bBorderDiagTop !== 0) {
+                    $blockLineStyle |= 1 << 30;
+                }
+                if ($bBorderDiagBottom !== 0) {
+                    $blockLineStyle |= 1 << 31;
+                }
+            }
+            */
+            $dataBlockBorder = pack('VV', $blockLineStyle, $blockColor);
         }
         if ($bFormatFill === 1) {
             // Fill Pattern Style
             $blockFillPatternStyle = Style\CellFill::style($conditional->getStyle()->getFill());
             // Background Color
-            $colorIdxBg = Style\ColorMap::lookup($conditional->getStyle()->getFill()->getStartColor(), 0x41);
+            $colorIdxBg = $this->workbookColorIndex($conditional->getStyle()->getFill()->getStartColor()->getRgb(), 0x41);
             // Foreground Color
-            $colorIdxFg = Style\ColorMap::lookup($conditional->getStyle()->getFill()->getEndColor(), 0x40);
+            $colorIdxFg = $this->workbookColorIndex($conditional->getStyle()->getFill()->getEndColor()->getRgb(), 0x40);
 
             $dataBlockFill = pack('v', $blockFillPatternStyle);
             $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7));
@@ -3140,18 +3095,18 @@ class Worksheet extends BIFFwriter
         if ($bFormatFont === 1) { // Block Formatting : OK
             $data .= $dataBlockFont;
         }
-        if ($bFormatAlign === 1) {
-            $data .= $dataBlockAlign;
-        }
+        //if ($bFormatAlign === 1) {
+        //    $data .= $dataBlockAlign;
+        //}
         if ($bFormatBorder === 1) {
             $data .= $dataBlockBorder;
         }
         if ($bFormatFill === 1) { // Block Formatting : OK
             $data .= $dataBlockFill;
         }
-        if ($bFormatProt == 1) {
-            $data .= $this->getDataBlockProtection($conditional);
-        }
+        //if ($bFormatProt == 1) {
+        //    $data .= $this->getDataBlockProtection($conditional);
+        //}
         if ($operand1 !== null) {
             $data .= $operand1;
         }
@@ -3215,7 +3170,7 @@ class Worksheet extends BIFFwriter
         return true;
     }
 
-    private function getDataBlockProtection(Conditional $conditional): int
+    /*private function getDataBlockProtection(Conditional $conditional): int
     {
         $dataBlockProtection = 0;
         if ($conditional->getStyle()->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED) {
@@ -3226,5 +3181,10 @@ class Worksheet extends BIFFwriter
         }
 
         return $dataBlockProtection;
+    }*/
+
+    private function workbookColorIndex(?string $rgb, int $default): int
+    {
+        return (empty($rgb) || $this->writerWorkbook === null) ? $default : $this->writerWorkbook->addColor($rgb, $default);
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php
index b2dbc67..2ab5a4e 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Xf.php
@@ -48,88 +48,58 @@ class Xf
 {
     /**
      * Style XF or a cell XF ?
-     *
-     * @var bool
      */
-    private $isStyleXf;
+    private bool $isStyleXf;
 
     /**
      * Index to the FONT record. Index 4 does not exist.
-     *
-     * @var int
      */
-    private $fontIndex;
+    private int $fontIndex;
 
     /**
      * An index (2 bytes) to a FORMAT record (number format).
-     *
-     * @var int
      */
-    private $numberFormatIndex;
+    private int $numberFormatIndex;
 
     /**
      * 1 bit, apparently not used.
-     *
-     * @var int
      */
-    private $textJustLast;
+    private int $textJustLast;
 
     /**
      * The cell's foreground color.
-     *
-     * @var int
      */
-    private $foregroundColor;
+    private int $foregroundColor;
 
     /**
      * The cell's background color.
-     *
-     * @var int
      */
-    private $backgroundColor;
+    private int $backgroundColor;
 
     /**
      * Color of the bottom border of the cell.
-     *
-     * @var int
      */
-    private $bottomBorderColor;
+    private int $bottomBorderColor;
 
     /**
      * Color of the top border of the cell.
-     *
-     * @var int
      */
-    private $topBorderColor;
+    private int $topBorderColor;
 
     /**
      * Color of the left border of the cell.
-     *
-     * @var int
      */
-    private $leftBorderColor;
+    private int $leftBorderColor;
 
     /**
      * Color of the right border of the cell.
-     *
-     * @var int
      */
-    private $rightBorderColor;
+    private int $rightBorderColor;
 
-    /**
-     * @var int
-     */
-    private $diag;
+    //private $diag; // theoretically int, not yet implemented
+    private int $diagColor;
 
-    /**
-     * @var int
-     */
-    private $diagColor;
-
-    /**
-     * @var Style
-     */
-    private $style;
+    private Style $style;
 
     /**
      * Constructor.
@@ -148,7 +118,7 @@ class Xf
         $this->foregroundColor = 0x40;
         $this->backgroundColor = 0x41;
 
-        $this->diag = 0;
+        //$this->diag = 0;
 
         $this->bottomBorderColor = 0x40;
         $this->topBorderColor = 0x40;
@@ -163,7 +133,7 @@ class Xf
      *
      * @return string The XF record
      */
-    public function writeXf()
+    public function writeXf(): string
     {
         // Set the type of the XF record and some of the attributes.
         if ($this->isStyleXf) {
@@ -177,10 +147,10 @@ class Xf
         $atr_num = ($this->numberFormatIndex != 0) ? 1 : 0;
         $atr_fnt = ($this->fontIndex != 0) ? 1 : 0;
         $atr_alc = ((int) $this->style->getAlignment()->getWrapText()) ? 1 : 0;
-        $atr_bdr = (CellBorder::style($this->style->getBorders()->getBottom()) ||
-            CellBorder::style($this->style->getBorders()->getTop()) ||
-            CellBorder::style($this->style->getBorders()->getLeft()) ||
-            CellBorder::style($this->style->getBorders()->getRight())) ? 1 : 0;
+        $atr_bdr = (CellBorder::style($this->style->getBorders()->getBottom())
+            || CellBorder::style($this->style->getBorders()->getTop())
+            || CellBorder::style($this->style->getBorders()->getLeft())
+            || CellBorder::style($this->style->getBorders()->getRight())) ? 1 : 0;
         $atr_pat = ($this->foregroundColor != 0x40) ? 1 : 0;
         $atr_pat = ($this->backgroundColor != 0x41) ? 1 : $atr_pat;
         $atr_pat = CellFill::style($this->style->getFill()) ? 1 : $atr_pat;
@@ -254,7 +224,7 @@ class Xf
         $biff8_options |= (int) $this->style->getAlignment()->getShrinkToFit() << 4;
 
         $data = pack('vvvC', $ifnt, $ifmt, $style, $align);
-        $data .= pack('CCC', self::mapTextRotation($this->style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib);
+        $data .= pack('CCC', self::mapTextRotation((int) $this->style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib);
         $data .= pack('VVv', $border1, $border2, $icv);
 
         return $header . $data;
@@ -262,10 +232,8 @@ class Xf
 
     /**
      * Is this a style XF ?
-     *
-     * @param bool $value
      */
-    public function setIsStyleXf($value): void
+    public function setIsStyleXf(bool $value): void
     {
         $this->isStyleXf = $value;
     }
@@ -275,7 +243,7 @@ class Xf
      *
      * @param int $colorIndex Color index
      */
-    public function setBottomColor($colorIndex): void
+    public function setBottomColor(int $colorIndex): void
     {
         $this->bottomBorderColor = $colorIndex;
     }
@@ -285,7 +253,7 @@ class Xf
      *
      * @param int $colorIndex Color index
      */
-    public function setTopColor($colorIndex): void
+    public function setTopColor(int $colorIndex): void
     {
         $this->topBorderColor = $colorIndex;
     }
@@ -295,7 +263,7 @@ class Xf
      *
      * @param int $colorIndex Color index
      */
-    public function setLeftColor($colorIndex): void
+    public function setLeftColor(int $colorIndex): void
     {
         $this->leftBorderColor = $colorIndex;
     }
@@ -305,7 +273,7 @@ class Xf
      *
      * @param int $colorIndex Color index
      */
-    public function setRightColor($colorIndex): void
+    public function setRightColor(int $colorIndex): void
     {
         $this->rightBorderColor = $colorIndex;
     }
@@ -315,7 +283,7 @@ class Xf
      *
      * @param int $colorIndex Color index
      */
-    public function setDiagColor($colorIndex): void
+    public function setDiagColor(int $colorIndex): void
     {
         $this->diagColor = $colorIndex;
     }
@@ -325,7 +293,7 @@ class Xf
      *
      * @param int $colorIndex Color index
      */
-    public function setFgColor($colorIndex): void
+    public function setFgColor(int $colorIndex): void
     {
         $this->foregroundColor = $colorIndex;
     }
@@ -335,7 +303,7 @@ class Xf
      *
      * @param int $colorIndex Color index
      */
-    public function setBgColor($colorIndex): void
+    public function setBgColor(int $colorIndex): void
     {
         $this->backgroundColor = $colorIndex;
     }
@@ -346,7 +314,7 @@ class Xf
      *
      * @param int $numberFormatIndex Index to format record
      */
-    public function setNumberFormatIndex($numberFormatIndex): void
+    public function setNumberFormatIndex(int $numberFormatIndex): void
     {
         $this->numberFormatIndex = $numberFormatIndex;
     }
@@ -356,19 +324,15 @@ class Xf
      *
      * @param int $value Font index, note that value 4 does not exist
      */
-    public function setFontIndex($value): void
+    public function setFontIndex(int $value): void
     {
         $this->fontIndex = $value;
     }
 
     /**
      * Map to BIFF8 codes for text rotation angle.
-     *
-     * @param int $textRotation
-     *
-     * @return int
      */
-    private static function mapTextRotation($textRotation)
+    private static function mapTextRotation(int $textRotation): int
     {
         if ($textRotation >= 0) {
             return $textRotation;
@@ -388,14 +352,10 @@ class Xf
 
     /**
      * Map locked values.
-     *
-     * @param string $locked
-     *
-     * @return int
      */
-    private static function mapLocked($locked)
+    private static function mapLocked(?string $locked): int
     {
-        return array_key_exists($locked, self::LOCK_ARRAY) ? self::LOCK_ARRAY[$locked] : 1;
+        return $locked !== null && array_key_exists($locked, self::LOCK_ARRAY) ? self::LOCK_ARRAY[$locked] : 1;
     }
 
     private const HIDDEN_ARRAY = [
@@ -406,13 +366,9 @@ class Xf
 
     /**
      * Map hidden.
-     *
-     * @param string $hidden
-     *
-     * @return int
      */
-    private static function mapHidden($hidden)
+    private static function mapHidden(?string $hidden): int
     {
-        return array_key_exists($hidden, self::HIDDEN_ARRAY) ? self::HIDDEN_ARRAY[$hidden] : 0;
+        return $hidden !== null && array_key_exists($hidden, self::HIDDEN_ARRAY) ? self::HIDDEN_ARRAY[$hidden] : 0;
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php
index 741faf4..f38eaff 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php
@@ -31,157 +31,110 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
 use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
 use ZipArchive;
 use ZipStream\Exception\OverflowException;
-use ZipStream\Option\Archive;
 use ZipStream\ZipStream;
 
 class Xlsx extends BaseWriter
 {
     /**
      * Office2003 compatibility.
-     *
-     * @var bool
      */
-    private $office2003compatibility = false;
+    private bool $office2003compatibility = false;
 
     /**
      * Private Spreadsheet.
-     *
-     * @var Spreadsheet
      */
-    private $spreadSheet;
+    private Spreadsheet $spreadSheet;
 
     /**
      * Private string table.
      *
      * @var string[]
      */
-    private $stringTable = [];
+    private array $stringTable = [];
 
     /**
      * Private unique Conditional HashTable.
      *
      * @var HashTable<Conditional>
      */
-    private $stylesConditionalHashTable;
+    private HashTable $stylesConditionalHashTable;
 
     /**
      * Private unique Style HashTable.
      *
      * @var HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
      */
-    private $styleHashTable;
+    private HashTable $styleHashTable;
 
     /**
      * Private unique Fill HashTable.
      *
      * @var HashTable<Fill>
      */
-    private $fillHashTable;
+    private HashTable $fillHashTable;
 
     /**
      * Private unique \PhpOffice\PhpSpreadsheet\Style\Font HashTable.
      *
      * @var HashTable<Font>
      */
-    private $fontHashTable;
+    private HashTable $fontHashTable;
 
     /**
      * Private unique Borders HashTable.
      *
      * @var HashTable<Borders>
      */
-    private $bordersHashTable;
+    private HashTable $bordersHashTable;
 
     /**
      * Private unique NumberFormat HashTable.
      *
      * @var HashTable<NumberFormat>
      */
-    private $numFmtHashTable;
+    private HashTable $numFmtHashTable;
 
     /**
      * Private unique \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\BaseDrawing HashTable.
      *
      * @var HashTable<BaseDrawing>
      */
-    private $drawingHashTable;
+    private HashTable $drawingHashTable;
 
     /**
      * Private handle for zip stream.
-     *
-     * @var ZipStream
      */
-    private $zip;
+    private ZipStream $zip;
 
-    /**
-     * @var Chart
-     */
-    private $writerPartChart;
+    private Chart $writerPartChart;
 
-    /**
-     * @var Comments
-     */
-    private $writerPartComments;
+    private Comments $writerPartComments;
 
-    /**
-     * @var ContentTypes
-     */
-    private $writerPartContentTypes;
+    private ContentTypes $writerPartContentTypes;
 
-    /**
-     * @var DocProps
-     */
-    private $writerPartDocProps;
+    private DocProps $writerPartDocProps;
 
-    /**
-     * @var Drawing
-     */
-    private $writerPartDrawing;
+    private Drawing $writerPartDrawing;
 
-    /**
-     * @var Rels
-     */
-    private $writerPartRels;
+    private Rels $writerPartRels;
 
-    /**
-     * @var RelsRibbon
-     */
-    private $writerPartRelsRibbon;
+    private RelsRibbon $writerPartRelsRibbon;
 
-    /**
-     * @var RelsVBA
-     */
-    private $writerPartRelsVBA;
+    private RelsVBA $writerPartRelsVBA;
 
-    /**
-     * @var StringTable
-     */
-    private $writerPartStringTable;
+    private StringTable $writerPartStringTable;
 
-    /**
-     * @var Style
-     */
-    private $writerPartStyle;
+    private Style $writerPartStyle;
 
-    /**
-     * @var Theme
-     */
-    private $writerPartTheme;
+    private Theme $writerPartTheme;
 
-    /**
-     * @var Table
-     */
-    private $writerPartTable;
+    private Table $writerPartTable;
 
-    /**
-     * @var Workbook
-     */
-    private $writerPartWorkbook;
+    private Workbook $writerPartWorkbook;
 
-    /**
-     * @var Worksheet
-     */
-    private $writerPartWorksheet;
+    private Worksheet $writerPartWorksheet;
+
+    private bool $explicitStyle0 = false;
 
     /**
      * Create a new Xlsx Writer.
@@ -207,19 +160,12 @@ class Xlsx extends BaseWriter
         $this->writerPartWorksheet = new Worksheet($this);
 
         // Set HashTable variables
-        // @phpstan-ignore-next-line
         $this->bordersHashTable = new HashTable();
-        // @phpstan-ignore-next-line
         $this->drawingHashTable = new HashTable();
-        // @phpstan-ignore-next-line
         $this->fillHashTable = new HashTable();
-        // @phpstan-ignore-next-line
         $this->fontHashTable = new HashTable();
-        // @phpstan-ignore-next-line
         $this->numFmtHashTable = new HashTable();
-        // @phpstan-ignore-next-line
         $this->styleHashTable = new HashTable();
-        // @phpstan-ignore-next-line
         $this->stylesConditionalHashTable = new HashTable();
     }
 
@@ -377,7 +323,7 @@ class Xlsx extends BaseWriter
         }
 
         // Add theme to ZIP file
-        $zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme();
+        $zipContent['xl/theme/theme1.xml'] = $this->getWriterPartTheme()->writeTheme($this->spreadSheet);
 
         // Add string table to ZIP file
         $zipContent['xl/sharedStrings.xml'] = $this->getWriterPartStringTable()->writeStringTable($this->stringTable);
@@ -408,7 +354,7 @@ class Xlsx extends BaseWriter
         // Add worksheet relationships (drawings, ...)
         for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
             // Add relationships
-            $zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts, $tableRef1);
+            $zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts, $tableRef1, $zipContent);
 
             // Add unparsedLoadedData
             $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
@@ -443,7 +389,7 @@ class Xlsx extends BaseWriter
             }
 
             // Add unparsed drawings
-            if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) {
+            if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings']) && !isset($zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'])) {
                 foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
                     $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
                     if ($drawingFile !== false) {
@@ -453,16 +399,22 @@ class Xlsx extends BaseWriter
                     }
                 }
             }
+            if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']) && !isset($zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'])) {
+                $zipContent['xl/drawings/drawing' . ($i + 1) . '.xml'] = '<xml></xml>';
+            }
 
             // Add comment relationship parts
-            if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
+            $legacy = $unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['legacyDrawing'] ?? null;
+            if (count($this->spreadSheet->getSheet($i)->getComments()) > 0 || $legacy !== null) {
                 // VML Comments relationships
                 $zipContent['xl/drawings/_rels/vmlDrawing' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeVMLDrawingRelationships($this->spreadSheet->getSheet($i));
 
                 // VML Comments
-                $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
+                $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $legacy ?? $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
+            }
 
-                // Comments
+            // Comments
+            if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
                 $zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i));
 
                 // Media
@@ -477,7 +429,9 @@ class Xlsx extends BaseWriter
             // Add unparsed relationship parts
             if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
                 foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
-                    $zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
+                    if (!isset($zipContent[$vmlDrawing['filePath']])) {
+                        $zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
+                    }
                 }
             }
 
@@ -507,7 +461,7 @@ class Xlsx extends BaseWriter
             if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
                 $imageContents = null;
                 $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
-                if (strpos($imagePath, 'zip://') !== false) {
+                if (str_contains($imagePath, 'zip://')) {
                     $imagePath = substr($imagePath, 6);
                     $imagePathSplitted = explode('#', $imagePath);
 
@@ -523,7 +477,7 @@ class Xlsx extends BaseWriter
                 $zipContent['xl/media/' . $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()] = $imageContents;
             } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
                 ob_start();
-                /** @var callable */
+                /** @var callable $callable */
                 $callable = $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction();
                 call_user_func(
                     $callable,
@@ -541,18 +495,14 @@ class Xlsx extends BaseWriter
 
         $this->openFileHandle($filename);
 
-        $options = new Archive();
-        $options->setEnableZip64(false);
-        $options->setOutputStream($this->fileHandle);
-
-        $this->zip = new ZipStream(null, $options);
+        $this->zip = ZipStream0::newZipStream($this->fileHandle);
 
         $this->addZipFiles($zipContent);
 
         // Close file
         try {
             $this->zip->finish();
-        } catch (OverflowException $e) {
+        } catch (OverflowException) {
             throw new WriterException('Could not close resource.');
         }
 
@@ -561,10 +511,8 @@ class Xlsx extends BaseWriter
 
     /**
      * Get Spreadsheet object.
-     *
-     * @return Spreadsheet
      */
-    public function getSpreadsheet()
+    public function getSpreadsheet(): Spreadsheet
     {
         return $this->spreadSheet;
     }
@@ -576,7 +524,7 @@ class Xlsx extends BaseWriter
      *
      * @return $this
      */
-    public function setSpreadsheet(Spreadsheet $spreadsheet)
+    public function setSpreadsheet(Spreadsheet $spreadsheet): static
     {
         $this->spreadSheet = $spreadsheet;
 
@@ -588,7 +536,7 @@ class Xlsx extends BaseWriter
      *
      * @return string[]
      */
-    public function getStringTable()
+    public function getStringTable(): array
     {
         return $this->stringTable;
     }
@@ -598,7 +546,7 @@ class Xlsx extends BaseWriter
      *
      * @return HashTable<\PhpOffice\PhpSpreadsheet\Style\Style>
      */
-    public function getStyleHashTable()
+    public function getStyleHashTable(): HashTable
     {
         return $this->styleHashTable;
     }
@@ -608,7 +556,7 @@ class Xlsx extends BaseWriter
      *
      * @return HashTable<Conditional>
      */
-    public function getStylesConditionalHashTable()
+    public function getStylesConditionalHashTable(): HashTable
     {
         return $this->stylesConditionalHashTable;
     }
@@ -618,7 +566,7 @@ class Xlsx extends BaseWriter
      *
      * @return HashTable<Fill>
      */
-    public function getFillHashTable()
+    public function getFillHashTable(): HashTable
     {
         return $this->fillHashTable;
     }
@@ -628,7 +576,7 @@ class Xlsx extends BaseWriter
      *
      * @return HashTable<Font>
      */
-    public function getFontHashTable()
+    public function getFontHashTable(): HashTable
     {
         return $this->fontHashTable;
     }
@@ -638,7 +586,7 @@ class Xlsx extends BaseWriter
      *
      * @return HashTable<Borders>
      */
-    public function getBordersHashTable()
+    public function getBordersHashTable(): HashTable
     {
         return $this->bordersHashTable;
     }
@@ -648,7 +596,7 @@ class Xlsx extends BaseWriter
      *
      * @return HashTable<NumberFormat>
      */
-    public function getNumFmtHashTable()
+    public function getNumFmtHashTable(): HashTable
     {
         return $this->numFmtHashTable;
     }
@@ -658,17 +606,15 @@ class Xlsx extends BaseWriter
      *
      * @return HashTable<BaseDrawing>
      */
-    public function getDrawingHashTable()
+    public function getDrawingHashTable(): HashTable
     {
         return $this->drawingHashTable;
     }
 
     /**
      * Get Office2003 compatibility.
-     *
-     * @return bool
      */
-    public function getOffice2003Compatibility()
+    public function getOffice2003Compatibility(): bool
     {
         return $this->office2003compatibility;
     }
@@ -680,15 +626,14 @@ class Xlsx extends BaseWriter
      *
      * @return $this
      */
-    public function setOffice2003Compatibility($office2003compatibility)
+    public function setOffice2003Compatibility(bool $office2003compatibility): static
     {
         $this->office2003compatibility = $office2003compatibility;
 
         return $this;
     }
 
-    /** @var array */
-    private $pathNames = [];
+    private array $pathNames = [];
 
     private function addZipFile(string $path, string $content): void
     {
@@ -705,10 +650,7 @@ class Xlsx extends BaseWriter
         }
     }
 
-    /**
-     * @return mixed
-     */
-    private function processDrawing(WorksheetDrawing $drawing)
+    private function processDrawing(WorksheetDrawing $drawing): string|null|false
     {
         $data = null;
         $filename = $drawing->getPath();
@@ -752,4 +694,21 @@ class Xlsx extends BaseWriter
 
         return $data;
     }
+
+    public function getExplicitStyle0(): bool
+    {
+        return $this->explicitStyle0;
+    }
+
+    /**
+     * This may be useful if non-default Alignment is part of default style
+     * and you think you might want to open the spreadsheet
+     * with LibreOffice or Gnumeric.
+     */
+    public function setExplicitStyle0(bool $explicitStyle0): self
+    {
+        $this->explicitStyle0 = $explicitStyle0;
+
+        return $this;
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php
index ebe4341..d96ac09 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/AutoFilter.php
@@ -71,9 +71,9 @@ class AutoFilter extends WriterPart
     private static function writeAutoFilterColumnRule(Column $column, Rule $rule, XMLWriter $objWriter): void
     {
         if (
-            ($column->getFilterType() === Column::AUTOFILTER_FILTERTYPE_FILTER) &&
-            ($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_EQUAL) &&
-            ($rule->getValue() === '')
+            ($column->getFilterType() === Column::AUTOFILTER_FILTERTYPE_FILTER)
+            && ($rule->getOperator() === Rule::AUTOFILTER_COLUMN_RULE_EQUAL)
+            && ($rule->getValue() === '')
         ) {
             //    Filter rule for Blanks
             $objWriter->writeAttribute('blank', '1');
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
index 7d76823..ca16557 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
@@ -14,23 +14,19 @@ use PhpOffice\PhpSpreadsheet\Chart\Title;
 use PhpOffice\PhpSpreadsheet\Chart\TrendLine;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Style\Font;
 use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
 
 class Chart extends WriterPart
 {
-    /**
-     * @var int
-     */
-    private $seriesIndex;
+    private int $seriesIndex;
 
     /**
      * Write charts to XML format.
      *
-     * @param mixed $calculateCellValues
-     *
      * @return string XML Output
      */
-    public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $chart, $calculateCellValues = true)
+    public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $chart, bool $calculateCellValues = true): string
     {
         // Create XML writer
         $objWriter = null;
@@ -100,21 +96,31 @@ class Chart extends WriterPart
         $objWriter->writeAttribute('val', (string) (int) $chart->getPlotVisibleOnly());
         $objWriter->endElement();
 
-        $objWriter->startElement('c:dispBlanksAs');
-        $objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
-        $objWriter->endElement();
+        if ($chart->getDisplayBlanksAs() !== '') {
+            $objWriter->startElement('c:dispBlanksAs');
+            $objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
+            $objWriter->endElement();
+        }
 
         $objWriter->startElement('c:showDLblsOverMax');
         $objWriter->writeAttribute('val', '0');
         $objWriter->endElement();
 
         $objWriter->endElement(); // c:chart
+
+        $objWriter->startElement('c:spPr');
         if ($chart->getNoFill()) {
-            $objWriter->startElement('c:spPr');
             $objWriter->startElement('a:noFill');
             $objWriter->endElement(); // a:noFill
-            $objWriter->endElement(); // c:spPr
         }
+        $fillColor = $chart->getFillColor();
+        if ($fillColor->isUsable()) {
+            $this->writeColor($objWriter, $fillColor);
+        }
+        $borderLines = $chart->getBorderLines();
+        $this->writeLineStyles($objWriter, $borderLines);
+        $this->writeEffects($objWriter, $borderLines);
+        $objWriter->endElement(); // c:spPr
 
         $this->writePrintSettings($objWriter);
 
@@ -144,40 +150,99 @@ class Chart extends WriterPart
         if ($title === null) {
             return;
         }
+        if ($this->writeCalculatedTitle($objWriter, $title)) {
+            return;
+        }
 
         $objWriter->startElement('c:title');
-        $objWriter->startElement('c:tx');
-        $objWriter->startElement('c:rich');
-
-        $objWriter->startElement('a:bodyPr');
-        $objWriter->endElement();
-
-        $objWriter->startElement('a:lstStyle');
-        $objWriter->endElement();
-
-        $objWriter->startElement('a:p');
-        $objWriter->startElement('a:pPr');
-        $objWriter->startElement('a:defRPr');
-        $objWriter->endElement();
-        $objWriter->endElement();
-
         $caption = $title->getCaption();
-        if ((is_array($caption)) && (count($caption) > 0)) {
-            $caption = $caption[0];
-        }
-        $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
+        if ($caption !== null) {
+            $objWriter->startElement('c:tx');
+            $objWriter->startElement('c:rich');
 
-        $objWriter->endElement();
-        $objWriter->endElement();
-        $objWriter->endElement();
+            $objWriter->startElement('a:bodyPr');
+            $objWriter->endElement(); // a:bodyPr
+
+            $objWriter->startElement('a:lstStyle');
+            $objWriter->endElement(); // a:lstStyle
+
+            $objWriter->startElement('a:p');
+            $objWriter->startElement('a:pPr');
+            $objWriter->startElement('a:defRPr');
+            $objWriter->endElement(); // a:defRPr
+            $objWriter->endElement(); // a:pPr
+
+            if (is_array($caption)) {
+                $caption = $caption[0] ?? '';
+            }
+            $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
+
+            $objWriter->endElement(); // a:p
+            $objWriter->endElement(); // c:rich
+            $objWriter->endElement(); // c:tx
+        }
 
         $this->writeLayout($objWriter, $title->getLayout());
 
         $objWriter->startElement('c:overlay');
-        $objWriter->writeAttribute('val', '0');
-        $objWriter->endElement();
+        $objWriter->writeAttribute('val', ($title->getOverlay()) ? '1' : '0');
+        $objWriter->endElement(); // c:overlay
 
-        $objWriter->endElement();
+        $objWriter->endElement(); // c:title
+    }
+
+    /**
+     * Write Calculated Chart Title.
+     */
+    private function writeCalculatedTitle(XMLWriter $objWriter, Title $title): bool
+    {
+        $calc = $title->getCalculatedTitle($this->getParentWriter()->getSpreadsheet());
+        if (empty($calc)) {
+            return false;
+        }
+
+        $objWriter->startElement('c:title');
+        $objWriter->startElement('c:tx');
+        $objWriter->startElement('c:strRef');
+        $objWriter->writeElement('c:f', $title->getCellReference());
+        $objWriter->startElement('c:strCache');
+
+        $objWriter->startElement('c:ptCount');
+        $objWriter->writeAttribute('val', '1');
+        $objWriter->endElement(); // c:ptCount
+        $objWriter->startElement('c:pt');
+        $objWriter->writeAttribute('idx', '0');
+        $objWriter->writeElement('c:v', $calc);
+        $objWriter->endElement(); // c:pt
+
+        $objWriter->endElement(); // c:strCache
+        $objWriter->endElement(); // c:strRef
+        $objWriter->endElement(); // c:tx
+
+        $this->writeLayout($objWriter, $title->getLayout());
+
+        $objWriter->startElement('c:overlay');
+        $objWriter->writeAttribute('val', ($title->getOverlay()) ? '1' : '0');
+        $objWriter->endElement(); // c:overlay
+        // c:spPr
+
+        // c:txPr
+        $labelFont = $title->getFont();
+        if ($labelFont !== null) {
+            $objWriter->startElement('c:txPr');
+
+            $objWriter->startElement('a:bodyPr');
+            $objWriter->endElement(); // a:bodyPr
+            $objWriter->startElement('a:lstStyle');
+            $objWriter->endElement(); // a:lstStyle
+            $this->writeLabelFont($objWriter, $labelFont, null);
+
+            $objWriter->endElement(); // c:txPr
+        }
+
+        $objWriter->endElement(); // c:title
+
+        return true;
     }
 
     /**
@@ -201,6 +266,17 @@ class Chart extends WriterPart
         $objWriter->writeAttribute('val', ($legend->getOverlay()) ? '1' : '0');
         $objWriter->endElement();
 
+        $objWriter->startElement('c:spPr');
+        $fillColor = $legend->getFillColor();
+        if ($fillColor->isUsable()) {
+            $this->writeColor($objWriter, $fillColor);
+        }
+        $borderLines = $legend->getBorderLines();
+        $this->writeLineStyles($objWriter, $borderLines);
+        $this->writeEffects($objWriter, $borderLines);
+        $objWriter->endElement(); // c:spPr
+
+        $legendText = $legend->getLegendText();
         $objWriter->startElement('c:txPr');
         $objWriter->startElement('a:bodyPr');
         $objWriter->endElement();
@@ -213,17 +289,21 @@ class Chart extends WriterPart
         $objWriter->writeAttribute('rtl', '0');
 
         $objWriter->startElement('a:defRPr');
-        $objWriter->endElement();
-        $objWriter->endElement();
+        if ($legendText !== null) {
+            $this->writeColor($objWriter, $legendText->getFillColorObject());
+            $this->writeEffects($objWriter, $legendText);
+        }
+        $objWriter->endElement(); // a:defRpr
+        $objWriter->endElement(); // a:pPr
 
         $objWriter->startElement('a:endParaRPr');
         $objWriter->writeAttribute('lang', 'en-US');
-        $objWriter->endElement();
+        $objWriter->endElement(); // a:endParaRPr
 
-        $objWriter->endElement();
-        $objWriter->endElement();
+        $objWriter->endElement(); // a:p
+        $objWriter->endElement(); // c:txPr
 
-        $objWriter->endElement();
+        $objWriter->endElement(); // c:legend
     }
 
     /**
@@ -307,19 +387,26 @@ class Chart extends WriterPart
                 $objWriter->startElement('c:hiLowLines');
                 $objWriter->endElement();
 
-                $objWriter->startElement('c:upDownBars');
-
-                $objWriter->startElement('c:gapWidth');
-                $objWriter->writeAttribute('val', '300');
-                $objWriter->endElement();
-
-                $objWriter->startElement('c:upBars');
-                $objWriter->endElement();
-
-                $objWriter->startElement('c:downBars');
-                $objWriter->endElement();
-
-                $objWriter->endElement();
+                $gapWidth = $plotArea->getGapWidth();
+                $upBars = $plotArea->getUseUpBars();
+                $downBars = $plotArea->getUseDownBars();
+                if ($gapWidth !== null || $upBars || $downBars) {
+                    $objWriter->startElement('c:upDownBars');
+                    if ($gapWidth !== null) {
+                        $objWriter->startElement('c:gapWidth');
+                        $objWriter->writeAttribute('val', "$gapWidth");
+                        $objWriter->endElement();
+                    }
+                    if ($upBars) {
+                        $objWriter->startElement('c:upBars');
+                        $objWriter->endElement();
+                    }
+                    if ($downBars) {
+                        $objWriter->startElement('c:downBars');
+                        $objWriter->endElement();
+                    }
+                    $objWriter->endElement(); // c:upDownBars
+                }
             }
 
             //    Generate 3 unique numbers to use for axId values
@@ -428,8 +515,8 @@ class Chart extends WriterPart
             }
             $objWriter->endElement(); // c:spPr
         }
-        $fontColor = $chartLayout->getLabelFontColor();
-        if ($fontColor && $fontColor->isUsable()) {
+        $labelFont = $chartLayout->getLabelFont();
+        if ($labelFont !== null) {
             $objWriter->startElement('c:txPr');
 
             $objWriter->startElement('a:bodyPr');
@@ -445,14 +532,7 @@ class Chart extends WriterPart
 
             $objWriter->startElement('a:lstStyle');
             $objWriter->endElement(); // a:lstStyle
-
-            $objWriter->startElement('a:p');
-            $objWriter->startElement('a:pPr');
-            $objWriter->startElement('a:defRPr');
-            $this->writeColor($objWriter, $fontColor);
-            $objWriter->endElement(); // a:defRPr
-            $objWriter->endElement(); // a:pPr
-            $objWriter->endElement(); // a:p
+            $this->writeLabelFont($objWriter, $labelFont, $chartLayout->getLabelEffects());
 
             $objWriter->endElement(); // c:txPr
         }
@@ -481,12 +561,8 @@ class Chart extends WriterPart
 
     /**
      * Write Category Axis.
-     *
-     * @param string $id1
-     * @param string $id2
-     * @param bool $isMultiLevelSeries
      */
-    private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void
+    private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, string $id1, string $id2, bool $isMultiLevelSeries, Axis $yAxis): void
     {
         // N.B. writeCategoryAxis may be invoked with the last parameter($yAxis) using $xAxis for ScatterChart, etc
         // In that case, xAxis may contain values like the yAxis, or it may be a date axis (LINECHART).
@@ -508,6 +584,14 @@ class Chart extends WriterPart
         }
 
         $objWriter->startElement('c:scaling');
+        if (is_numeric($yAxis->getAxisOptionsProperty('logBase'))) {
+            $logBase = $yAxis->getAxisOptionsProperty('logBase') + 0;
+            if ($logBase >= 2 && $logBase <= 1000) {
+                $objWriter->startElement('c:logBase');
+                $objWriter->writeAttribute('val', (string) $logBase);
+                $objWriter->endElement();
+            }
+        }
         if ($yAxis->getAxisOptionsProperty('maximum') !== null) {
             $objWriter->startElement('c:max');
             $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('maximum'));
@@ -553,35 +637,37 @@ class Chart extends WriterPart
 
         if ($xAxisLabel !== null) {
             $objWriter->startElement('c:title');
-            $objWriter->startElement('c:tx');
-            $objWriter->startElement('c:rich');
-
-            $objWriter->startElement('a:bodyPr');
-            $objWriter->endElement();
-
-            $objWriter->startElement('a:lstStyle');
-            $objWriter->endElement();
-
-            $objWriter->startElement('a:p');
-
             $caption = $xAxisLabel->getCaption();
-            if (is_array($caption)) {
-                $caption = $caption[0];
-            }
-            $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
+            if ($caption !== null) {
+                $objWriter->startElement('c:tx');
+                $objWriter->startElement('c:rich');
 
-            $objWriter->endElement();
-            $objWriter->endElement();
-            $objWriter->endElement();
+                $objWriter->startElement('a:bodyPr');
+                $objWriter->endElement(); // a:bodyPr
+
+                $objWriter->startElement('a:lstStyle');
+                $objWriter->endElement(); // a::lstStyle
+
+                $objWriter->startElement('a:p');
+
+                if (is_array($caption)) {
+                    $caption = $caption[0];
+                }
+                $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
+
+                $objWriter->endElement(); // a:p
+                $objWriter->endElement(); // c:rich
+                $objWriter->endElement(); // c:tx
+            }
 
             $layout = $xAxisLabel->getLayout();
             $this->writeLayout($objWriter, $layout);
 
             $objWriter->startElement('c:overlay');
             $objWriter->writeAttribute('val', '0');
-            $objWriter->endElement();
+            $objWriter->endElement(); // c:overlay
 
-            $objWriter->endElement();
+            $objWriter->endElement(); // c:title
         }
 
         $objWriter->startElement('c:numFmt');
@@ -608,25 +694,24 @@ class Chart extends WriterPart
         }
 
         $textRotation = $yAxis->getAxisOptionsProperty('textRotation');
-        if (is_numeric($textRotation)) {
+        $axisText = $yAxis->getAxisText();
+
+        if ($axisText !== null || is_numeric($textRotation)) {
             $objWriter->startElement('c:txPr');
             $objWriter->startElement('a:bodyPr');
-            $objWriter->writeAttribute('rot', Properties::angleToXml((float) $textRotation));
+            if (is_numeric($textRotation)) {
+                $objWriter->writeAttribute('rot', Properties::angleToXml((float) $textRotation));
+            }
             $objWriter->endElement(); // a:bodyPr
             $objWriter->startElement('a:lstStyle');
             $objWriter->endElement(); // a:lstStyle
-            $objWriter->startElement('a:p');
-            $objWriter->startElement('a:pPr');
-            $objWriter->startElement('a:defRPr');
-            $objWriter->endElement(); // a:defRPr
-            $objWriter->endElement(); // a:pPr
-            $objWriter->endElement(); // a:p
+            $this->writeLabelFont($objWriter, ($axisText === null) ? null : $axisText->getFont(), $axisText);
             $objWriter->endElement(); // c:txPr
         }
 
         $objWriter->startElement('c:spPr');
         $this->writeColor($objWriter, $yAxis->getFillColorObject());
-        $this->writeLineStyles($objWriter, $yAxis);
+        $this->writeLineStyles($objWriter, $yAxis, $yAxis->getNoFill());
         $this->writeEffects($objWriter, $yAxis);
         $objWriter->endElement(); // spPr
 
@@ -703,11 +788,8 @@ class Chart extends WriterPart
      * Write Value Axis.
      *
      * @param null|string $groupType Chart type
-     * @param string $id1
-     * @param string $id2
-     * @param bool $isMultiLevelSeries
      */
-    private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis): void
+    private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, ?string $groupType, string $id1, string $id2, bool $isMultiLevelSeries, Axis $xAxis): void
     {
         $objWriter->startElement('c:' . Axis::AXIS_TYPE_VALUE);
         $majorGridlines = $xAxis->getMajorGridlines();
@@ -720,6 +802,14 @@ class Chart extends WriterPart
         }
 
         $objWriter->startElement('c:scaling');
+        if (is_numeric($xAxis->getAxisOptionsProperty('logBase'))) {
+            $logBase = $xAxis->getAxisOptionsProperty('logBase') + 0;
+            if ($logBase >= 2 && $logBase <= 1000) {
+                $objWriter->startElement('c:logBase');
+                $objWriter->writeAttribute('val', (string) $logBase);
+                $objWriter->endElement();
+            }
+        }
 
         if ($xAxis->getAxisOptionsProperty('maximum') !== null) {
             $objWriter->startElement('c:max');
@@ -769,26 +859,28 @@ class Chart extends WriterPart
 
         if ($yAxisLabel !== null) {
             $objWriter->startElement('c:title');
-            $objWriter->startElement('c:tx');
-            $objWriter->startElement('c:rich');
-
-            $objWriter->startElement('a:bodyPr');
-            $objWriter->endElement();
-
-            $objWriter->startElement('a:lstStyle');
-            $objWriter->endElement();
-
-            $objWriter->startElement('a:p');
-
             $caption = $yAxisLabel->getCaption();
-            if (is_array($caption)) {
-                $caption = $caption[0];
-            }
-            $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
+            if ($caption !== null) {
+                $objWriter->startElement('c:tx');
+                $objWriter->startElement('c:rich');
 
-            $objWriter->endElement();
-            $objWriter->endElement();
-            $objWriter->endElement();
+                $objWriter->startElement('a:bodyPr');
+                $objWriter->endElement(); // a:bodyPr
+
+                $objWriter->startElement('a:lstStyle');
+                $objWriter->endElement(); // a:lstStyle
+
+                $objWriter->startElement('a:p');
+
+                if (is_array($caption)) {
+                    $caption = $caption[0];
+                }
+                $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
+
+                $objWriter->endElement(); // a:p
+                $objWriter->endElement(); // c:rich
+                $objWriter->endElement(); // c:tx
+            }
 
             if ($groupType !== DataSeries::TYPE_BUBBLECHART) {
                 $layout = $yAxisLabel->getLayout();
@@ -797,9 +889,9 @@ class Chart extends WriterPart
 
             $objWriter->startElement('c:overlay');
             $objWriter->writeAttribute('val', '0');
-            $objWriter->endElement();
+            $objWriter->endElement(); // c:overlay
 
-            $objWriter->endElement();
+            $objWriter->endElement(); // c:title
         }
 
         $objWriter->startElement('c:numFmt');
@@ -826,25 +918,26 @@ class Chart extends WriterPart
         }
 
         $textRotation = $xAxis->getAxisOptionsProperty('textRotation');
-        if (is_numeric($textRotation)) {
+        $axisText = $xAxis->getAxisText();
+
+        if ($axisText !== null || is_numeric($textRotation)) {
             $objWriter->startElement('c:txPr');
             $objWriter->startElement('a:bodyPr');
-            $objWriter->writeAttribute('rot', Properties::angleToXml((float) $textRotation));
+            if (is_numeric($textRotation)) {
+                $objWriter->writeAttribute('rot', Properties::angleToXml((float) $textRotation));
+            }
             $objWriter->endElement(); // a:bodyPr
             $objWriter->startElement('a:lstStyle');
             $objWriter->endElement(); // a:lstStyle
-            $objWriter->startElement('a:p');
-            $objWriter->startElement('a:pPr');
-            $objWriter->startElement('a:defRPr');
-            $objWriter->endElement(); // a:defRPr
-            $objWriter->endElement(); // a:pPr
-            $objWriter->endElement(); // a:p
+
+            $this->writeLabelFont($objWriter, ($axisText === null) ? null : $axisText->getFont(), $axisText);
+
             $objWriter->endElement(); // c:txPr
         }
 
         $objWriter->startElement('c:spPr');
         $this->writeColor($objWriter, $xAxis->getFillColorObject());
-        $this->writeLineStyles($objWriter, $xAxis);
+        $this->writeLineStyles($objWriter, $xAxis, $xAxis->getNoFill());
         $this->writeEffects($objWriter, $xAxis);
         $objWriter->endElement(); //end spPr
 
@@ -873,6 +966,22 @@ class Chart extends WriterPart
                 $objWriter->endElement();
             }
 
+            if ($xAxis->getAxisType() === Axis::AXIS_TYPE_VALUE) {
+                $dispUnits = $xAxis->getAxisOptionsProperty('dispUnitsBuiltIn');
+                $dispUnits = ($dispUnits == Axis::TRILLION_INDEX) ? Axis::DISP_UNITS_TRILLIONS : (is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits);
+                if (in_array($dispUnits, Axis::DISP_UNITS_BUILTIN_INT, true)) {
+                    $objWriter->startElement('c:dispUnits');
+                    $objWriter->startElement('c:builtInUnit');
+                    $objWriter->writeAttribute('val', $dispUnits);
+                    $objWriter->endElement(); // c:builtInUnit
+                    if ($xAxis->getDispUnitsTitle() !== null) {
+                        // TODO output title elements
+                        $objWriter->writeElement('c:dispUnitsLbl');
+                    }
+                    $objWriter->endElement(); // c:dispUnits
+                }
+            }
+
             if ($xAxis->getAxisOptionsProperty('major_unit') !== null) {
                 $objWriter->startElement('c:majorUnit');
                 $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_unit'));
@@ -955,16 +1064,20 @@ class Chart extends WriterPart
         $groupCount = $plotArea->getPlotGroupCount();
 
         if ($groupCount == 1) {
-            $chartType = [$plotArea->getPlotGroupByIndex(0)->getPlotType()];
+            $plotType = $plotArea->getPlotGroupByIndex(0)->getPlotType();
+            $chartType = ($plotType === null) ? [] : [$plotType];
         } else {
             $chartTypes = [];
             for ($i = 0; $i < $groupCount; ++$i) {
-                $chartTypes[] = $plotArea->getPlotGroupByIndex($i)->getPlotType();
+                $plotType = $plotArea->getPlotGroupByIndex($i)->getPlotType();
+                if ($plotType !== null) {
+                    $chartTypes[] = $plotType;
+                }
             }
             $chartType = array_unique($chartTypes);
-            if (count($chartTypes) == 0) {
-                throw new WriterException('Chart is not yet implemented');
-            }
+        }
+        if (count($chartType) == 0) {
+            throw new WriterException('Chart is not yet implemented');
         }
 
         return $chartType;
@@ -999,7 +1112,7 @@ class Chart extends WriterPart
      * @param bool $valIsMultiLevelSeries Is value set a multi-series set
      * @param string $plotGroupingType Type of grouping for multi-series values
      */
-    private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWriter $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType): void
+    private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWriter $objWriter, bool &$catIsMultiLevelSeries, bool &$valIsMultiLevelSeries, string &$plotGroupingType): void
     {
         if ($plotGroup === null) {
             return;
@@ -1041,11 +1154,12 @@ class Chart extends WriterPart
             $objWriter->startElement('c:ser');
 
             $objWriter->startElement('c:idx');
-            $objWriter->writeAttribute('val', (string) ($this->seriesIndex + $plotSeriesIdx));
+            $adder = array_key_exists(0, $plotSeriesOrder) ? $this->seriesIndex : 0;
+            $objWriter->writeAttribute('val', (string) ($adder + $plotSeriesIdx));
             $objWriter->endElement();
 
             $objWriter->startElement('c:order');
-            $objWriter->writeAttribute('val', (string) ($this->seriesIndex + $plotSeriesRef));
+            $objWriter->writeAttribute('val', (string) ($adder + $plotSeriesRef));
             $objWriter->endElement();
 
             $plotLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx);
@@ -1054,14 +1168,6 @@ class Chart extends WriterPart
                 $labelFill = $plotLabel->getFillColorObject();
                 $labelFill = ($labelFill instanceof ChartColor) ? $labelFill : null;
             }
-            if ($plotLabel && $groupType !== DataSeries::TYPE_LINECHART) {
-                $fillColor = $plotLabel->getFillColorObject();
-                if ($fillColor !== null && !is_array($fillColor) && $fillColor->isUsable()) {
-                    $objWriter->startElement('c:spPr');
-                    $this->writeColor($objWriter, $fillColor);
-                    $objWriter->endElement(); // c:spPr
-                }
-            }
 
             //    Values
             $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesIdx);
@@ -1069,7 +1175,7 @@ class Chart extends WriterPart
             if ($plotSeriesValues !== false && in_array($groupType, self::CUSTOM_COLOR_TYPES, true)) {
                 $fillColorValues = $plotSeriesValues->getFillColorObject();
                 if ($fillColorValues !== null && is_array($fillColorValues)) {
-                    foreach ($plotSeriesValues->getDataValues() as $dataKey => $dataValue) {
+                    foreach (($plotSeriesValues->getDataValues() ?? []) as $dataKey => $dataValue) {
                         $this->writePlotSeriesValuesElement($objWriter, $dataKey, $fillColorValues[$dataKey] ?? null);
                     }
                 }
@@ -1093,6 +1199,12 @@ class Chart extends WriterPart
                 $plotSeriesValues !== false
             ) {
                 $objWriter->startElement('c:spPr');
+                if ($plotLabel && $groupType !== DataSeries::TYPE_LINECHART) {
+                    $fillColor = $plotLabel->getFillColorObject();
+                    if ($fillColor !== null && !is_array($fillColor) && $fillColor->isUsable()) {
+                        $this->writeColor($objWriter, $fillColor);
+                    }
+                }
                 $fillObject = $labelFill ?? $plotSeriesValues->getFillColorObject();
                 $callLineStyles = true;
                 if ($fillObject instanceof ChartColor && $fillObject->isUsable()) {
@@ -1327,7 +1439,7 @@ class Chart extends WriterPart
         $objWriter->writeAttribute('val', (string) $plotSeriesLabel->getPointCount());
         $objWriter->endElement();
 
-        foreach ($plotSeriesLabel->getDataValues() as $plotLabelKey => $plotLabelValue) {
+        foreach (($plotSeriesLabel->getDataValues() ?? []) as $plotLabelKey => $plotLabelValue) {
             $objWriter->startElement('c:pt');
             $objWriter->writeAttribute('idx', $plotLabelKey);
 
@@ -1345,7 +1457,7 @@ class Chart extends WriterPart
      * @param string $groupType Type of plot for dataseries
      * @param string $dataType Datatype of series values
      */
-    private function writePlotSeriesValues(?DataSeriesValues $plotSeriesValues, XMLWriter $objWriter, $groupType, $dataType = 'str'): void
+    private function writePlotSeriesValues(?DataSeriesValues $plotSeriesValues, XMLWriter $objWriter, string $groupType, string $dataType = 'str'): void
     {
         if ($plotSeriesValues === null) {
             return;
@@ -1369,7 +1481,7 @@ class Chart extends WriterPart
             for ($level = 0; $level < $levelCount; ++$level) {
                 $objWriter->startElement('c:lvl');
 
-                foreach ($plotSeriesValues->getDataValues() as $plotSeriesKey => $plotSeriesValue) {
+                foreach (($plotSeriesValues->getDataValues() ?? []) as $plotSeriesKey => $plotSeriesValue) {
                     if (isset($plotSeriesValue[$level])) {
                         $objWriter->startElement('c:pt');
                         $objWriter->writeAttribute('idx', $plotSeriesKey);
@@ -1397,7 +1509,7 @@ class Chart extends WriterPart
             $count = $plotSeriesValues->getPointCount();
             $source = $plotSeriesValues->getDataSource();
             $values = $plotSeriesValues->getDataValues();
-            if ($count > 1 || ($count === 1 && "=$source" !== (string) $values[0])) {
+            if ($count > 1 || ($count === 1 && is_array($values) && array_key_exists(0, $values) && "=$source" !== (string) $values[0])) {
                 $objWriter->startElement('c:' . $dataType . 'Cache');
 
                 if (($groupType != DataSeries::TYPE_PIECHART) && ($groupType != DataSeries::TYPE_PIECHART_3D) && ($groupType != DataSeries::TYPE_DONUTCHART)) {
@@ -1617,7 +1729,7 @@ class Chart extends WriterPart
         if (empty($xAxis->getShadowProperty('effect'))) {
             return;
         }
-        /** @var string */
+        /** @var string $effect */
         $effect = $xAxis->getShadowProperty('effect');
         $objWriter->startElement("a:$effect");
 
@@ -1769,4 +1881,55 @@ class Chart extends WriterPart
             }
         }
     }
+
+    private function writeLabelFont(XMLWriter $objWriter, ?Font $labelFont, ?Properties $axisText): void
+    {
+        $objWriter->startElement('a:p');
+        $objWriter->startElement('a:pPr');
+        $objWriter->startElement('a:defRPr');
+        if ($labelFont !== null) {
+            $fontSize = $labelFont->getSize();
+            if (is_numeric($fontSize)) {
+                $fontSize *= (($fontSize < 100) ? 100 : 1);
+                $objWriter->writeAttribute('sz', (string) $fontSize);
+            }
+            if ($labelFont->getBold() === true) {
+                $objWriter->writeAttribute('b', '1');
+            }
+            if ($labelFont->getItalic() === true) {
+                $objWriter->writeAttribute('i', '1');
+            }
+            $cap = $labelFont->getCap();
+            if ($cap !== null) {
+                $objWriter->writeAttribute('cap', $cap);
+            }
+            $fontColor = $labelFont->getChartColor();
+            if ($fontColor !== null) {
+                $this->writeColor($objWriter, $fontColor);
+            }
+        }
+        if ($axisText !== null) {
+            $this->writeEffects($objWriter, $axisText);
+        }
+        if ($labelFont !== null) {
+            if (!empty($labelFont->getLatin())) {
+                $objWriter->startElement('a:latin');
+                $objWriter->writeAttribute('typeface', $labelFont->getLatin());
+                $objWriter->endElement();
+            }
+            if (!empty($labelFont->getEastAsian())) {
+                $objWriter->startElement('a:eastAsian');
+                $objWriter->writeAttribute('typeface', $labelFont->getEastAsian());
+                $objWriter->endElement();
+            }
+            if (!empty($labelFont->getComplexScript())) {
+                $objWriter->startElement('a:complexScript');
+                $objWriter->writeAttribute('typeface', $labelFont->getComplexScript());
+                $objWriter->endElement();
+            }
+        }
+        $objWriter->endElement(); // a:defRPr
+        $objWriter->endElement(); // a:pPr
+        $objWriter->endElement(); // a:p
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php
index 9197388..f42c242 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Comments.php
@@ -6,15 +6,24 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Comment;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
 
 class Comments extends WriterPart
 {
+    private const VALID_HORIZONTAL_ALIGNMENT = [
+        Alignment::HORIZONTAL_CENTER,
+        Alignment::HORIZONTAL_DISTRIBUTED,
+        Alignment::HORIZONTAL_JUSTIFY,
+        Alignment::HORIZONTAL_LEFT,
+        Alignment::HORIZONTAL_RIGHT,
+    ];
+
     /**
      * Write comments to XML format.
      *
      * @return string XML Output
      */
-    public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
+    public function writeComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -70,7 +79,7 @@ class Comments extends WriterPart
      * @param Comment $comment Comment
      * @param array $authors Array of authors
      */
-    private function writeComment(XMLWriter $objWriter, $cellReference, Comment $comment, array $authors): void
+    private function writeComment(XMLWriter $objWriter, string $cellReference, Comment $comment, array $authors): void
     {
         // comment
         $objWriter->startElement('comment');
@@ -90,7 +99,7 @@ class Comments extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
+    public function writeVMLComments(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -161,7 +170,7 @@ class Comments extends WriterPart
      * @param string $cellReference Cell reference, eg: 'A1'
      * @param Comment $comment Comment
      */
-    private function writeVMLComment(XMLWriter $objWriter, $cellReference, Comment $comment): void
+    private function writeVMLComment(XMLWriter $objWriter, string $cellReference, Comment $comment): void
     {
         // Metadata
         [$column, $row] = Coordinate::indexesFromString($cellReference);
@@ -200,12 +209,14 @@ class Comments extends WriterPart
         $objWriter->endElement();
 
         // v:textbox
+        $textBoxArray = [Comment::TEXTBOX_DIRECTION_RTL => 'rtl', Comment::TEXTBOX_DIRECTION_LTR => 'ltr'];
+        $textboxRtl = $textBoxArray[strtolower($comment->getTextBoxDirection())] ?? 'auto';
         $objWriter->startElement('v:textbox');
-        $objWriter->writeAttribute('style', 'mso-direction-alt:auto');
+        $objWriter->writeAttribute('style', "mso-direction-alt:$textboxRtl");
 
         // div
         $objWriter->startElement('div');
-        $objWriter->writeAttribute('style', 'text-align:left');
+        $objWriter->writeAttribute('style', ($textboxRtl === 'rtl' ? 'text-align:right;direction:rtl' : 'text-align:left'));
         $objWriter->endElement();
 
         $objWriter->endElement();
@@ -223,6 +234,12 @@ class Comments extends WriterPart
         // x:AutoFill
         $objWriter->writeElement('x:AutoFill', 'False');
 
+        // x:TextHAlign horizontal alignment of text
+        $alignment = strtolower($comment->getAlignment());
+        if (in_array($alignment, self::VALID_HORIZONTAL_ALIGNMENT, true)) {
+            $objWriter->writeElement('x:TextHAlign', ucfirst($alignment));
+        }
+
         // x:Row
         $objWriter->writeElement('x:Row', (string) ($row - 1));
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
index 73657fc..e3b9ba5 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
@@ -18,7 +18,7 @@ class ContentTypes extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = false)
+    public function writeContentTypes(Spreadsheet $spreadsheet, bool $includeCharts = false): string
     {
         // Create XML writer
         $objWriter = null;
@@ -186,6 +186,13 @@ class ContentTypes extends WriterPart
                     }
                 }
             }
+
+            $bgImage = $spreadsheet->getSheet($i)->getBackgroundImage();
+            $mimeType = $spreadsheet->getSheet($i)->getBackgroundMime();
+            $extension = $spreadsheet->getSheet($i)->getBackgroundExtension();
+            if ($bgImage !== '' && !isset($aMediaContentTypes[$mimeType])) {
+                $this->writeDefaultContentType($objWriter, $extension, $mimeType);
+            }
         }
 
         // unparsed defaults
@@ -208,6 +215,8 @@ class ContentTypes extends WriterPart
         return $objWriter->getData();
     }
 
+    private static int $three = 3; // phpstan silliness
+
     /**
      * Get image mime type.
      *
@@ -215,12 +224,12 @@ class ContentTypes extends WriterPart
      *
      * @return string Mime Type
      */
-    private function getImageMimeType($filename)
+    private function getImageMimeType(string $filename): string
     {
         if (File::fileExists($filename)) {
             $image = getimagesize($filename);
 
-            return image_type_to_mime_type((is_array($image) && count($image) >= 3) ? $image[2] : 0);
+            return image_type_to_mime_type((is_array($image) && count($image) >= self::$three) ? $image[2] : 0);
         }
 
         throw new WriterException("File $filename does not exist");
@@ -232,7 +241,7 @@ class ContentTypes extends WriterPart
      * @param string $partName Part name
      * @param string $contentType Content type
      */
-    private function writeDefaultContentType(XMLWriter $objWriter, $partName, $contentType): void
+    private function writeDefaultContentType(XMLWriter $objWriter, string $partName, string $contentType): void
     {
         if ($partName != '' && $contentType != '') {
             // Write content type
@@ -251,7 +260,7 @@ class ContentTypes extends WriterPart
      * @param string $partName Part name
      * @param string $contentType Content type
      */
-    private function writeOverrideContentType(XMLWriter $objWriter, $partName, $contentType): void
+    private function writeOverrideContentType(XMLWriter $objWriter, string $partName, string $contentType): void
     {
         if ($partName != '' && $contentType != '') {
             // Write content type
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php
index 24752c9..4dac185 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DefinedNames.php
@@ -12,11 +12,9 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as ActualWorksheet;
 
 class DefinedNames
 {
-    /** @var XMLWriter */
-    private $objWriter;
+    private XMLWriter $objWriter;
 
-    /** @var Spreadsheet */
-    private $spreadsheet;
+    private Spreadsheet $spreadsheet;
 
     public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet)
     {
@@ -72,8 +70,8 @@ class DefinedNames
         $local = -1;
         if ($definedName->getLocalOnly() && $definedName->getScope() !== null) {
             try {
-                $local = $definedName->getScope()->getParent()->getIndex($definedName->getScope());
-            } catch (Exception $e) {
+                $local = $definedName->getScope()->getParentOrThrow()->getIndex($definedName->getScope());
+            } catch (Exception) {
                 // See issue 2266 - deleting sheet which contains
                 //     defined names will cause Exception above.
                 return;
@@ -114,7 +112,7 @@ class DefinedNames
             //    Strip any worksheet ref so we can make the cell ref absolute
             [, $range[0]] = ActualWorksheet::extractSheetTitle($range[0], true);
 
-            $range[0] = Coordinate::absoluteCoordinate($range[0]);
+            $range[0] = Coordinate::absoluteCoordinate($range[0] ?? '');
             if (count($range) > 1) {
                 $range[1] = Coordinate::absoluteCoordinate($range[1]);
             }
@@ -235,7 +233,7 @@ class DefinedNames
             $definedRange = substr($definedRange, 0, $offset) . $newRange . substr($definedRange, $offset + $length);
         }
 
-        if (substr($definedRange, 0, 1) === '=') {
+        if (str_starts_with($definedRange, '=')) {
             $definedRange = substr($definedRange, 1);
         }
 
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
index 8902826..e2c6320 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/DocProps.php
@@ -15,7 +15,7 @@ class DocProps extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeDocPropsApp(Spreadsheet $spreadsheet)
+    public function writeDocPropsApp(Spreadsheet $spreadsheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -93,6 +93,9 @@ class DocProps extends WriterPart
         // SharedDoc
         $objWriter->writeElement('SharedDoc', 'false');
 
+        // HyperlinkBase
+        $objWriter->writeElement('HyperlinkBase', $spreadsheet->getProperties()->getHyperlinkBase());
+
         // HyperlinksChanged
         $objWriter->writeElement('HyperlinksChanged', 'false');
 
@@ -110,7 +113,7 @@ class DocProps extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeDocPropsCore(Spreadsheet $spreadsheet)
+    public function writeDocPropsCore(Spreadsheet $spreadsheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -179,7 +182,7 @@ class DocProps extends WriterPart
      *
      * @return null|string XML Output
      */
-    public function writeDocPropsCustom(Spreadsheet $spreadsheet)
+    public function writeDocPropsCustom(Spreadsheet $spreadsheet): ?string
     {
         $customPropertyList = $spreadsheet->getProperties()->getCustomProperties();
         if (empty($customPropertyList)) {
@@ -213,7 +216,7 @@ class DocProps extends WriterPart
 
             switch ($propertyType) {
                 case Properties::PROPERTY_TYPE_INTEGER:
-                    $objWriter->writeElement('vt:i4', $propertyValue);
+                    $objWriter->writeElement('vt:i4', (string) $propertyValue);
 
                     break;
                 case Properties::PROPERTY_TYPE_FLOAT:
@@ -232,7 +235,7 @@ class DocProps extends WriterPart
 
                     break;
                 default:
-                    $objWriter->writeElement('vt:lpwstr', $propertyValue);
+                    $objWriter->writeElement('vt:lpwstr', (string) $propertyValue);
 
                     break;
             }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
index d4f7f11..fc84c67 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php
@@ -20,7 +20,7 @@ class Drawing extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, $includeCharts = false)
+    public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, bool $includeCharts = false): string
     {
         // Create XML writer
         $objWriter = null;
@@ -67,7 +67,7 @@ class Drawing extends WriterPart
         }
 
         // unparsed AlternateContent
-        $unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
+        $unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
         if (isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'])) {
             foreach ($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) {
                 $objWriter->writeRaw($drawingAlternateContent);
@@ -82,10 +82,8 @@ class Drawing extends WriterPart
 
     /**
      * Write drawings to XML format.
-     *
-     * @param int $relationId
      */
-    public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $chart, $relationId = -1): void
+    public function writeChart(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Chart\Chart $chart, int $relationId = -1): void
     {
         $tl = $chart->getTopLeftPosition();
         $tlColRow = Coordinate::indexesFromString($tl['cell']);
@@ -178,11 +176,8 @@ class Drawing extends WriterPart
 
     /**
      * Write drawings to XML format.
-     *
-     * @param int $relationId
-     * @param null|int $hlinkClickId
      */
-    public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, $relationId = -1, $hlinkClickId = null): void
+    public function writeDrawing(XMLWriter $objWriter, BaseDrawing $drawing, int $relationId = -1, ?int $hlinkClickId = null): void
     {
         if ($relationId >= 0) {
             $isTwoCellAnchor = $drawing->getCoordinates2() !== '';
@@ -270,10 +265,21 @@ class Drawing extends WriterPart
             $objWriter->writeAttribute('r:embed', 'rId' . $relationId);
             $objWriter->endElement();
 
-            // a:stretch
-            $objWriter->startElement('a:stretch');
-            $objWriter->writeElement('a:fillRect', null);
-            $objWriter->endElement();
+            $srcRect = $drawing->getSrcRect();
+            if (!empty($srcRect)) {
+                $objWriter->startElement('a:srcRect');
+                foreach ($srcRect as $key => $value) {
+                    $objWriter->writeAttribute($key, (string) $value);
+                }
+                $objWriter->endElement(); // a:srcRect
+                $objWriter->startElement('a:stretch');
+                $objWriter->endElement(); // a:stretch
+            } else {
+                // a:stretch
+                $objWriter->startElement('a:stretch');
+                $objWriter->writeElement('a:fillRect', null);
+                $objWriter->endElement();
+            }
 
             $objWriter->endElement();
 
@@ -283,6 +289,8 @@ class Drawing extends WriterPart
             // a:xfrm
             $objWriter->startElement('a:xfrm');
             $objWriter->writeAttribute('rot', (string) SharedDrawing::degreesToAngle($drawing->getRotation()));
+            self::writeAttributeIf($objWriter, $drawing->getFlipVertical(), 'flipV', '1');
+            self::writeAttributeIf($objWriter, $drawing->getFlipHorizontal(), 'flipH', '1');
             if ($isTwoCellAnchor) {
                 $objWriter->startElement('a:ext');
                 $objWriter->writeAttribute('cx', self::stringEmu($drawing->getWidth()));
@@ -345,7 +353,7 @@ class Drawing extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
+    public function writeVMLHeaderFooterImages(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -490,7 +498,7 @@ class Drawing extends WriterPart
      *
      * @param string $reference Reference
      */
-    private function writeVMLHeaderFooterImage(XMLWriter $objWriter, $reference, HeaderFooterDrawing $image): void
+    private function writeVMLHeaderFooterImage(XMLWriter $objWriter, string $reference, HeaderFooterDrawing $image): void
     {
         // Calculate object id
         preg_match('{(\d+)}', md5($reference), $m);
@@ -529,7 +537,7 @@ class Drawing extends WriterPart
      *
      * @return BaseDrawing[] All drawings in PhpSpreadsheet
      */
-    public function allDrawings(Spreadsheet $spreadsheet)
+    public function allDrawings(Spreadsheet $spreadsheet): array
     {
         // Get an array of all drawings
         $aDrawings = [];
@@ -549,10 +557,7 @@ class Drawing extends WriterPart
         return $aDrawings;
     }
 
-    /**
-     * @param null|int $hlinkClickId
-     */
-    private function writeHyperLinkDrawing(XMLWriter $objWriter, $hlinkClickId): void
+    private function writeHyperLinkDrawing(XMLWriter $objWriter, ?int $hlinkClickId): void
     {
         if ($hlinkClickId === null) {
             return;
@@ -568,4 +573,11 @@ class Drawing extends WriterPart
     {
         return (string) SharedDrawing::pixelsToEMU($pixelValue);
     }
+
+    private static function writeAttributeIf(XMLWriter $objWriter, ?bool $condition, string $attr, string $val): void
+    {
+        if ($condition) {
+            $objWriter->writeAttribute($attr, $val);
+        }
+    }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php
index ecc247d..6f66ecc 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php
@@ -4,8 +4,8 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
 
 class FunctionPrefix
 {
-    const XLFNREGEXP = '/(?:_xlfn\.)?((?:_xlws\.)?('
-            // functions added with Excel 2010
+    const XLFNREGEXP = '/(?:_xlfn\.)?((?:_xlws\.)?\b('
+        // functions added with Excel 2010
         . 'beta[.]dist'
         . '|beta[.]inv'
         . '|binom[.]dist'
@@ -125,7 +125,6 @@ class FunctionPrefix
         . '|switch'
         // functions added with Excel 2019
         . '|concat'
-        . '|countifs'
         . '|ifs'
         . '|maxifs'
         . '|minifs'
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php
index 96a0e3b..1e6e3b4 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Rels.php
@@ -16,7 +16,7 @@ class Rels extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeRelationships(Spreadsheet $spreadsheet)
+    public function writeRelationships(Spreadsheet $spreadsheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -88,7 +88,7 @@ class Rels extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeWorkbookRelationships(Spreadsheet $spreadsheet)
+    public function writeWorkbookRelationships(Spreadsheet $spreadsheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -163,13 +163,12 @@ class Rels extends WriterPart
      *     rId1                 - Drawings
      *  rId_hyperlink_x     - Hyperlinks
      *
-     * @param int $worksheetId
      * @param bool $includeCharts Flag indicating if we should write charts
      * @param int $tableRef Table ID
      *
      * @return string XML Output
      */
-    public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, $worksheetId = 1, $includeCharts = false, $tableRef = 1)
+    public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, int $worksheetId = 1, bool $includeCharts = false, int $tableRef = 1, array &$zipContent = []): string
     {
         // Create XML writer
         $objWriter = null;
@@ -188,7 +187,7 @@ class Rels extends WriterPart
 
         // Write drawing relationships?
         $drawingOriginalIds = [];
-        $unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
+        $unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
         if (isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingOriginalIds'])) {
             $drawingOriginalIds = $unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingOriginalIds'];
         }
@@ -221,6 +220,20 @@ class Rels extends WriterPart
             );
         }
 
+        $backgroundImage = $worksheet->getBackgroundImage();
+        if ($backgroundImage !== '') {
+            $rId = 'Bg';
+            $uniqueName = md5(mt_rand(0, 9999) . time() . mt_rand(0, 9999));
+            $relPath = "../media/$uniqueName." . $worksheet->getBackgroundExtension();
+            $this->writeRelationship(
+                $objWriter,
+                $rId,
+                Namespaces::IMAGE,
+                $relPath
+            );
+            $zipContent["xl/media/$uniqueName." . $worksheet->getBackgroundExtension()] = $backgroundImage;
+        }
+
         // Write hyperlink relationships?
         $i = 1;
         foreach ($worksheet->getHyperlinkCollection() as $hyperlink) {
@@ -239,14 +252,16 @@ class Rels extends WriterPart
 
         // Write comments relationship?
         $i = 1;
-        if (count($worksheet->getComments()) > 0) {
+        if (count($worksheet->getComments()) > 0 || isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'])) {
             $this->writeRelationship(
                 $objWriter,
                 '_comments_vml' . $i,
                 Namespaces::VML,
                 '../drawings/vmlDrawing' . $worksheetId . '.vml'
             );
+        }
 
+        if (count($worksheet->getComments()) > 0) {
             $this->writeRelationship(
                 $objWriter,
                 '_comments' . $i,
@@ -288,13 +303,13 @@ class Rels extends WriterPart
 
     private function writeUnparsedRelationship(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, XMLWriter $objWriter, string $relationship, string $type): void
     {
-        $unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
+        $unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
         if (!isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()][$relationship])) {
             return;
         }
 
         foreach ($unparsedLoadedData['sheets'][$worksheet->getCodeName()][$relationship] as $rId => $value) {
-            if (substr($rId, 0, 17) !== '_headerfooter_vml') {
+            if (!str_starts_with($rId, '_headerfooter_vml')) {
                 $this->writeRelationship(
                     $objWriter,
                     $rId,
@@ -313,7 +328,7 @@ class Rels extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, &$chartRef, $includeCharts = false)
+    public function writeDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, int &$chartRef, bool $includeCharts = false): string
     {
         // Create XML writer
         $objWriter = null;
@@ -379,7 +394,7 @@ class Rels extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet)
+    public function writeHeaderFooterDrawingRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -457,7 +472,7 @@ class Rels extends WriterPart
      * @param string $target Relationship target
      * @param string $targetMode Relationship target mode
      */
-    private function writeRelationship(XMLWriter $objWriter, $id, $type, $target, $targetMode = ''): void
+    private function writeRelationship(XMLWriter $objWriter, $id, string $type, string $target, string $targetMode = ''): void
     {
         if ($type != '' && $target != '') {
             // Write relationship
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
index e231972..93c9777 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsRibbon.php
@@ -13,7 +13,7 @@ class RelsRibbon extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeRibbonRelationships(Spreadsheet $spreadsheet)
+    public function writeRibbonRelationships(Spreadsheet $spreadsheet): string
     {
         // Create XML writer
         $objWriter = null;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
index d5b6060..4ec0202 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/RelsVBA.php
@@ -12,7 +12,7 @@ class RelsVBA extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeVBARelationships()
+    public function writeVBARelationships(): string
     {
         // Create XML writer
         $objWriter = null;
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
index 7f62393..98b1ddd 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
@@ -21,7 +21,7 @@ class StringTable extends WriterPart
      *
      * @return string[] String table for worksheet
      */
-    public function createStringTable(ActualWorksheet $worksheet, $existingTable = null)
+    public function createStringTable(ActualWorksheet $worksheet, ?array $existingTable = null): array
     {
         // Create string lookup table
         $aStringTable = [];
@@ -40,18 +40,18 @@ class StringTable extends WriterPart
             $cell = $worksheet->getCellCollection()->get($coordinate);
             $cellValue = $cell->getValue();
             if (
-                !is_object($cellValue) &&
-                ($cellValue !== null) &&
-                $cellValue !== '' &&
-                ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL) &&
-                !isset($aFlippedStringTable[$cellValue])
+                !is_object($cellValue)
+                && ($cellValue !== null)
+                && $cellValue !== ''
+                && ($cell->getDataType() == DataType::TYPE_STRING || $cell->getDataType() == DataType::TYPE_STRING2 || $cell->getDataType() == DataType::TYPE_NULL)
+                && !isset($aFlippedStringTable[$cellValue])
             ) {
                 $aStringTable[] = $cellValue;
                 $aFlippedStringTable[$cellValue] = true;
             } elseif (
-                $cellValue instanceof RichText &&
-                ($cellValue !== null) &&
-                !isset($aFlippedStringTable[$cellValue->getHashCode()])
+                $cellValue instanceof RichText
+                && ($cellValue !== null)
+                && !isset($aFlippedStringTable[$cellValue->getHashCode()])
             ) {
                 $aStringTable[] = $cellValue;
                 $aFlippedStringTable[$cellValue->getHashCode()] = true;
@@ -64,11 +64,11 @@ class StringTable extends WriterPart
     /**
      * Write string table to XML format.
      *
-     * @param (string|RichText)[] $stringTable
+     * @param (RichText|string)[] $stringTable
      *
      * @return string XML Output
      */
-    public function writeStringTable(array $stringTable)
+    public function writeStringTable(array $stringTable): string
     {
         // Create XML writer
         $objWriter = null;
@@ -113,9 +113,9 @@ class StringTable extends WriterPart
     /**
      * Write Rich Text.
      *
-     * @param string $prefix Optional Namespace prefix
+     * @param ?string $prefix Optional Namespace prefix
      */
-    public function writeRichText(XMLWriter $objWriter, RichText $richText, $prefix = null): void
+    public function writeRichText(XMLWriter $objWriter, RichText $richText, ?string $prefix = null): void
     {
         if ($prefix !== null) {
             $prefix .= ':';
@@ -205,7 +205,7 @@ class StringTable extends WriterPart
      * @param RichText|string $richText text string or Rich text
      * @param string $prefix Optional Namespace prefix
      */
-    public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $prefix = ''): void
+    public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, string $prefix = ''): void
     {
         if (!($richText instanceof RichText)) {
             $textRun = $richText;
@@ -226,9 +226,10 @@ class StringTable extends WriterPart
             if ($element->getFont() !== null) {
                 // rPr
                 $objWriter->startElement($prefix . 'rPr');
-                $size = $element->getFont()->getSize();
-                if (is_numeric($size)) {
-                    $objWriter->writeAttribute('sz', (string) (int) ($size * 100));
+                $fontSize = $element->getFont()->getSize();
+                if (is_numeric($fontSize)) {
+                    $fontSize *= (($fontSize < 100) ? 100 : 1);
+                    $objWriter->writeAttribute('sz', (string) $fontSize);
                 }
 
                 // Bold
@@ -323,10 +324,8 @@ class StringTable extends WriterPart
      * Flip string table (for index searching).
      *
      * @param array $stringTable Stringtable
-     *
-     * @return array
      */
-    public function flipStringTable(array $stringTable)
+    public function flipStringTable(array $stringTable): array
     {
         // Return value
         $returnValue = [];
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php
index b914664..daac5d0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Style.php
@@ -22,7 +22,7 @@ class Style extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeStyles(Spreadsheet $spreadsheet)
+    public function writeStyles(Spreadsheet $spreadsheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -112,8 +112,13 @@ class Style extends WriterPart
         $objWriter->writeAttribute('count', (string) count($spreadsheet->getCellXfCollection()));
 
         // xf
+        $alignment = new Alignment();
+        $defaultAlignHash = $alignment->getHashCode();
+        if ($defaultAlignHash !== $spreadsheet->getDefaultStyle()->getAlignment()->getHashCode()) {
+            $defaultAlignHash = '';
+        }
         foreach ($spreadsheet->getCellXfCollection() as $cellXf) {
-            $this->writeCellStyleXf($objWriter, $cellXf, $spreadsheet);
+            $this->writeCellStyleXf($objWriter, $cellXf, $spreadsheet, $defaultAlignHash);
         }
 
         $objWriter->endElement();
@@ -164,8 +169,8 @@ class Style extends WriterPart
     {
         // Check if this is a pattern type or gradient type
         if (
-            $fill->getFillType() === Fill::FILL_GRADIENT_LINEAR ||
-            $fill->getFillType() === Fill::FILL_GRADIENT_PATH
+            $fill->getFillType() === Fill::FILL_GRADIENT_LINEAR
+            || $fill->getFillType() === Fill::FILL_GRADIENT_PATH
         ) {
             // Gradient fill
             $this->writeGradientFill($objWriter, $fill);
@@ -243,8 +248,13 @@ class Style extends WriterPart
         if (self::writePatternColors($fill)) {
             // fgColor
             if ($fill->getStartColor()->getARGB()) {
-                $objWriter->startElement('fgColor');
-                $objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
+                if (!$fill->getEndColor()->getARGB() && $fill->getFillType() === Fill::FILL_SOLID) {
+                    $objWriter->startElement('bgColor');
+                    $objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
+                } else {
+                    $objWriter->startElement('fgColor');
+                    $objWriter->writeAttribute('rgb', $fill->getStartColor()->getARGB());
+                }
                 $objWriter->endElement();
             }
             // bgColor
@@ -260,13 +270,21 @@ class Style extends WriterPart
         $objWriter->endElement();
     }
 
+    private function startFont(XMLWriter $objWriter, bool &$fontStarted): void
+    {
+        if (!$fontStarted) {
+            $fontStarted = true;
+            $objWriter->startElement('font');
+        }
+    }
+
     /**
      * Write Font.
      */
     private function writeFont(XMLWriter $objWriter, Font $font): void
     {
+        $fontStarted = false;
         // font
-        $objWriter->startElement('font');
         //    Weird! The order of these elements actually makes a difference when opening Xlsx
         //        files in Excel2003 with the compatibility pack. It's not documented behaviour,
         //        and makes for a real WTF!
@@ -275,6 +293,7 @@ class Style extends WriterPart
         // for conditional formatting). Otherwise it will apparently not be picked up in conditional
         // formatting style dialog
         if ($font->getBold() !== null) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('b');
             $objWriter->writeAttribute('val', $font->getBold() ? '1' : '0');
             $objWriter->endElement();
@@ -282,6 +301,7 @@ class Style extends WriterPart
 
         // Italic
         if ($font->getItalic() !== null) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('i');
             $objWriter->writeAttribute('val', $font->getItalic() ? '1' : '0');
             $objWriter->endElement();
@@ -289,6 +309,7 @@ class Style extends WriterPart
 
         // Strikethrough
         if ($font->getStrikethrough() !== null) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('strike');
             $objWriter->writeAttribute('val', $font->getStrikethrough() ? '1' : '0');
             $objWriter->endElement();
@@ -296,6 +317,7 @@ class Style extends WriterPart
 
         // Underline
         if ($font->getUnderline() !== null) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('u');
             $objWriter->writeAttribute('val', $font->getUnderline());
             $objWriter->endElement();
@@ -303,6 +325,7 @@ class Style extends WriterPart
 
         // Superscript / subscript
         if ($font->getSuperscript() === true || $font->getSubscript() === true) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('vertAlign');
             if ($font->getSuperscript() === true) {
                 $objWriter->writeAttribute('val', 'superscript');
@@ -314,6 +337,7 @@ class Style extends WriterPart
 
         // Size
         if ($font->getSize() !== null) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('sz');
             $objWriter->writeAttribute('val', StringHelper::formatNumber($font->getSize()));
             $objWriter->endElement();
@@ -321,6 +345,7 @@ class Style extends WriterPart
 
         // Foreground color
         if ($font->getColor()->getARGB() !== null) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('color');
             $objWriter->writeAttribute('rgb', $font->getColor()->getARGB());
             $objWriter->endElement();
@@ -328,12 +353,22 @@ class Style extends WriterPart
 
         // Name
         if ($font->getName() !== null) {
+            $this->startFont($objWriter, $fontStarted);
             $objWriter->startElement('name');
             $objWriter->writeAttribute('val', $font->getName());
             $objWriter->endElement();
         }
 
-        $objWriter->endElement();
+        if (!empty($font->getScheme())) {
+            $this->startFont($objWriter, $fontStarted);
+            $objWriter->startElement('scheme');
+            $objWriter->writeAttribute('val', $font->getScheme());
+            $objWriter->endElement();
+        }
+
+        if ($fontStarted) {
+            $objWriter->endElement();
+        }
     }
 
     /**
@@ -371,13 +406,10 @@ class Style extends WriterPart
         $objWriter->endElement();
     }
 
-    /** @var mixed */
-    private static $scrutinizerFalse = false;
-
     /**
      * Write Cell Style Xf.
      */
-    private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style, Spreadsheet $spreadsheet): void
+    private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style, Spreadsheet $spreadsheet, string $defaultAlignHash): void
     {
         // xf
         $objWriter->startElement('xf');
@@ -387,7 +419,7 @@ class Style extends WriterPart
             $objWriter->writeAttribute('quotePrefix', '1');
         }
 
-        if ($style->getNumberFormat()->getBuiltInFormatCode() === self::$scrutinizerFalse) {
+        if ($style->getNumberFormat()->getBuiltInFormatCode() === false) {
             $objWriter->writeAttribute('numFmtId', (string) (int) ($this->getParentWriter()->getNumFmtHashTable()->getIndexForHashCode($style->getNumberFormat()->getHashCode()) + 164));
         } else {
             $objWriter->writeAttribute('numFmtId', (string) (int) $style->getNumberFormat()->getBuiltInFormatCode());
@@ -401,40 +433,46 @@ class Style extends WriterPart
         $objWriter->writeAttribute('applyNumberFormat', ($spreadsheet->getDefaultStyle()->getNumberFormat()->getHashCode() != $style->getNumberFormat()->getHashCode()) ? '1' : '0');
         $objWriter->writeAttribute('applyFill', ($spreadsheet->getDefaultStyle()->getFill()->getHashCode() != $style->getFill()->getHashCode()) ? '1' : '0');
         $objWriter->writeAttribute('applyBorder', ($spreadsheet->getDefaultStyle()->getBorders()->getHashCode() != $style->getBorders()->getHashCode()) ? '1' : '0');
-        $objWriter->writeAttribute('applyAlignment', ($spreadsheet->getDefaultStyle()->getAlignment()->getHashCode() != $style->getAlignment()->getHashCode()) ? '1' : '0');
+        if ($defaultAlignHash !== '' && $defaultAlignHash === $style->getAlignment()->getHashCode()) {
+            $applyAlignment = '0';
+        } else {
+            $applyAlignment = '1';
+        }
+        $objWriter->writeAttribute('applyAlignment', $applyAlignment);
         if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
             $objWriter->writeAttribute('applyProtection', 'true');
         }
 
         // alignment
-        $objWriter->startElement('alignment');
-        $vertical = Alignment::VERTICAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getVertical()] ?? '';
-        $horizontal = Alignment::HORIZONTAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getHorizontal()] ?? '';
-        if ($horizontal !== '') {
-            $objWriter->writeAttribute('horizontal', $horizontal);
-        }
-        if ($vertical !== '') {
-            $objWriter->writeAttribute('vertical', $vertical);
-        }
+        if ($applyAlignment === '1') {
+            $objWriter->startElement('alignment');
+            $vertical = Alignment::VERTICAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getVertical()] ?? '';
+            $horizontal = Alignment::HORIZONTAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getHorizontal()] ?? '';
+            if ($horizontal !== '') {
+                $objWriter->writeAttribute('horizontal', $horizontal);
+            }
+            if ($vertical !== '') {
+                $objWriter->writeAttribute('vertical', $vertical);
+            }
 
-        $textRotation = 0;
-        if ($style->getAlignment()->getTextRotation() >= 0) {
-            $textRotation = $style->getAlignment()->getTextRotation();
-        } else {
-            $textRotation = 90 - $style->getAlignment()->getTextRotation();
-        }
-        $objWriter->writeAttribute('textRotation', (string) $textRotation);
+            if ($style->getAlignment()->getTextRotation() >= 0) {
+                $textRotation = $style->getAlignment()->getTextRotation();
+            } else {
+                $textRotation = 90 - $style->getAlignment()->getTextRotation();
+            }
+            $objWriter->writeAttribute('textRotation', (string) $textRotation);
 
-        $objWriter->writeAttribute('wrapText', ($style->getAlignment()->getWrapText() ? 'true' : 'false'));
-        $objWriter->writeAttribute('shrinkToFit', ($style->getAlignment()->getShrinkToFit() ? 'true' : 'false'));
+            $objWriter->writeAttribute('wrapText', ($style->getAlignment()->getWrapText() ? 'true' : 'false'));
+            $objWriter->writeAttribute('shrinkToFit', ($style->getAlignment()->getShrinkToFit() ? 'true' : 'false'));
 
-        if ($style->getAlignment()->getIndent() > 0) {
-            $objWriter->writeAttribute('indent', (string) $style->getAlignment()->getIndent());
+            if ($style->getAlignment()->getIndent() > 0) {
+                $objWriter->writeAttribute('indent', (string) $style->getAlignment()->getIndent());
+            }
+            if ($style->getAlignment()->getReadOrder() > 0) {
+                $objWriter->writeAttribute('readingOrder', (string) $style->getAlignment()->getReadOrder());
+            }
+            $objWriter->endElement();
         }
-        if ($style->getAlignment()->getReadOrder() > 0) {
-            $objWriter->writeAttribute('readingOrder', (string) $style->getAlignment()->getReadOrder());
-        }
-        $objWriter->endElement();
 
         // protection
         if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
@@ -468,54 +506,9 @@ class Style extends WriterPart
         // fill
         $this->writeFill($objWriter, $style->getFill());
 
-        // alignment
-        $objWriter->startElement('alignment');
-        $horizontal = Alignment::HORIZONTAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getHorizontal()] ?? '';
-        if ($horizontal) {
-            $objWriter->writeAttribute('horizontal', $horizontal);
-        }
-        $vertical = Alignment::VERTICAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getVertical()] ?? '';
-        if ($vertical) {
-            $objWriter->writeAttribute('vertical', $vertical);
-        }
-
-        if ($style->getAlignment()->getTextRotation() !== null) {
-            $textRotation = 0;
-            if ($style->getAlignment()->getTextRotation() >= 0) {
-                $textRotation = $style->getAlignment()->getTextRotation();
-            } else {
-                $textRotation = 90 - $style->getAlignment()->getTextRotation();
-            }
-            $objWriter->writeAttribute('textRotation', (string) $textRotation);
-        }
-        $objWriter->endElement();
-
         // border
         $this->writeBorder($objWriter, $style->getBorders());
 
-        // protection
-        if ((!empty($style->getProtection()->getLocked())) || (!empty($style->getProtection()->getHidden()))) {
-            if (
-                $style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT ||
-                $style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT
-            ) {
-                $objWriter->startElement('protection');
-                if (
-                    ($style->getProtection()->getLocked() !== null) &&
-                    ($style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT)
-                ) {
-                    $objWriter->writeAttribute('locked', ($style->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
-                }
-                if (
-                    ($style->getProtection()->getHidden() !== null) &&
-                    ($style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT)
-                ) {
-                    $objWriter->writeAttribute('hidden', ($style->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
-                }
-                $objWriter->endElement();
-            }
-        }
-
         $objWriter->endElement();
     }
 
@@ -524,11 +517,14 @@ class Style extends WriterPart
      *
      * @param string $name Element name
      */
-    private function writeBorderPr(XMLWriter $objWriter, $name, Border $border): void
+    private function writeBorderPr(XMLWriter $objWriter, string $name, Border $border): void
     {
         // Write BorderPr
-        if ($border->getBorderStyle() != Border::BORDER_NONE) {
-            $objWriter->startElement($name);
+        if ($border->getBorderStyle() === Border::BORDER_OMIT) {
+            return;
+        }
+        $objWriter->startElement($name);
+        if ($border->getBorderStyle() !== Border::BORDER_NONE) {
             $objWriter->writeAttribute('style', $border->getBorderStyle());
 
             // color
@@ -536,10 +532,9 @@ class Style extends WriterPart
                 $objWriter->startElement('color');
                 $objWriter->writeAttribute('rgb', $border->getColor()->getARGB());
                 $objWriter->endElement();
-
-                $objWriter->endElement();
             }
         }
+        $objWriter->endElement();
     }
 
     /**
@@ -547,7 +542,7 @@ class Style extends WriterPart
      *
      * @param int $id Number Format identifier
      */
-    private function writeNumFmt(XMLWriter $objWriter, ?NumberFormat $numberFormat, $id = 0): void
+    private function writeNumFmt(XMLWriter $objWriter, ?NumberFormat $numberFormat, int $id = 0): void
     {
         // Translate formatcode
         $formatCode = ($numberFormat === null) ? null : $numberFormat->getFormatCode();
@@ -566,7 +561,7 @@ class Style extends WriterPart
      *
      * @return \PhpOffice\PhpSpreadsheet\Style\Style[] All styles in PhpSpreadsheet
      */
-    public function allStyles(Spreadsheet $spreadsheet)
+    public function allStyles(Spreadsheet $spreadsheet): array
     {
         return $spreadsheet->getCellXfCollection();
     }
@@ -576,7 +571,7 @@ class Style extends WriterPart
      *
      * @return Conditional[] All conditional styles in PhpSpreadsheet
      */
-    public function allConditionalStyles(Spreadsheet $spreadsheet)
+    public function allConditionalStyles(Spreadsheet $spreadsheet): array
     {
         // Get an array of all styles
         $aStyles = [];
@@ -598,7 +593,7 @@ class Style extends WriterPart
      *
      * @return Fill[] All fills in PhpSpreadsheet
      */
-    public function allFills(Spreadsheet $spreadsheet)
+    public function allFills(Spreadsheet $spreadsheet): array
     {
         // Get an array of unique fills
         $aFills = [];
@@ -613,7 +608,6 @@ class Style extends WriterPart
         $aFills[] = $fill1;
         // The remaining fills
         $aStyles = $this->allStyles($spreadsheet);
-        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
         foreach ($aStyles as $style) {
             if (!isset($aFills[$style->getFill()->getHashCode()])) {
                 $aFills[$style->getFill()->getHashCode()] = $style->getFill();
@@ -628,13 +622,12 @@ class Style extends WriterPart
      *
      * @return Font[] All fonts in PhpSpreadsheet
      */
-    public function allFonts(Spreadsheet $spreadsheet)
+    public function allFonts(Spreadsheet $spreadsheet): array
     {
         // Get an array of unique fonts
         $aFonts = [];
         $aStyles = $this->allStyles($spreadsheet);
 
-        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
         foreach ($aStyles as $style) {
             if (!isset($aFonts[$style->getFont()->getHashCode()])) {
                 $aFonts[$style->getFont()->getHashCode()] = $style->getFont();
@@ -649,13 +642,12 @@ class Style extends WriterPart
      *
      * @return Borders[] All borders in PhpSpreadsheet
      */
-    public function allBorders(Spreadsheet $spreadsheet)
+    public function allBorders(Spreadsheet $spreadsheet): array
     {
         // Get an array of unique borders
         $aBorders = [];
         $aStyles = $this->allStyles($spreadsheet);
 
-        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
         foreach ($aStyles as $style) {
             if (!isset($aBorders[$style->getBorders()->getHashCode()])) {
                 $aBorders[$style->getBorders()->getHashCode()] = $style->getBorders();
@@ -670,13 +662,12 @@ class Style extends WriterPart
      *
      * @return NumberFormat[] All number formats in PhpSpreadsheet
      */
-    public function allNumberFormats(Spreadsheet $spreadsheet)
+    public function allNumberFormats(Spreadsheet $spreadsheet): array
     {
         // Get an array of unique number formats
         $aNumFmts = [];
         $aStyles = $this->allStyles($spreadsheet);
 
-        /** @var \PhpOffice\PhpSpreadsheet\Style\Style $style */
         foreach ($aStyles as $style) {
             if ($style->getNumberFormat()->getBuiltInFormatCode() === false && !isset($aNumFmts[$style->getNumberFormat()->getHashCode()])) {
                 $aNumFmts[$style->getNumberFormat()->getHashCode()] = $style->getNumberFormat();
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Table.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Table.php
index 2c1468b..f88b2a0 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Table.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Table.php
@@ -16,7 +16,7 @@ class Table extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeTable(WorksheetTable $table, $tableRef): string
+    public function writeTable(WorksheetTable $table, int $tableRef): string
     {
         // Create XML writer
         $objWriter = null;
@@ -50,7 +50,6 @@ class Table extends WriterPart
         if ($table->getShowHeaderRow() && $table->getAllowFilter() === true) {
             $objWriter->startElement('autoFilter');
             $objWriter->writeAttribute('ref', $range);
-            $objWriter->endElement();
             foreach (range($rangeStart[0], $rangeEnd[0]) as $offset => $columnIndex) {
                 $column = $table->getColumnByOffset($offset);
 
@@ -64,6 +63,7 @@ class Table extends WriterPart
                     AutoFilter::writeAutoFilterColumn($objWriter, $column, $offset);
                 }
             }
+            $objWriter->endElement(); // autoFilter
         }
 
         // Table Columns
@@ -80,7 +80,7 @@ class Table extends WriterPart
 
             $objWriter->startElement('tableColumn');
             $objWriter->writeAttribute('id', (string) ($offset + 1));
-            $objWriter->writeAttribute('name', $table->getShowHeaderRow() ? $cell->getValue() : 'Column' . ($offset + 1));
+            $objWriter->writeAttribute('name', $table->getShowHeaderRow() ? $cell->getValueString() : ('Column' . ($offset + 1)));
 
             if ($table->getShowTotalsRow()) {
                 if ($column->getTotalsRowLabel()) {
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php
index 9ff29d4..abacd41 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Theme.php
@@ -4,109 +4,17 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
 
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Theme as SpreadsheetTheme;
 
 class Theme extends WriterPart
 {
-    /**
-     * Map of Major fonts to write.
-     *
-     * @var string[]
-     */
-    private static $majorFonts = [
-        'Jpan' => 'MS Pゴシック',
-        'Hang' => '맑은 고딕',
-        'Hans' => '宋体',
-        'Hant' => '新細明體',
-        'Arab' => 'Times New Roman',
-        'Hebr' => 'Times New Roman',
-        'Thai' => 'Tahoma',
-        'Ethi' => 'Nyala',
-        'Beng' => 'Vrinda',
-        'Gujr' => 'Shruti',
-        'Khmr' => 'MoolBoran',
-        'Knda' => 'Tunga',
-        'Guru' => 'Raavi',
-        'Cans' => 'Euphemia',
-        'Cher' => 'Plantagenet Cherokee',
-        'Yiii' => 'Microsoft Yi Baiti',
-        'Tibt' => 'Microsoft Himalaya',
-        'Thaa' => 'MV Boli',
-        'Deva' => 'Mangal',
-        'Telu' => 'Gautami',
-        'Taml' => 'Latha',
-        'Syrc' => 'Estrangelo Edessa',
-        'Orya' => 'Kalinga',
-        'Mlym' => 'Kartika',
-        'Laoo' => 'DokChampa',
-        'Sinh' => 'Iskoola Pota',
-        'Mong' => 'Mongolian Baiti',
-        'Viet' => 'Times New Roman',
-        'Uigh' => 'Microsoft Uighur',
-        'Geor' => 'Sylfaen',
-    ];
-
-    /**
-     * Map of Minor fonts to write.
-     *
-     * @var string[]
-     */
-    private static $minorFonts = [
-        'Jpan' => 'MS Pゴシック',
-        'Hang' => '맑은 고딕',
-        'Hans' => '宋体',
-        'Hant' => '新細明體',
-        'Arab' => 'Arial',
-        'Hebr' => 'Arial',
-        'Thai' => 'Tahoma',
-        'Ethi' => 'Nyala',
-        'Beng' => 'Vrinda',
-        'Gujr' => 'Shruti',
-        'Khmr' => 'DaunPenh',
-        'Knda' => 'Tunga',
-        'Guru' => 'Raavi',
-        'Cans' => 'Euphemia',
-        'Cher' => 'Plantagenet Cherokee',
-        'Yiii' => 'Microsoft Yi Baiti',
-        'Tibt' => 'Microsoft Himalaya',
-        'Thaa' => 'MV Boli',
-        'Deva' => 'Mangal',
-        'Telu' => 'Gautami',
-        'Taml' => 'Latha',
-        'Syrc' => 'Estrangelo Edessa',
-        'Orya' => 'Kalinga',
-        'Mlym' => 'Kartika',
-        'Laoo' => 'DokChampa',
-        'Sinh' => 'Iskoola Pota',
-        'Mong' => 'Mongolian Baiti',
-        'Viet' => 'Arial',
-        'Uigh' => 'Microsoft Uighur',
-        'Geor' => 'Sylfaen',
-    ];
-
-    /**
-     * Map of core colours.
-     *
-     * @var string[]
-     */
-    private static $colourScheme = [
-        'dk2' => '1F497D',
-        'lt2' => 'EEECE1',
-        'accent1' => '4F81BD',
-        'accent2' => 'C0504D',
-        'accent3' => '9BBB59',
-        'accent4' => '8064A2',
-        'accent5' => '4BACC6',
-        'accent6' => 'F79646',
-        'hlink' => '0000FF',
-        'folHlink' => '800080',
-    ];
-
     /**
      * Write theme to XML format.
      *
      * @return string XML Output
      */
-    public function writeTheme()
+    public function writeTheme(Spreadsheet $spreadsheet): string
     {
         // Create XML writer
         $objWriter = null;
@@ -115,6 +23,7 @@ class Theme extends WriterPart
         } else {
             $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
         }
+        $theme = $spreadsheet->getTheme();
 
         // XML header
         $objWriter->startDocument('1.0', 'UTF-8', 'yes');
@@ -129,50 +38,39 @@ class Theme extends WriterPart
 
         // a:clrScheme
         $objWriter->startElement('a:clrScheme');
-        $objWriter->writeAttribute('name', 'Office');
+        $objWriter->writeAttribute('name', $theme->getThemeColorName());
 
-        // a:dk1
-        $objWriter->startElement('a:dk1');
-
-        // a:sysClr
-        $objWriter->startElement('a:sysClr');
-        $objWriter->writeAttribute('val', 'windowText');
-        $objWriter->writeAttribute('lastClr', '000000');
-        $objWriter->endElement();
-
-        $objWriter->endElement();
-
-        // a:lt1
-        $objWriter->startElement('a:lt1');
-
-        // a:sysClr
-        $objWriter->startElement('a:sysClr');
-        $objWriter->writeAttribute('val', 'window');
-        $objWriter->writeAttribute('lastClr', 'FFFFFF');
-        $objWriter->endElement();
-
-        $objWriter->endElement();
-
-        // a:dk2
-        $this->writeColourScheme($objWriter);
+        $this->writeColourScheme($objWriter, $theme);
 
         $objWriter->endElement();
 
         // a:fontScheme
         $objWriter->startElement('a:fontScheme');
-        $objWriter->writeAttribute('name', 'Office');
+        $objWriter->writeAttribute('name', $theme->getThemeFontName());
 
         // a:majorFont
         $objWriter->startElement('a:majorFont');
-        $this->writeFonts($objWriter, 'Cambria', self::$majorFonts);
-        $objWriter->endElement();
+        $this->writeFonts(
+            $objWriter,
+            $theme->getMajorFontLatin(),
+            $theme->getMajorFontEastAsian(),
+            $theme->getMajorFontComplexScript(),
+            $theme->getMajorFontSubstitutions()
+        );
+        $objWriter->endElement(); // a:majorFont
 
         // a:minorFont
         $objWriter->startElement('a:minorFont');
-        $this->writeFonts($objWriter, 'Calibri', self::$minorFonts);
-        $objWriter->endElement();
+        $this->writeFonts(
+            $objWriter,
+            $theme->getMinorFontLatin(),
+            $theme->getMinorFontEastAsian(),
+            $theme->getMinorFontComplexScript(),
+            $theme->getMinorFontSubstitutions()
+        );
+        $objWriter->endElement(); // a:minorFont
 
-        $objWriter->endElement();
+        $objWriter->endElement(); // a:fontScheme
 
         // a:fmtScheme
         $objWriter->startElement('a:fmtScheme');
@@ -786,7 +684,7 @@ class Theme extends WriterPart
      *
      * @param string[] $fontSet
      */
-    private function writeFonts(XMLWriter $objWriter, string $latinFont, array $fontSet): void
+    private function writeFonts(XMLWriter $objWriter, string $latinFont, string $eastAsianFont, string $complexScriptFont, array $fontSet): void
     {
         // a:latin
         $objWriter->startElement('a:latin');
@@ -795,12 +693,12 @@ class Theme extends WriterPart
 
         // a:ea
         $objWriter->startElement('a:ea');
-        $objWriter->writeAttribute('typeface', '');
+        $objWriter->writeAttribute('typeface', $eastAsianFont);
         $objWriter->endElement();
 
         // a:cs
         $objWriter->startElement('a:cs');
-        $objWriter->writeAttribute('typeface', '');
+        $objWriter->writeAttribute('typeface', $complexScriptFont);
         $objWriter->endElement();
 
         foreach ($fontSet as $fontScript => $typeface) {
@@ -814,16 +712,33 @@ class Theme extends WriterPart
     /**
      * Write colour scheme to XML format.
      */
-    private function writeColourScheme(XMLWriter $objWriter): void
+    private function writeColourScheme(XMLWriter $objWriter, SpreadsheetTheme $theme): void
     {
-        foreach (self::$colourScheme as $colourName => $colourValue) {
-            $objWriter->startElement('a:' . $colourName);
+        $themeArray = $theme->getThemeColors();
+        // a:dk1
+        $objWriter->startElement('a:dk1');
+        $objWriter->startElement('a:sysClr');
+        $objWriter->writeAttribute('val', 'windowText');
+        $objWriter->writeAttribute('lastClr', $themeArray['dk1'] ?? '000000');
+        $objWriter->endElement(); // a:sysClr
+        $objWriter->endElement(); // a:dk1
 
-            $objWriter->startElement('a:srgbClr');
-            $objWriter->writeAttribute('val', $colourValue);
-            $objWriter->endElement();
+        // a:lt1
+        $objWriter->startElement('a:lt1');
+        $objWriter->startElement('a:sysClr');
+        $objWriter->writeAttribute('val', 'window');
+        $objWriter->writeAttribute('lastClr', $themeArray['lt1'] ?? 'FFFFFF');
+        $objWriter->endElement(); // a:sysClr
+        $objWriter->endElement(); // a:lt1
 
-            $objWriter->endElement();
+        foreach ($themeArray as $colourName => $colourValue) {
+            if ($colourName !== 'dk1' && $colourName !== 'lt1') {
+                $objWriter->startElement('a:' . $colourName);
+                $objWriter->startElement('a:srgbClr');
+                $objWriter->writeAttribute('val', $colourValue);
+                $objWriter->endElement(); // a:srgbClr
+                $objWriter->endElement(); // a:$colourName
+            }
         }
     }
 }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php
index f2a8678..24a2eeb 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php
@@ -14,11 +14,11 @@ class Workbook extends WriterPart
     /**
      * Write workbook to XML format.
      *
-     * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
+     * @param bool $preCalculateFormulas If true, formulas will be calculated before writing
      *
      * @return string XML Output
      */
-    public function writeWorkbook(Spreadsheet $spreadsheet, $recalcRequired = false)
+    public function writeWorkbook(Spreadsheet $spreadsheet, bool $preCalculateFormulas = false): string
     {
         // Create XML writer
         if ($this->getParentWriter()->getUseDiskCaching()) {
@@ -40,7 +40,7 @@ class Workbook extends WriterPart
         $this->writeFileVersion($objWriter);
 
         // workbookPr
-        $this->writeWorkbookPr($objWriter);
+        $this->writeWorkbookPr($objWriter, $spreadsheet);
 
         // workbookProtection
         $this->writeWorkbookProtection($objWriter, $spreadsheet);
@@ -57,7 +57,7 @@ class Workbook extends WriterPart
         (new DefinedNamesWriter($objWriter, $spreadsheet))->write();
 
         // calcPr
-        $this->writeCalcPr($objWriter, $recalcRequired);
+        $this->writeCalcPr($objWriter, $preCalculateFormulas);
 
         $objWriter->endElement();
 
@@ -81,11 +81,11 @@ class Workbook extends WriterPart
     /**
      * Write WorkbookPr.
      */
-    private function writeWorkbookPr(XMLWriter $objWriter): void
+    private function writeWorkbookPr(XMLWriter $objWriter, Spreadsheet $spreadsheet): void
     {
         $objWriter->startElement('workbookPr');
 
-        if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) {
+        if ($spreadsheet->getExcelCalendar() === Date::CALENDAR_MAC_1904) {
             $objWriter->writeAttribute('date1904', '1');
         }
 
@@ -146,9 +146,9 @@ class Workbook extends WriterPart
     /**
      * Write calcPr.
      *
-     * @param bool $recalcRequired Indicate whether formulas should be recalculated before writing
+     * @param bool $preCalculateFormulas If true, formulas will be calculated before writing
      */
-    private function writeCalcPr(XMLWriter $objWriter, $recalcRequired = true): void
+    private function writeCalcPr(XMLWriter $objWriter, bool $preCalculateFormulas = true): void
     {
         $objWriter->startElement('calcPr');
 
@@ -157,10 +157,10 @@ class Workbook extends WriterPart
         //     because the file has changed
         $objWriter->writeAttribute('calcId', '999999');
         $objWriter->writeAttribute('calcMode', 'auto');
-        //    fullCalcOnLoad isn't needed if we've recalculating for the save
-        $objWriter->writeAttribute('calcCompleted', ($recalcRequired) ? '1' : '0');
-        $objWriter->writeAttribute('fullCalcOnLoad', ($recalcRequired) ? '0' : '1');
-        $objWriter->writeAttribute('forceFullCalc', ($recalcRequired) ? '0' : '1');
+        //    fullCalcOnLoad isn't needed if we will calculate before writing
+        $objWriter->writeAttribute('calcCompleted', ($preCalculateFormulas) ? '1' : '0');
+        $objWriter->writeAttribute('fullCalcOnLoad', ($preCalculateFormulas) ? '0' : '1');
+        $objWriter->writeAttribute('forceFullCalc', ($preCalculateFormulas) ? '0' : '1');
 
         $objWriter->endElement();
     }
@@ -195,7 +195,7 @@ class Workbook extends WriterPart
      * @param int $relId Relationship ID
      * @param string $sheetState Sheet state (visible, hidden, veryHidden)
      */
-    private function writeSheet(XMLWriter $objWriter, $worksheetName, $worksheetId = 1, $relId = 1, $sheetState = 'visible'): void
+    private function writeSheet(XMLWriter $objWriter, string $worksheetName, int $worksheetId = 1, int $relId = 1, string $sheetState = 'visible'): void
     {
         if ($worksheetName != '') {
             // Write sheet
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
index 61b1264..bd6eec3 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
@@ -11,13 +11,25 @@ use PhpOffice\PhpSpreadsheet\Settings;
 use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
 use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalColorScale;
 use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;
 use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormattingRuleExtension;
+use PhpOffice\PhpSpreadsheet\Worksheet\RowDimension;
 use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as PhpspreadsheetWorksheet;
 
 class Worksheet extends WriterPart
 {
+    private string $numberStoredAsText = '';
+
+    private string $formula = '';
+
+    private string $twoDigitTextYear = '';
+
+    private string $evalError = '';
+
+    private bool $explicitStyle0;
+
     /**
      * Write worksheet to XML format.
      *
@@ -26,8 +38,13 @@ class Worksheet extends WriterPart
      *
      * @return string XML Output
      */
-    public function writeWorksheet(PhpspreadsheetWorksheet $worksheet, $stringTable = [], $includeCharts = false)
+    public function writeWorksheet(PhpspreadsheetWorksheet $worksheet, array $stringTable = [], bool $includeCharts = false): string
     {
+        $this->explicitStyle0 = $this->getParentWriter()->getExplicitStyle0();
+        $this->numberStoredAsText = '';
+        $this->formula = '';
+        $this->twoDigitTextYear = '';
+        $this->evalError = '';
         // Create XML writer
         $objWriter = null;
         if ($this->getParentWriter()->getUseDiskCaching()) {
@@ -118,6 +135,12 @@ class Worksheet extends WriterPart
         // AlternateContent
         $this->writeAlternateContent($objWriter, $worksheet);
 
+        // IgnoredErrors
+        $this->writeIgnoredErrors($objWriter);
+
+        // BackgroundImage must come after ignored, before table
+        $this->writeBackgroundImage($objWriter, $worksheet);
+
         // Table
         $this->writeTable($objWriter, $worksheet);
 
@@ -131,6 +154,32 @@ class Worksheet extends WriterPart
         return $objWriter->getData();
     }
 
+    private function writeIgnoredError(XMLWriter $objWriter, bool &$started, string $attr, string $cells): void
+    {
+        if ($cells !== '') {
+            if (!$started) {
+                $objWriter->startElement('ignoredErrors');
+                $started = true;
+            }
+            $objWriter->startElement('ignoredError');
+            $objWriter->writeAttribute('sqref', substr($cells, 1));
+            $objWriter->writeAttribute($attr, '1');
+            $objWriter->endElement();
+        }
+    }
+
+    private function writeIgnoredErrors(XMLWriter $objWriter): void
+    {
+        $started = false;
+        $this->writeIgnoredError($objWriter, $started, 'numberStoredAsText', $this->numberStoredAsText);
+        $this->writeIgnoredError($objWriter, $started, 'formula', $this->formula);
+        $this->writeIgnoredError($objWriter, $started, 'twoDigitTextYear', $this->twoDigitTextYear);
+        $this->writeIgnoredError($objWriter, $started, 'evalError', $this->evalError);
+        if ($started) {
+            $objWriter->endElement();
+        }
+    }
+
     /**
      * Write SheetPr.
      */
@@ -138,7 +187,7 @@ class Worksheet extends WriterPart
     {
         // sheetPr
         $objWriter->startElement('sheetPr');
-        if ($worksheet->getParent()->hasMacros()) {
+        if ($worksheet->getParentOrThrow()->hasMacros()) {
             //if the workbook have macros, we need to have codeName for the sheet
             if (!$worksheet->hasCodeName()) {
                 $worksheet->setCodeName($worksheet->getTitle());
@@ -215,11 +264,21 @@ class Worksheet extends WriterPart
         $objWriter->writeAttribute('workbookViewId', '0');
 
         // Zoom scales
-        if ($worksheet->getSheetView()->getZoomScale() != 100) {
-            $objWriter->writeAttribute('zoomScale', (string) $worksheet->getSheetView()->getZoomScale());
+        $zoomScale = $worksheet->getSheetView()->getZoomScale();
+        if ($zoomScale !== 100 && $zoomScale !== null) {
+            $objWriter->writeAttribute('zoomScale', (string) $zoomScale);
         }
-        if ($worksheet->getSheetView()->getZoomScaleNormal() != 100) {
-            $objWriter->writeAttribute('zoomScaleNormal', (string) $worksheet->getSheetView()->getZoomScaleNormal());
+        $zoomScale = $worksheet->getSheetView()->getZoomScaleNormal();
+        if ($zoomScale !== 100 && $zoomScale !== null) {
+            $objWriter->writeAttribute('zoomScaleNormal', (string) $zoomScale);
+        }
+        $zoomScale = $worksheet->getSheetView()->getZoomScalePageLayoutView();
+        if ($zoomScale !== 100) {
+            $objWriter->writeAttribute('zoomScalePageLayoutView', (string) $zoomScale);
+        }
+        $zoomScale = $worksheet->getSheetView()->getZoomScaleSheetLayoutView();
+        if ($zoomScale !== 100) {
+            $objWriter->writeAttribute('zoomScaleSheetLayoutView', (string) $zoomScale);
         }
 
         // Show zeros (Excel also writes this attribute only if set to false)
@@ -252,55 +311,91 @@ class Worksheet extends WriterPart
         }
 
         $topLeftCell = $worksheet->getTopLeftCell();
+        if (!empty($topLeftCell) && $worksheet->getPaneState() !== PhpspreadsheetWorksheet::PANE_FROZEN && $worksheet->getPaneState() !== PhpspreadsheetWorksheet::PANE_FROZENSPLIT) {
+            $objWriter->writeAttribute('topLeftCell', $topLeftCell);
+        }
         $activeCell = $worksheet->getActiveCell();
         $sqref = $worksheet->getSelectedCells();
 
         // Pane
-        $pane = '';
-        if ($worksheet->getFreezePane()) {
-            [$xSplit, $ySplit] = Coordinate::coordinateFromString($worksheet->getFreezePane());
-            $xSplit = Coordinate::columnIndexFromString($xSplit);
-            --$xSplit;
-            --$ySplit;
-
-            // pane
-            $pane = 'topRight';
+        if ($worksheet->usesPanes()) {
             $objWriter->startElement('pane');
+            $xSplit = $worksheet->getXSplit();
+            $ySplit = $worksheet->getYSplit();
+            $pane = $worksheet->getActivePane();
+            $paneTopLeftCell = $worksheet->getPaneTopLeftCell();
+            $paneState = $worksheet->getPaneState();
+            $normalFreeze = '';
+            if ($paneState === PhpspreadsheetWorksheet::PANE_FROZEN) {
+                if ($ySplit > 0) {
+                    $normalFreeze = ($xSplit <= 0) ? 'bottomLeft' : 'bottomRight';
+                } else {
+                    $normalFreeze = 'topRight';
+                }
+            }
             if ($xSplit > 0) {
                 $objWriter->writeAttribute('xSplit', "$xSplit");
             }
             if ($ySplit > 0) {
-                $objWriter->writeAttribute('ySplit', $ySplit);
-                $pane = ($xSplit > 0) ? 'bottomRight' : 'bottomLeft';
+                $objWriter->writeAttribute('ySplit', "$ySplit");
             }
-            self::writeAttributeNotNull($objWriter, 'topLeftCell', $topLeftCell);
-            $objWriter->writeAttribute('activePane', $pane);
-            $objWriter->writeAttribute('state', 'frozen');
-            $objWriter->endElement();
+            if ($normalFreeze !== '') {
+                $objWriter->writeAttribute('activePane', $normalFreeze);
+            } elseif ($pane !== '') {
+                $objWriter->writeAttribute('activePane', $pane);
+            }
+            if ($paneState !== '') {
+                $objWriter->writeAttribute('state', $paneState);
+            }
+            if ($paneTopLeftCell !== '') {
+                $objWriter->writeAttribute('topLeftCell', $paneTopLeftCell);
+            }
+            $objWriter->endElement(); // pane
 
-            if (($xSplit > 0) && ($ySplit > 0)) {
-                //    Write additional selections if more than two panes (ie both an X and a Y split)
+            if ($normalFreeze !== '') {
                 $objWriter->startElement('selection');
-                $objWriter->writeAttribute('pane', 'topRight');
-                $objWriter->endElement();
-                $objWriter->startElement('selection');
-                $objWriter->writeAttribute('pane', 'bottomLeft');
-                $objWriter->endElement();
+                $objWriter->writeAttribute('pane', $normalFreeze);
+                if ($activeCell !== '') {
+                    $objWriter->writeAttribute('activeCell', $activeCell);
+                }
+                if ($sqref !== '') {
+                    $objWriter->writeAttribute('sqref', $sqref);
+                }
+                $objWriter->endElement(); // selection
+                $sqref = $activeCell = '';
+            } else {
+                foreach ($worksheet->getPanes() as $panex) {
+                    if ($panex !== null) {
+                        $sqref = $activeCell = '';
+                        $objWriter->startElement('selection');
+                        $objWriter->writeAttribute('pane', $panex->getPosition());
+                        $activeCellPane = $panex->getActiveCell();
+                        if ($activeCellPane !== '') {
+                            $objWriter->writeAttribute('activeCell', $activeCellPane);
+                        }
+                        $sqrefPane = $panex->getSqref();
+                        if ($sqrefPane !== '') {
+                            $objWriter->writeAttribute('sqref', $sqrefPane);
+                        }
+                        $objWriter->endElement(); // selection
+                    }
+                }
             }
-        } else {
-            self::writeAttributeNotNull($objWriter, 'topLeftCell', $topLeftCell);
         }
 
         // Selection
         // Only need to write selection element if we have a split pane
         // We cheat a little by over-riding the active cell selection, setting it to the split cell
-        $objWriter->startElement('selection');
-        if ($pane != '') {
-            $objWriter->writeAttribute('pane', $pane);
+        if (!empty($sqref) || !empty($activeCell)) {
+            $objWriter->startElement('selection');
+            if (!empty($activeCell)) {
+                $objWriter->writeAttribute('activeCell', $activeCell);
+            }
+            if (!empty($sqref)) {
+                $objWriter->writeAttribute('sqref', $sqref);
+            }
+            $objWriter->endElement(); // selection
         }
-        $objWriter->writeAttribute('activeCell', $activeCell);
-        $objWriter->writeAttribute('sqref', $sqref);
-        $objWriter->endElement();
 
         $objWriter->endElement();
 
@@ -626,15 +721,15 @@ class Worksheet extends WriterPart
             $minCfvo = $dataBar->getMinimumConditionalFormatValueObject();
             if ($minCfvo) {
                 $objWriter->startElement('cfvo');
-                self::writeAttributeIf($objWriter, $minCfvo->getType(), 'type', (string) $minCfvo->getType());
-                self::writeAttributeIf($objWriter, $minCfvo->getValue(), 'val', (string) $minCfvo->getValue());
+                $objWriter->writeAttribute('type', $minCfvo->getType());
+                self::writeAttributeIf($objWriter, $minCfvo->getValue() !== null, 'val', (string) $minCfvo->getValue());
                 $objWriter->endElement();
             }
             $maxCfvo = $dataBar->getMaximumConditionalFormatValueObject();
             if ($maxCfvo) {
                 $objWriter->startElement('cfvo');
-                self::writeAttributeIf($objWriter, $maxCfvo->getType(), 'type', (string) $maxCfvo->getType());
-                self::writeAttributeIf($objWriter, $maxCfvo->getValue(), 'val', (string) $maxCfvo->getValue());
+                $objWriter->writeAttribute('type', $maxCfvo->getType());
+                self::writeAttributeIf($objWriter, $maxCfvo->getValue() !== null, 'val', (string) $maxCfvo->getValue());
                 $objWriter->endElement();
             }
             if ($dataBar->getColor()) {
@@ -658,6 +753,102 @@ class Worksheet extends WriterPart
         }
     }
 
+    private static function writeColorScaleElements(XMLWriter $objWriter, ?ConditionalColorScale $colorScale): void
+    {
+        if ($colorScale) {
+            $objWriter->startElement('colorScale');
+
+            $minCfvo = $colorScale->getMinimumConditionalFormatValueObject();
+            $minArgb = $colorScale->getMinimumColor()?->getARGB();
+            $useMin = $minCfvo !== null || $minArgb !== null;
+            if ($useMin) {
+                $objWriter->startElement('cfvo');
+                $type = 'min';
+                $value = null;
+                if ($minCfvo !== null) {
+                    $typex = $minCfvo->getType();
+                    if ($typex === 'formula') {
+                        $value = $minCfvo->getCellFormula();
+                        if ($value !== null) {
+                            $type = $typex;
+                        }
+                    } else {
+                        $type = $typex;
+                        $defaults = ['number' => '0', 'percent' => '0', 'percentile' => '10'];
+                        $value = $minCfvo->getValue() ?? $defaults[$type] ?? null;
+                    }
+                }
+                $objWriter->writeAttribute('type', $type);
+                self::writeAttributeIf($objWriter, $value !== null, 'val', (string) $value);
+                $objWriter->endElement();
+            }
+            $midCfvo = $colorScale->getMidpointConditionalFormatValueObject();
+            $midArgb = $colorScale->getMidpointColor()?->getARGB();
+            $useMid = $midCfvo !== null || $midArgb !== null;
+            if ($useMid) {
+                $objWriter->startElement('cfvo');
+                $type = 'percentile';
+                $value = '50';
+                if ($midCfvo !== null) {
+                    $type = $midCfvo->getType();
+                    if ($type === 'formula') {
+                        $value = $midCfvo->getCellFormula();
+                        if ($value === null) {
+                            $type = 'percentile';
+                            $value = '50';
+                        }
+                    } else {
+                        $defaults = ['number' => '0', 'percent' => '50', 'percentile' => '50'];
+                        $value = $midCfvo->getValue() ?? $defaults[$type] ?? null;
+                    }
+                }
+                $objWriter->writeAttribute('type', $type);
+                self::writeAttributeIf($objWriter, $value !== null, 'val', (string) $value);
+                $objWriter->endElement();
+            }
+            $maxCfvo = $colorScale->getMaximumConditionalFormatValueObject();
+            $maxArgb = $colorScale->getMaximumColor()?->getARGB();
+            $useMax = $maxCfvo !== null || $maxArgb !== null;
+            if ($useMax) {
+                $objWriter->startElement('cfvo');
+                $type = 'max';
+                $value = null;
+                if ($maxCfvo !== null) {
+                    $typex = $maxCfvo->getType();
+                    if ($typex === 'formula') {
+                        $value = $maxCfvo->getCellFormula();
+                        if ($value !== null) {
+                            $type = $typex;
+                        }
+                    } else {
+                        $type = $typex;
+                        $defaults = ['number' => '0', 'percent' => '100', 'percentile' => '90'];
+                        $value = $maxCfvo->getValue() ?? $defaults[$type] ?? null;
+                    }
+                }
+                $objWriter->writeAttribute('type', $type);
+                self::writeAttributeIf($objWriter, $value !== null, 'val', (string) $value);
+                $objWriter->endElement();
+            }
+            if ($useMin) {
+                $objWriter->startElement('color');
+                self::writeAttributeIf($objWriter, $minArgb !== null, 'rgb', "$minArgb");
+                $objWriter->endElement();
+            }
+            if ($useMid) {
+                $objWriter->startElement('color');
+                self::writeAttributeIf($objWriter, $midArgb !== null, 'rgb', "$midArgb");
+                $objWriter->endElement();
+            }
+            if ($useMax) {
+                $objWriter->startElement('color');
+                self::writeAttributeIf($objWriter, $maxArgb !== null, 'rgb', "$maxArgb");
+                $objWriter->endElement();
+            }
+            $objWriter->endElement(); // end colorScale
+        }
+    }
+
     /**
      * Write ConditionalFormatting.
      */
@@ -669,7 +860,11 @@ class Worksheet extends WriterPart
         // Loop through styles in the current worksheet
         foreach ($worksheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
             $objWriter->startElement('conditionalFormatting');
-            $objWriter->writeAttribute('sqref', $cellCoordinate);
+            // N.B. In Excel UI, intersection is space and union is comma.
+            // But in Xml, intersection is comma and union is space.
+            // Anyhow, I don't think Excel handles intersection correctly when reading.
+            $outCoordinate = Coordinate::resolveUnionAndIntersection(str_replace('$', '', $cellCoordinate), ' ');
+            $objWriter->writeAttribute('sqref', $outCoordinate);
 
             foreach ($conditionalStyles as $conditional) {
                 // WHY was this again?
@@ -681,7 +876,9 @@ class Worksheet extends WriterPart
                 $objWriter->writeAttribute('type', $conditional->getConditionType());
                 self::writeAttributeIf(
                     $objWriter,
-                    ($conditional->getConditionType() != Conditional::CONDITION_DATABAR),
+                    ($conditional->getConditionType() !== Conditional::CONDITION_COLORSCALE
+                        && $conditional->getConditionType() !== Conditional::CONDITION_DATABAR
+                        && $conditional->getNoFormatSet() === false),
                     'dxfId',
                     (string) $this->getParentWriter()->getStylesConditionalHashTable()->getIndexForHashCode($conditional->getHashCode())
                 );
@@ -714,6 +911,8 @@ class Worksheet extends WriterPart
                     self::writeTextCondElements($objWriter, $conditional, $topLeftCell);
                 } elseif ($conditional->getConditionType() === Conditional::CONDITION_TIMEPERIOD) {
                     self::writeTimePeriodCondElements($objWriter, $conditional, $topLeftCell);
+                } elseif ($conditional->getConditionType() === Conditional::CONDITION_COLORSCALE) {
+                    self::writeColorScaleElements($objWriter, $conditional->getColorScale());
                 } else {
                     self::writeOtherCondElements($objWriter, $conditional, $topLeftCell);
                 }
@@ -834,19 +1033,20 @@ class Worksheet extends WriterPart
      */
     private function writeProtectedRanges(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
     {
-        if (count($worksheet->getProtectedCells()) > 0) {
+        if (count($worksheet->getProtectedCellRanges()) > 0) {
             // protectedRanges
             $objWriter->startElement('protectedRanges');
 
             // Loop protectedRanges
-            foreach ($worksheet->getProtectedCells() as $protectedCell => $passwordHash) {
+            foreach ($worksheet->getProtectedCellRanges() as $protectedCell => $protectedRange) {
                 // protectedRange
                 $objWriter->startElement('protectedRange');
-                $objWriter->writeAttribute('name', 'p' . md5($protectedCell));
+                $objWriter->writeAttribute('name', $protectedRange->getName());
                 $objWriter->writeAttribute('sqref', $protectedCell);
-                if (!empty($passwordHash)) {
-                    $objWriter->writeAttribute('password', $passwordHash);
-                }
+                $passwordHash = $protectedRange->getPassword();
+                $this->writeAttributeIf($objWriter, $passwordHash !== '', 'password', $passwordHash);
+                $securityDescriptor = $protectedRange->getSecurityDescriptor();
+                $this->writeAttributeIf($objWriter, $securityDescriptor !== '', 'securityDescriptor', $securityDescriptor);
                 $objWriter->endElement();
             }
 
@@ -927,6 +1127,9 @@ class Worksheet extends WriterPart
     private function writeTable(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
     {
         $tableCount = $worksheet->getTableCollection()->count();
+        if ($tableCount === 0) {
+            return;
+        }
 
         $objWriter->startElement('tableParts');
         $objWriter->writeAttribute('count', (string) $tableCount);
@@ -940,6 +1143,18 @@ class Worksheet extends WriterPart
         $objWriter->endElement();
     }
 
+    /**
+     * Write Background Image.
+     */
+    private function writeBackgroundImage(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
+    {
+        if ($worksheet->getBackgroundImage() !== '') {
+            $objWriter->startElement('picture');
+            $objWriter->writeAttribute('r:id', 'rIdBg');
+            $objWriter->endElement();
+        }
+    }
+
     /**
      * Write PageSetup.
      */
@@ -969,7 +1184,7 @@ class Worksheet extends WriterPart
         }
         $objWriter->writeAttribute('pageOrder', $worksheet->getPageSetup()->getPageOrder());
 
-        $getUnparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
+        $getUnparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
         if (isset($getUnparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'])) {
             $objWriter->writeAttribute('r:id', $getUnparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId']);
         }
@@ -983,19 +1198,31 @@ class Worksheet extends WriterPart
     private function writeHeaderFooter(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
     {
         // headerFooter
+        $headerFooter = $worksheet->getHeaderFooter();
+        $oddHeader = $headerFooter->getOddHeader();
+        $oddFooter = $headerFooter->getOddFooter();
+        $evenHeader = $headerFooter->getEvenHeader();
+        $evenFooter = $headerFooter->getEvenFooter();
+        $firstHeader = $headerFooter->getFirstHeader();
+        $firstFooter = $headerFooter->getFirstFooter();
+        if ("$oddHeader$oddFooter$evenHeader$evenFooter$firstHeader$firstFooter" === '') {
+            return;
+        }
+
         $objWriter->startElement('headerFooter');
         $objWriter->writeAttribute('differentOddEven', ($worksheet->getHeaderFooter()->getDifferentOddEven() ? 'true' : 'false'));
         $objWriter->writeAttribute('differentFirst', ($worksheet->getHeaderFooter()->getDifferentFirst() ? 'true' : 'false'));
         $objWriter->writeAttribute('scaleWithDoc', ($worksheet->getHeaderFooter()->getScaleWithDocument() ? 'true' : 'false'));
         $objWriter->writeAttribute('alignWithMargins', ($worksheet->getHeaderFooter()->getAlignWithMargins() ? 'true' : 'false'));
 
-        $objWriter->writeElement('oddHeader', $worksheet->getHeaderFooter()->getOddHeader());
-        $objWriter->writeElement('oddFooter', $worksheet->getHeaderFooter()->getOddFooter());
-        $objWriter->writeElement('evenHeader', $worksheet->getHeaderFooter()->getEvenHeader());
-        $objWriter->writeElement('evenFooter', $worksheet->getHeaderFooter()->getEvenFooter());
-        $objWriter->writeElement('firstHeader', $worksheet->getHeaderFooter()->getFirstHeader());
-        $objWriter->writeElement('firstFooter', $worksheet->getHeaderFooter()->getFirstFooter());
-        $objWriter->endElement();
+        self::writeElementIf($objWriter, $oddHeader !== '', 'oddHeader', $oddHeader);
+        self::writeElementIf($objWriter, $oddFooter !== '', 'oddFooter', $oddFooter);
+        self::writeElementIf($objWriter, $evenHeader !== '', 'evenHeader', $evenHeader);
+        self::writeElementIf($objWriter, $evenFooter !== '', 'evenFooter', $evenFooter);
+        self::writeElementIf($objWriter, $firstHeader !== '', 'firstHeader', $firstHeader);
+        self::writeElementIf($objWriter, $firstFooter !== '', 'firstFooter', $firstFooter);
+
+        $objWriter->endElement(); // headerFooter
     }
 
     /**
@@ -1006,12 +1233,11 @@ class Worksheet extends WriterPart
         // Get row and column breaks
         $aRowBreaks = [];
         $aColumnBreaks = [];
-        foreach ($worksheet->getBreaks() as $cell => $breakType) {
-            if ($breakType == PhpspreadsheetWorksheet::BREAK_ROW) {
-                $aRowBreaks[] = $cell;
-            } elseif ($breakType == PhpspreadsheetWorksheet::BREAK_COLUMN) {
-                $aColumnBreaks[] = $cell;
-            }
+        foreach ($worksheet->getRowBreaks() as $cell => $break) {
+            $aRowBreaks[$cell] = $break;
+        }
+        foreach ($worksheet->getColumnBreaks() as $cell => $break) {
+            $aColumnBreaks[$cell] = $break;
         }
 
         // rowBreaks
@@ -1020,12 +1246,16 @@ class Worksheet extends WriterPart
             $objWriter->writeAttribute('count', (string) count($aRowBreaks));
             $objWriter->writeAttribute('manualBreakCount', (string) count($aRowBreaks));
 
-            foreach ($aRowBreaks as $cell) {
+            foreach ($aRowBreaks as $cell => $break) {
                 $coords = Coordinate::coordinateFromString($cell);
 
                 $objWriter->startElement('brk');
                 $objWriter->writeAttribute('id', $coords[1]);
                 $objWriter->writeAttribute('man', '1');
+                $rowBreakMax = $break->getMaxColOrRow();
+                if ($rowBreakMax >= 0) {
+                    $objWriter->writeAttribute('max', "$rowBreakMax");
+                }
                 $objWriter->endElement();
             }
 
@@ -1038,11 +1268,11 @@ class Worksheet extends WriterPart
             $objWriter->writeAttribute('count', (string) count($aColumnBreaks));
             $objWriter->writeAttribute('manualBreakCount', (string) count($aColumnBreaks));
 
-            foreach ($aColumnBreaks as $cell) {
-                $coords = Coordinate::coordinateFromString($cell);
+            foreach ($aColumnBreaks as $cell => $break) {
+                $coords = Coordinate::indexesFromString($cell);
 
                 $objWriter->startElement('brk');
-                $objWriter->writeAttribute('id', (string) (Coordinate::columnIndexFromString($coords[0]) - 1));
+                $objWriter->writeAttribute('id', (string) ((int) $coords[0] - 1));
                 $objWriter->writeAttribute('man', '1');
                 $objWriter->endElement();
             }
@@ -1082,11 +1312,12 @@ class Worksheet extends WriterPart
         }
 
         $currentRow = 0;
+        $emptyDimension = new RowDimension();
         while ($currentRow++ < $highestRow) {
             $isRowSet = isset($cellsByRow[$currentRow]);
             if ($isRowSet || $worksheet->rowDimensionExists($currentRow)) {
                 // Get row dimension
-                $rowDimension = $worksheet->getRowDimension($currentRow);
+                $rowDimension = $worksheet->rowDimensionExists($currentRow) ? $worksheet->getRowDimension($currentRow) : $emptyDimension;
 
                 // Write current row?
                 $writeCurrentRow = $isRowSet || $rowDimension->getRowHeight() >= 0 || $rowDimension->getVisible() === false || $rowDimension->getCollapsed() === true || $rowDimension->getOutlineLevel() > 0 || $rowDimension->getXfIndex() !== null;
@@ -1131,7 +1362,20 @@ class Worksheet extends WriterPart
                         array_pop($columnsInRow);
                         foreach ($columnsInRow as $column) {
                             // Write cell
-                            $this->writeCell($objWriter, $worksheet, "{$column}{$currentRow}", $aFlippedStringTable);
+                            $coord = "$column$currentRow";
+                            if ($worksheet->getCell($coord)->getIgnoredErrors()->getNumberStoredAsText()) {
+                                $this->numberStoredAsText .= " $coord";
+                            }
+                            if ($worksheet->getCell($coord)->getIgnoredErrors()->getFormula()) {
+                                $this->formula .= " $coord";
+                            }
+                            if ($worksheet->getCell($coord)->getIgnoredErrors()->getTwoDigitTextYear()) {
+                                $this->twoDigitTextYear .= " $coord";
+                            }
+                            if ($worksheet->getCell($coord)->getIgnoredErrors()->getEvalError()) {
+                                $this->evalError .= " $coord";
+                            }
+                            $this->writeCell($objWriter, $worksheet, $coord, $aFlippedStringTable);
                         }
                     }
 
@@ -1144,10 +1388,7 @@ class Worksheet extends WriterPart
         $objWriter->endElement();
     }
 
-    /**
-     * @param RichText|string $cellValue
-     */
-    private function writeCellInlineStr(XMLWriter $objWriter, string $mappedType, $cellValue): void
+    private function writeCellInlineStr(XMLWriter $objWriter, string $mappedType, RichText|string $cellValue): void
     {
         $objWriter->writeAttribute('t', $mappedType);
         if (!$cellValue instanceof RichText) {
@@ -1165,10 +1406,9 @@ class Worksheet extends WriterPart
     }
 
     /**
-     * @param RichText|string $cellValue
      * @param string[] $flippedStringTable
      */
-    private function writeCellString(XMLWriter $objWriter, string $mappedType, $cellValue, array $flippedStringTable): void
+    private function writeCellString(XMLWriter $objWriter, string $mappedType, RichText|string $cellValue, array $flippedStringTable): void
     {
         $objWriter->writeAttribute('t', $mappedType);
         if (!$cellValue instanceof RichText) {
@@ -1178,16 +1418,13 @@ class Worksheet extends WriterPart
         }
     }
 
-    /**
-     * @param float|int $cellValue
-     */
-    private function writeCellNumeric(XMLWriter $objWriter, $cellValue): void
+    private function writeCellNumeric(XMLWriter $objWriter, float|int $cellValue): void
     {
         //force a decimal to be written if the type is float
         if (is_float($cellValue)) {
             // force point as decimal separator in case current locale uses comma
             $cellValue = str_replace(',', '.', (string) $cellValue);
-            if (strpos($cellValue, '.') === false) {
+            if (!str_contains($cellValue, '.')) {
                 $cellValue = $cellValue . '.0';
             }
         }
@@ -1203,7 +1440,7 @@ class Worksheet extends WriterPart
     private function writeCellError(XMLWriter $objWriter, string $mappedType, string $cellValue, string $formulaerr = '#NULL!'): void
     {
         $objWriter->writeAttribute('t', $mappedType);
-        $cellIsFormula = substr($cellValue, 0, 1) === '=';
+        $cellIsFormula = str_starts_with($cellValue, '=');
         self::writeElementIf($objWriter, $cellIsFormula, 'f', FunctionPrefix::addFunctionPrefixStripEquals($cellValue));
         $objWriter->writeElement('v', $cellIsFormula ? $formulaerr : $cellValue);
     }
@@ -1211,6 +1448,7 @@ class Worksheet extends WriterPart
     private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell $cell): void
     {
         $calculatedValue = $this->getParentWriter()->getPreCalculateFormulas() ? $cell->getCalculatedValue() : $cellValue;
+        $calculatedValueString = $this->getParentWriter()->getPreCalculateFormulas() ? $cell->getCalculatedValueString() : $cellValue;
         if (is_string($calculatedValue)) {
             if (ErrorValue::isError($calculatedValue)) {
                 $this->writeCellError($objWriter, 'e', $cellValue, $calculatedValue);
@@ -1219,28 +1457,32 @@ class Worksheet extends WriterPart
             }
             $objWriter->writeAttribute('t', 'str');
             $calculatedValue = StringHelper::controlCharacterPHP2OOXML($calculatedValue);
+            $calculatedValueString = $calculatedValue;
         } elseif (is_bool($calculatedValue)) {
             $objWriter->writeAttribute('t', 'b');
             $calculatedValue = (int) $calculatedValue;
+            $calculatedValueString = (string) $calculatedValue;
         }
 
         $attributes = $cell->getFormulaAttributes();
-        if (($attributes['t'] ?? null) === 'array') {
+        if (is_array($attributes) && ($attributes['t'] ?? null) === 'array') {
             $objWriter->startElement('f');
             $objWriter->writeAttribute('t', 'array');
             $objWriter->writeAttribute('ref', $cell->getCoordinate());
             $objWriter->writeAttribute('aca', '1');
             $objWriter->writeAttribute('ca', '1');
-            $objWriter->text(substr($cellValue, 1));
+            $objWriter->text(FunctionPrefix::addFunctionPrefixStripEquals($cellValue));
             $objWriter->endElement();
         } else {
             $objWriter->writeElement('f', FunctionPrefix::addFunctionPrefixStripEquals($cellValue));
             self::writeElementIf(
                 $objWriter,
-                $this->getParentWriter()->getOffice2003Compatibility() === false,
+                $this->getParentWriter()->getOffice2003Compatibility() === false
+                && $this->getParentWriter()->getPreCalculateFormulas()
+                && $calculatedValue !== null,
                 'v',
-                ($this->getParentWriter()->getPreCalculateFormulas() && !is_array($calculatedValue) && substr($calculatedValue ?? '', 0, 1) !== '#')
-                    ? StringHelper::formatNumber($calculatedValue) : '0'
+                (!is_array($calculatedValue) && !str_starts_with($calculatedValueString, '#'))
+                    ? StringHelper::formatNumber($calculatedValueString) : '0'
             );
         }
     }
@@ -1255,43 +1497,55 @@ class Worksheet extends WriterPart
     {
         // Cell
         $pCell = $worksheet->getCell($cellAddress);
+        $xfi = $pCell->getXfIndex();
+        $cellValue = $pCell->getValue();
+        $cellValueString = $pCell->getValueString();
+        $writeValue = $cellValue !== '' && $cellValue !== null;
+        if (empty($xfi) && !$writeValue) {
+            return;
+        }
         $objWriter->startElement('c');
         $objWriter->writeAttribute('r', $cellAddress);
 
         // Sheet styles
-        $xfi = $pCell->getXfIndex();
-        self::writeAttributeIf($objWriter, (bool) $xfi, 's', "$xfi");
+        if ($xfi) {
+            $objWriter->writeAttribute('s', "$xfi");
+        } elseif ($this->explicitStyle0) {
+            $objWriter->writeAttribute('s', '0');
+        }
 
         // If cell value is supplied, write cell value
-        $cellValue = $pCell->getValue();
-        if (is_object($cellValue) || $cellValue !== '') {
+        if ($writeValue) {
             // Map type
             $mappedType = $pCell->getDataType();
 
             // Write data depending on its type
             switch (strtolower($mappedType)) {
                 case 'inlinestr':    // Inline string
-                    $this->writeCellInlineStr($objWriter, $mappedType, $cellValue);
+                    /** @var RichText|string */
+                    $richText = $cellValue;
+                    $this->writeCellInlineStr($objWriter, $mappedType, $richText);
 
                     break;
                 case 's':            // String
-                    $this->writeCellString($objWriter, $mappedType, $cellValue, $flippedStringTable);
+                    $this->writeCellString($objWriter, $mappedType, ($cellValue instanceof RichText) ? $cellValue : $cellValueString, $flippedStringTable);
 
                     break;
                 case 'f':            // Formula
-                    $this->writeCellFormula($objWriter, $cellValue, $pCell);
+                    $this->writeCellFormula($objWriter, $cellValueString, $pCell);
 
                     break;
                 case 'n':            // Numeric
-                    $this->writeCellNumeric($objWriter, $cellValue);
+                    $cellValueNumeric = is_numeric($cellValue) ? ($cellValue + 0) : 0;
+                    $this->writeCellNumeric($objWriter, $cellValueNumeric);
 
                     break;
                 case 'b':            // Boolean
-                    $this->writeCellBoolean($objWriter, $mappedType, $cellValue);
+                    $this->writeCellBoolean($objWriter, $mappedType, (bool) $cellValue);
 
                     break;
                 case 'e':            // Error
-                    $this->writeCellError($objWriter, $mappedType, $cellValue);
+                    $this->writeCellError($objWriter, $mappedType, $cellValueString);
             }
         }
 
@@ -1303,9 +1557,9 @@ class Worksheet extends WriterPart
      *
      * @param bool $includeCharts Flag indicating if we should include drawing details for charts
      */
-    private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet, $includeCharts = false): void
+    private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet, bool $includeCharts = false): void
     {
-        $unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
+        $unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
         $hasUnparsedDrawing = isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['drawingOriginalIds']);
         $chartCount = ($includeCharts) ? $worksheet->getChartCollection()->count() : 0;
         if ($chartCount == 0 && $worksheet->getDrawingCollection()->count() == 0 && !$hasUnparsedDrawing) {
@@ -1333,7 +1587,8 @@ class Worksheet extends WriterPart
     private function writeLegacyDrawing(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
     {
         // If sheet contains comments, add the relationships
-        if (count($worksheet->getComments()) > 0) {
+        $unparsedLoadedData = $worksheet->getParentOrThrow()->getUnparsedLoadedData();
+        if (count($worksheet->getComments()) > 0 || isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'])) {
             $objWriter->startElement('legacyDrawing');
             $objWriter->writeAttribute('r:id', 'rId_comments_vml1');
             $objWriter->endElement();
@@ -1355,11 +1610,11 @@ class Worksheet extends WriterPart
 
     private function writeAlternateContent(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
     {
-        if (empty($worksheet->getParent()->getUnparsedLoadedData()['sheets'][$worksheet->getCodeName()]['AlternateContents'])) {
+        if (empty($worksheet->getParentOrThrow()->getUnparsedLoadedData()['sheets'][$worksheet->getCodeName()]['AlternateContents'])) {
             return;
         }
 
-        foreach ($worksheet->getParent()->getUnparsedLoadedData()['sheets'][$worksheet->getCodeName()]['AlternateContents'] as $alternateContent) {
+        foreach ($worksheet->getParentOrThrow()->getUnparsedLoadedData()['sheets'][$worksheet->getCodeName()]['AlternateContents'] as $alternateContent) {
             $objWriter->writeRaw($alternateContent);
         }
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
index 0bfb356..7715a29 100644
--- a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/WriterPart.php
@@ -8,17 +8,13 @@ abstract class WriterPart
 {
     /**
      * Parent Xlsx object.
-     *
-     * @var Xlsx
      */
-    private $parentWriter;
+    private Xlsx $parentWriter;
 
     /**
      * Get parent Xlsx object.
-     *
-     * @return Xlsx
      */
-    public function getParentWriter()
+    public function getParentWriter(): Xlsx
     {
         return $this->parentWriter;
     }
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream0.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream0.php
new file mode 100644
index 0000000..886731c
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream0.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use ZipStream\Option\Archive;
+use ZipStream\ZipStream;
+
+class ZipStream0
+{
+    /**
+     * @param resource $fileHandle
+     */
+    public static function newZipStream($fileHandle): ZipStream
+    {
+        return class_exists(Archive::class) ? ZipStream2::newZipStream($fileHandle) : ZipStream3::newZipStream($fileHandle);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream2.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream2.php
new file mode 100644
index 0000000..ed21a7e
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream2.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use ZipStream\Option\Archive;
+use ZipStream\ZipStream;
+
+class ZipStream2
+{
+    /**
+     * @param resource $fileHandle
+     */
+    public static function newZipStream($fileHandle): ZipStream
+    {
+        $options = new Archive();
+        $options->setEnableZip64(false);
+        $options->setOutputStream($fileHandle);
+
+        return new ZipStream(null, $options);
+    }
+}
diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream3.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream3.php
new file mode 100644
index 0000000..96e13ec
--- /dev/null
+++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/ZipStream3.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Writer;
+
+use ZipStream\ZipStream;
+
+class ZipStream3
+{
+    /**
+     * @param resource $fileHandle
+     */
+    public static function newZipStream($fileHandle): ZipStream
+    {
+        return new ZipStream(
+            enableZip64: false,
+            outputStream: $fileHandle,
+            sendHttpHeaders: false,
+            defaultEnableZeroHeader: false,
+        );
+    }
+}
diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/phpstan/phpdoc-parser/LICENSE
similarity index 87%
rename from vendor/symfony/polyfill-mbstring/LICENSE
rename to vendor/phpstan/phpdoc-parser/LICENSE
index 4cd8bdd..98a854e 100644
--- a/vendor/symfony/polyfill-mbstring/LICENSE
+++ b/vendor/phpstan/phpdoc-parser/LICENSE
@@ -1,11 +1,13 @@
-Copyright (c) 2015-2019 Fabien Potencier
+MIT License
+
+Copyright (c) 2016 Ondřej Mirtes
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.
@@ -15,5 +17,5 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/vendor/phpstan/phpdoc-parser/README.md b/vendor/phpstan/phpdoc-parser/README.md
new file mode 100644
index 0000000..706b2a3
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/README.md
@@ -0,0 +1,121 @@
+<h1 align="center">PHPDoc Parser for PHPStan</h1>
+
+<p align="center">
+	<a href="https://github.com/phpstan/phpdoc-parser/actions"><img src="https://github.com/phpstan/phpdoc-parser/workflows/Build/badge.svg" alt="Build Status"></a>
+	<a href="https://packagist.org/packages/phpstan/phpdoc-parser"><img src="https://poser.pugx.org/phpstan/phpdoc-parser/v/stable" alt="Latest Stable Version"></a>
+	<a href="https://choosealicense.com/licenses/mit/"><img src="https://poser.pugx.org/phpstan/phpstan/license" alt="License"></a>
+	<a href="https://phpstan.org/"><img src="https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat" alt="PHPStan Enabled"></a>
+</p>
+
+This library `phpstan/phpdoc-parser` represents PHPDocs with an AST (Abstract Syntax Tree). It supports parsing and modifying PHPDocs.
+
+For the complete list of supported PHPDoc features check out PHPStan documentation. PHPStan is the main (but not the only) user of this library.
+
+* [PHPDoc Basics](https://phpstan.org/writing-php-code/phpdocs-basics) (list of PHPDoc tags)
+* [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) (list of PHPDoc types)
+* [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/1.23.x/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc.
+
+This parser also supports parsing [Doctrine Annotations](https://github.com/doctrine/annotations). The AST nodes live in the [PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine namespace](https://phpstan.github.io/phpdoc-parser/1.23.x/namespace-PHPStan.PhpDocParser.Ast.PhpDoc.Doctrine.html). The support needs to be turned on by setting `bool $parseDoctrineAnnotations` to `true` in `Lexer` and `PhpDocParser` class constructors.
+
+## Installation
+
+```
+composer require phpstan/phpdoc-parser
+```
+
+## Basic usage
+
+```php
+<?php
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\Parser\ConstExprParser;
+use PHPStan\PhpDocParser\Parser\PhpDocParser;
+use PHPStan\PhpDocParser\Parser\TokenIterator;
+use PHPStan\PhpDocParser\Parser\TypeParser;
+
+// basic setup
+
+$lexer = new Lexer();
+$constExprParser = new ConstExprParser();
+$typeParser = new TypeParser($constExprParser);
+$phpDocParser = new PhpDocParser($typeParser, $constExprParser);
+
+// parsing and reading a PHPDoc string
+
+$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
+$phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode
+$paramTags = $phpDocNode->getParamTagValues(); // ParamTagValueNode[]
+echo $paramTags[0]->parameterName; // '$a'
+echo $paramTags[0]->type; // IdentifierTypeNode - 'Lorem'
+```
+
+### Format-preserving printer
+
+This component can be used to modify the AST
+and print it again as close as possible to the original.
+
+It's heavily inspired by format-preserving printer component in [nikic/PHP-Parser](https://github.com/nikic/PHP-Parser).
+
+```php
+<?php
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+use PHPStan\PhpDocParser\Ast\NodeTraverser;
+use PHPStan\PhpDocParser\Ast\NodeVisitor\CloningVisitor;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\Parser\ConstExprParser;
+use PHPStan\PhpDocParser\Parser\PhpDocParser;
+use PHPStan\PhpDocParser\Parser\TokenIterator;
+use PHPStan\PhpDocParser\Parser\TypeParser;
+use PHPStan\PhpDocParser\Printer\Printer;
+
+// basic setup with enabled required lexer attributes
+
+$usedAttributes = ['lines' => true, 'indexes' => true];
+
+$lexer = new Lexer();
+$constExprParser = new ConstExprParser(true, true, $usedAttributes);
+$typeParser = new TypeParser($constExprParser, true, $usedAttributes);
+$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes);
+
+$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */'));
+$phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode
+
+$cloningTraverser = new NodeTraverser([new CloningVisitor()]);
+
+/** @var PhpDocNode $newPhpDocNode */
+[$newPhpDocNode] = $cloningTraverser->traverse([$phpDocNode]);
+
+// change something in $newPhpDocNode
+$newPhpDocNode->getParamTagValues()[0]->type = new IdentifierTypeNode('Ipsum');
+
+// print changed PHPDoc
+$printer = new Printer();
+$newPhpDoc = $printer->printFormatPreserving($newPhpDocNode, $phpDocNode, $tokens);
+echo $newPhpDoc; // '/** @param Ipsum $a */'
+```
+
+## Code of Conduct
+
+This project adheres to a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project and its community, you are expected to uphold this code.
+
+## Building
+
+Initially you need to run `composer install`, or `composer update` in case you aren't working in a folder which was built before.
+
+Afterwards you can either run the whole build including linting and coding standards using
+
+    make
+
+or run only tests using
+
+    make tests
diff --git a/vendor/phpstan/phpdoc-parser/composer.json b/vendor/phpstan/phpdoc-parser/composer.json
new file mode 100644
index 0000000..f1c648e
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/composer.json
@@ -0,0 +1,44 @@
+{
+	"name": "phpstan/phpdoc-parser",
+	"description": "PHPDoc parser with support for nullable, intersection and generic types",
+	"license": "MIT",
+	"require": {
+		"php": "^7.2 || ^8.0"
+	},
+	"require-dev": {
+		"doctrine/annotations": "^2.0",
+		"nikic/php-parser": "^4.15",
+		"php-parallel-lint/php-parallel-lint": "^1.2",
+		"phpstan/extension-installer": "^1.0",
+		"phpstan/phpstan": "^1.5",
+		"phpstan/phpstan-phpunit": "^1.1",
+		"phpstan/phpstan-strict-rules": "^1.0",
+		"phpunit/phpunit": "^9.5",
+		"symfony/process": "^5.2"
+	},
+	"config": {
+		"platform": {
+			"php": "7.4.6"
+		},
+		"sort-packages": true,
+		"allow-plugins": {
+			"phpstan/extension-installer": true
+		}
+	},
+	"autoload": {
+		"psr-4": {
+			"PHPStan\\PhpDocParser\\": [
+				"src/"
+			]
+		}
+	},
+	"autoload-dev": {
+		"psr-4": {
+			"PHPStan\\PhpDocParser\\": [
+				"tests/PHPStan"
+			]
+		}
+	},
+	"minimum-stability": "dev",
+	"prefer-stable": true
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php b/vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php
new file mode 100644
index 0000000..2b9c10e
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast;
+
+/**
+ * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
+ *
+ * Copyright (c) 2011, Nikita Popov
+ * All rights reserved.
+ */
+abstract class AbstractNodeVisitor implements NodeVisitor // phpcs:ignore SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix
+{
+
+	public function beforeTraverse(array $nodes): ?array
+	{
+		return null;
+	}
+
+	public function enterNode(Node $node)
+	{
+		return null;
+	}
+
+	public function leaveNode(Node $node)
+	{
+		return null;
+	}
+
+	public function afterTraverse(array $nodes): ?array
+	{
+		return null;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Attribute.php b/vendor/phpstan/phpdoc-parser/src/Ast/Attribute.php
new file mode 100644
index 0000000..cd3a0a2
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Attribute.php
@@ -0,0 +1,16 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast;
+
+final class Attribute
+{
+
+	public const START_LINE = 'startLine';
+	public const END_LINE = 'endLine';
+
+	public const START_INDEX = 'startIndex';
+	public const END_INDEX = 'endIndex';
+
+	public const ORIGINAL_NODE = 'originalNode';
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php
new file mode 100644
index 0000000..ef14452
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function sprintf;
+
+class ConstExprArrayItemNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	/** @var ConstExprNode|null */
+	public $key;
+
+	/** @var ConstExprNode */
+	public $value;
+
+	public function __construct(?ConstExprNode $key, ConstExprNode $value)
+	{
+		$this->key = $key;
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->key !== null) {
+			return sprintf('%s => %s', $this->key, $this->value);
+
+		}
+
+		return (string) $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php
new file mode 100644
index 0000000..1f9def3
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php
@@ -0,0 +1,30 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function implode;
+
+class ConstExprArrayNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	/** @var ConstExprArrayItemNode[] */
+	public $items;
+
+	/**
+	 * @param ConstExprArrayItemNode[] $items
+	 */
+	public function __construct(array $items)
+	{
+		$this->items = $items;
+	}
+
+
+	public function __toString(): string
+	{
+		return '[' . implode(', ', $this->items) . ']';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php
new file mode 100644
index 0000000..e681127
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php
@@ -0,0 +1,17 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstExprFalseNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	public function __toString(): string
+	{
+		return 'false';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFloatNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFloatNode.php
new file mode 100644
index 0000000..a4192fb
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFloatNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstExprFloatNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $value;
+
+	public function __construct(string $value)
+	{
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		return $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php
new file mode 100644
index 0000000..5339bb5
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstExprIntegerNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $value;
+
+	public function __construct(string $value)
+	{
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		return $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php
new file mode 100644
index 0000000..1859f03
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php
@@ -0,0 +1,10 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\Node;
+
+interface ConstExprNode extends Node
+{
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNullNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNullNode.php
new file mode 100644
index 0000000..b6e2277
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNullNode.php
@@ -0,0 +1,17 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstExprNullNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	public function __toString(): string
+	{
+		return 'null';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprStringNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprStringNode.php
new file mode 100644
index 0000000..fa44c26
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprStringNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstExprStringNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $value;
+
+	public function __construct(string $value)
+	{
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		return $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.php
new file mode 100644
index 0000000..ec98032
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.php
@@ -0,0 +1,17 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstExprTrueNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	public function __toString(): string
+	{
+		return 'true';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstFetchNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstFetchNode.php
new file mode 100644
index 0000000..3f20363
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstFetchNode.php
@@ -0,0 +1,35 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstFetchNode implements ConstExprNode
+{
+
+	use NodeAttributes;
+
+	/** @var string class name for class constants or empty string for non-class constants */
+	public $className;
+
+	/** @var string */
+	public $name;
+
+	public function __construct(string $className, string $name)
+	{
+		$this->className = $className;
+		$this->name = $name;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->className === '') {
+			return $this->name;
+
+		}
+
+		return "{$this->className}::{$this->name}";
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/DoctrineConstExprStringNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
new file mode 100644
index 0000000..a503937
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/DoctrineConstExprStringNode.php
@@ -0,0 +1,42 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function sprintf;
+use function str_replace;
+use function strlen;
+use function substr;
+
+class DoctrineConstExprStringNode extends ConstExprStringNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $value;
+
+	public function __construct(string $value)
+	{
+		parent::__construct($value);
+		$this->value = $value;
+	}
+
+	public function __toString(): string
+	{
+		return self::escape($this->value);
+	}
+
+	public static function unescape(string $value): string
+	{
+		// from https://github.com/doctrine/annotations/blob/a9ec7af212302a75d1f92fa65d3abfbd16245a2a/lib/Doctrine/Common/Annotations/DocLexer.php#L103-L107
+		return str_replace('""', '"', substr($value, 1, strlen($value) - 2));
+	}
+
+	private static function escape(string $value): string
+	{
+		// from https://github.com/phpstan/phpdoc-parser/issues/205#issuecomment-1662323656
+		return sprintf('"%s"', str_replace('"', '""', $value));
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php
new file mode 100644
index 0000000..f2792b1
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php
@@ -0,0 +1,78 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\ConstExpr;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function addcslashes;
+use function assert;
+use function dechex;
+use function ord;
+use function preg_replace_callback;
+use function sprintf;
+use function str_pad;
+use function strlen;
+use const STR_PAD_LEFT;
+
+class QuoteAwareConstExprStringNode extends ConstExprStringNode implements ConstExprNode
+{
+
+	public const SINGLE_QUOTED = 1;
+	public const DOUBLE_QUOTED = 2;
+
+	use NodeAttributes;
+
+	/** @var self::SINGLE_QUOTED|self::DOUBLE_QUOTED */
+	public $quoteType;
+
+	/**
+	 * @param self::SINGLE_QUOTED|self::DOUBLE_QUOTED $quoteType
+	 */
+	public function __construct(string $value, int $quoteType)
+	{
+		parent::__construct($value);
+		$this->quoteType = $quoteType;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->quoteType === self::SINGLE_QUOTED) {
+			// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1007
+			return sprintf("'%s'", addcslashes($this->value, '\'\\'));
+		}
+
+		// from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040
+		return sprintf('"%s"', $this->escapeDoubleQuotedString());
+	}
+
+	private function escapeDoubleQuotedString(): string
+	{
+		$quote = '"';
+		$escaped = addcslashes($this->value, "\n\r\t\f\v$" . $quote . '\\');
+
+		// Escape control characters and non-UTF-8 characters.
+		// Regex based on https://stackoverflow.com/a/11709412/385378.
+		$regex = '/(
+              [\x00-\x08\x0E-\x1F] # Control characters
+            | [\xC0-\xC1] # Invalid UTF-8 Bytes
+            | [\xF5-\xFF] # Invalid UTF-8 Bytes
+            | \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point
+            | \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point
+            | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
+            | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
+            | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
+            | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
+            | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
+            | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
+            | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
+            | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
+        )/x';
+		return preg_replace_callback($regex, static function ($matches) {
+			assert(strlen($matches[0]) === 1);
+			$hex = dechex(ord($matches[0]));
+
+			return '\\x' . str_pad($hex, 2, '0', STR_PAD_LEFT);
+		}, $escaped);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Node.php b/vendor/phpstan/phpdoc-parser/src/Ast/Node.php
new file mode 100644
index 0000000..51a1a19
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Node.php
@@ -0,0 +1,22 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast;
+
+interface Node
+{
+
+	public function __toString(): string;
+
+	/**
+	 * @param mixed $value
+	 */
+	public function setAttribute(string $key, $value): void;
+
+	public function hasAttribute(string $key): bool;
+
+	/**
+	 * @return mixed
+	 */
+	public function getAttribute(string $key);
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/NodeAttributes.php b/vendor/phpstan/phpdoc-parser/src/Ast/NodeAttributes.php
new file mode 100644
index 0000000..3da1779
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/NodeAttributes.php
@@ -0,0 +1,38 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast;
+
+use function array_key_exists;
+
+trait NodeAttributes
+{
+
+	/** @var array<string, mixed> */
+	private $attributes = [];
+
+	/**
+	 * @param mixed $value
+	 */
+	public function setAttribute(string $key, $value): void
+	{
+		$this->attributes[$key] = $value;
+	}
+
+	public function hasAttribute(string $key): bool
+	{
+		return array_key_exists($key, $this->attributes);
+	}
+
+	/**
+	 * @return mixed
+	 */
+	public function getAttribute(string $key)
+	{
+		if ($this->hasAttribute($key)) {
+			return $this->attributes[$key];
+		}
+
+		return null;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/NodeTraverser.php b/vendor/phpstan/phpdoc-parser/src/Ast/NodeTraverser.php
new file mode 100644
index 0000000..63b25c3
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/NodeTraverser.php
@@ -0,0 +1,312 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast;
+
+use LogicException;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function array_keys;
+use function array_pop;
+use function array_splice;
+use function count;
+use function get_class;
+use function get_object_vars;
+use function gettype;
+use function is_array;
+use function sprintf;
+
+/**
+ * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
+ *
+ * Copyright (c) 2011, Nikita Popov
+ * All rights reserved.
+ */
+final class NodeTraverser
+{
+
+	/**
+	 * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes
+	 * of the current node will not be traversed for any visitors.
+	 *
+	 * For subsequent visitors enterNode() will still be called on the current
+	 * node and leaveNode() will also be invoked for the current node.
+	 */
+	public const DONT_TRAVERSE_CHILDREN = 1;
+
+	/**
+	 * If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns
+	 * STOP_TRAVERSAL, traversal is aborted.
+	 *
+	 * The afterTraverse() method will still be invoked.
+	 */
+	public const STOP_TRAVERSAL = 2;
+
+	/**
+	 * If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs
+	 * in an array, it will be removed from the array.
+	 *
+	 * For subsequent visitors leaveNode() will still be invoked for the
+	 * removed node.
+	 */
+	public const REMOVE_NODE = 3;
+
+	/**
+	 * If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes
+	 * of the current node will not be traversed for any visitors.
+	 *
+	 * For subsequent visitors enterNode() will not be called as well.
+	 * leaveNode() will be invoked for visitors that has enterNode() method invoked.
+	 */
+	public const DONT_TRAVERSE_CURRENT_AND_CHILDREN = 4;
+
+	/** @var list<NodeVisitor> Visitors */
+	private $visitors = [];
+
+	/** @var bool Whether traversal should be stopped */
+	private $stopTraversal;
+
+	/**
+	 * @param list<NodeVisitor> $visitors
+	 */
+	public function __construct(array $visitors)
+	{
+		$this->visitors = $visitors;
+	}
+
+	/**
+	 * Traverses an array of nodes using the registered visitors.
+	 *
+	 * @param Node[] $nodes Array of nodes
+	 *
+	 * @return Node[] Traversed array of nodes
+	 */
+	public function traverse(array $nodes): array
+	{
+		$this->stopTraversal = false;
+
+		foreach ($this->visitors as $visitor) {
+			$return = $visitor->beforeTraverse($nodes);
+			if ($return === null) {
+				continue;
+			}
+
+			$nodes = $return;
+		}
+
+		$nodes = $this->traverseArray($nodes);
+
+		foreach ($this->visitors as $visitor) {
+			$return = $visitor->afterTraverse($nodes);
+			if ($return === null) {
+				continue;
+			}
+
+			$nodes = $return;
+		}
+
+		return $nodes;
+	}
+
+	/**
+	 * Recursively traverse a node.
+	 *
+	 * @param Node $node Node to traverse.
+	 *
+	 * @return Node Result of traversal (may be original node or new one)
+	 */
+	private function traverseNode(Node $node): Node
+	{
+		$subNodeNames = array_keys(get_object_vars($node));
+		foreach ($subNodeNames as $name) {
+			$subNode =& $node->$name;
+
+			if (is_array($subNode)) {
+				$subNode = $this->traverseArray($subNode);
+				if ($this->stopTraversal) {
+					break;
+				}
+			} elseif ($subNode instanceof Node) {
+				$traverseChildren = true;
+				$breakVisitorIndex = null;
+
+				foreach ($this->visitors as $visitorIndex => $visitor) {
+					$return = $visitor->enterNode($subNode);
+					if ($return === null) {
+						continue;
+					}
+
+					if ($return instanceof Node) {
+						$this->ensureReplacementReasonable($subNode, $return);
+						$subNode = $return;
+					} elseif ($return === self::DONT_TRAVERSE_CHILDREN) {
+						$traverseChildren = false;
+					} elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) {
+						$traverseChildren = false;
+						$breakVisitorIndex = $visitorIndex;
+						break;
+					} elseif ($return === self::STOP_TRAVERSAL) {
+						$this->stopTraversal = true;
+						break 2;
+					} else {
+						throw new LogicException(
+							'enterNode() returned invalid value of type ' . gettype($return)
+						);
+					}
+				}
+
+				if ($traverseChildren) {
+					$subNode = $this->traverseNode($subNode);
+					if ($this->stopTraversal) {
+						break;
+					}
+				}
+
+				foreach ($this->visitors as $visitorIndex => $visitor) {
+					$return = $visitor->leaveNode($subNode);
+
+					if ($return !== null) {
+						if ($return instanceof Node) {
+							$this->ensureReplacementReasonable($subNode, $return);
+							$subNode = $return;
+						} elseif ($return === self::STOP_TRAVERSAL) {
+							$this->stopTraversal = true;
+							break 2;
+						} elseif (is_array($return)) {
+							throw new LogicException(
+								'leaveNode() may only return an array ' .
+								'if the parent structure is an array'
+							);
+						} else {
+							throw new LogicException(
+								'leaveNode() returned invalid value of type ' . gettype($return)
+							);
+						}
+					}
+
+					if ($breakVisitorIndex === $visitorIndex) {
+						break;
+					}
+				}
+			}
+		}
+
+		return $node;
+	}
+
+	/**
+	 * Recursively traverse array (usually of nodes).
+	 *
+	 * @param mixed[] $nodes Array to traverse
+	 *
+	 * @return mixed[] Result of traversal (may be original array or changed one)
+	 */
+	private function traverseArray(array $nodes): array
+	{
+		$doNodes = [];
+
+		foreach ($nodes as $i => &$node) {
+			if ($node instanceof Node) {
+				$traverseChildren = true;
+				$breakVisitorIndex = null;
+
+				foreach ($this->visitors as $visitorIndex => $visitor) {
+					$return = $visitor->enterNode($node);
+					if ($return === null) {
+						continue;
+					}
+
+					if ($return instanceof Node) {
+						$this->ensureReplacementReasonable($node, $return);
+						$node = $return;
+					} elseif (is_array($return)) {
+						$doNodes[] = [$i, $return];
+						continue 2;
+					} elseif ($return === self::REMOVE_NODE) {
+						$doNodes[] = [$i, []];
+						continue 2;
+					} elseif ($return === self::DONT_TRAVERSE_CHILDREN) {
+						$traverseChildren = false;
+					} elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) {
+						$traverseChildren = false;
+						$breakVisitorIndex = $visitorIndex;
+						break;
+					} elseif ($return === self::STOP_TRAVERSAL) {
+						$this->stopTraversal = true;
+						break 2;
+					} else {
+						throw new LogicException(
+							'enterNode() returned invalid value of type ' . gettype($return)
+						);
+					}
+				}
+
+				if ($traverseChildren) {
+					$node = $this->traverseNode($node);
+					if ($this->stopTraversal) {
+						break;
+					}
+				}
+
+				foreach ($this->visitors as $visitorIndex => $visitor) {
+					$return = $visitor->leaveNode($node);
+
+					if ($return !== null) {
+						if ($return instanceof Node) {
+							$this->ensureReplacementReasonable($node, $return);
+							$node = $return;
+						} elseif (is_array($return)) {
+							$doNodes[] = [$i, $return];
+							break;
+						} elseif ($return === self::REMOVE_NODE) {
+							$doNodes[] = [$i, []];
+							break;
+						} elseif ($return === self::STOP_TRAVERSAL) {
+							$this->stopTraversal = true;
+							break 2;
+						} else {
+							throw new LogicException(
+								'leaveNode() returned invalid value of type ' . gettype($return)
+							);
+						}
+					}
+
+					if ($breakVisitorIndex === $visitorIndex) {
+						break;
+					}
+				}
+			} elseif (is_array($node)) {
+				throw new LogicException('Invalid node structure: Contains nested arrays');
+			}
+		}
+
+		if (count($doNodes) > 0) {
+			while ([$i, $replace] = array_pop($doNodes)) {
+				array_splice($nodes, $i, 1, $replace);
+			}
+		}
+
+		return $nodes;
+	}
+
+	private function ensureReplacementReasonable(Node $old, Node $new): void
+	{
+		if ($old instanceof TypeNode && !$new instanceof TypeNode) {
+			throw new LogicException(sprintf('Trying to replace TypeNode with %s', get_class($new)));
+		}
+
+		if ($old instanceof ConstExprNode && !$new instanceof ConstExprNode) {
+			throw new LogicException(sprintf('Trying to replace ConstExprNode with %s', get_class($new)));
+		}
+
+		if ($old instanceof PhpDocChildNode && !$new instanceof PhpDocChildNode) {
+			throw new LogicException(sprintf('Trying to replace PhpDocChildNode with %s', get_class($new)));
+		}
+
+		if ($old instanceof PhpDocTagValueNode && !$new instanceof PhpDocTagValueNode) {
+			throw new LogicException(sprintf('Trying to replace PhpDocTagValueNode with %s', get_class($new)));
+		}
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php
new file mode 100644
index 0000000..bf7d784
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php
@@ -0,0 +1,87 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast;
+
+/**
+ * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
+ *
+ * Copyright (c) 2011, Nikita Popov
+ * All rights reserved.
+ */
+interface NodeVisitor
+{
+
+	/**
+	 * Called once before traversal.
+	 *
+	 * Return value semantics:
+	 *  * null:      $nodes stays as-is
+	 *  * otherwise: $nodes is set to the return value
+	 *
+	 * @param Node[] $nodes Array of nodes
+	 *
+	 * @return Node[]|null Array of nodes
+	 */
+	public function beforeTraverse(array $nodes): ?array;
+
+	/**
+	 * Called when entering a node.
+	 *
+	 * Return value semantics:
+	 *  * null
+	 *        => $node stays as-is
+	 *  * array (of Nodes)
+	 *        => The return value is merged into the parent array (at the position of the $node)
+	 *  * NodeTraverser::REMOVE_NODE
+	 *        => $node is removed from the parent array
+	 *  * NodeTraverser::DONT_TRAVERSE_CHILDREN
+	 *        => Children of $node are not traversed. $node stays as-is
+	 *  * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN
+	 *        => Further visitors for the current node are skipped, and its children are not
+	 *           traversed. $node stays as-is.
+	 *  * NodeTraverser::STOP_TRAVERSAL
+	 *        => Traversal is aborted. $node stays as-is
+	 *  * otherwise
+	 *        => $node is set to the return value
+	 *
+	 * @param Node $node Node
+	 *
+	 * @return Node|Node[]|NodeTraverser::*|null Replacement node (or special return value)
+	 */
+	public function enterNode(Node $node);
+
+	/**
+	 * Called when leaving a node.
+	 *
+	 * Return value semantics:
+	 *  * null
+	 *        => $node stays as-is
+	 *  * NodeTraverser::REMOVE_NODE
+	 *        => $node is removed from the parent array
+	 *  * NodeTraverser::STOP_TRAVERSAL
+	 *        => Traversal is aborted. $node stays as-is
+	 *  * array (of Nodes)
+	 *        => The return value is merged into the parent array (at the position of the $node)
+	 *  * otherwise
+	 *        => $node is set to the return value
+	 *
+	 * @param Node $node Node
+	 *
+	 * @return Node|Node[]|NodeTraverser::REMOVE_NODE|NodeTraverser::STOP_TRAVERSAL|null Replacement node (or special return value)
+	 */
+	public function leaveNode(Node $node);
+
+	/**
+	 * Called once after traversal.
+	 *
+	 * Return value semantics:
+	 *  * null:      $nodes stays as-is
+	 *  * otherwise: $nodes is set to the return value
+	 *
+	 * @param Node[] $nodes Array of nodes
+	 *
+	 * @return Node[]|null Array of nodes
+	 */
+	public function afterTraverse(array $nodes): ?array;
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php
new file mode 100644
index 0000000..7200f3a
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php
@@ -0,0 +1,20 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\NodeVisitor;
+
+use PHPStan\PhpDocParser\Ast\AbstractNodeVisitor;
+use PHPStan\PhpDocParser\Ast\Attribute;
+use PHPStan\PhpDocParser\Ast\Node;
+
+final class CloningVisitor extends AbstractNodeVisitor
+{
+
+	public function enterNode(Node $originalNode)
+	{
+		$node = clone $originalNode;
+		$node->setAttribute(Attribute::ORIGINAL_NODE, $originalNode);
+
+		return $node;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagMethodValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagMethodValueNode.php
new file mode 100644
index 0000000..cf4f556
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagMethodValueNode.php
@@ -0,0 +1,50 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class AssertTagMethodValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string */
+	public $parameter;
+
+	/** @var string */
+	public $method;
+
+	/** @var bool */
+	public $isNegated;
+
+	/** @var bool */
+	public $isEquality;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $parameter, string $method, bool $isNegated, string $description, bool $isEquality = false)
+	{
+		$this->type = $type;
+		$this->parameter = $parameter;
+		$this->method = $method;
+		$this->isNegated = $isNegated;
+		$this->isEquality = $isEquality;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		$isNegated = $this->isNegated ? '!' : '';
+		$isEquality = $this->isEquality ? '=' : '';
+		return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->method}() {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagPropertyValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
new file mode 100644
index 0000000..4fb3180
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagPropertyValueNode.php
@@ -0,0 +1,50 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class AssertTagPropertyValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string */
+	public $parameter;
+
+	/** @var string */
+	public $property;
+
+	/** @var bool */
+	public $isNegated;
+
+	/** @var bool */
+	public $isEquality;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $parameter, string $property, bool $isNegated, string $description, bool $isEquality = false)
+	{
+		$this->type = $type;
+		$this->parameter = $parameter;
+		$this->property = $property;
+		$this->isNegated = $isNegated;
+		$this->isEquality = $isEquality;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		$isNegated = $this->isNegated ? '!' : '';
+		$isEquality = $this->isEquality ? '=' : '';
+		return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter}->{$this->property} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagValueNode.php
new file mode 100644
index 0000000..d6423f5
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagValueNode.php
@@ -0,0 +1,46 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class AssertTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string */
+	public $parameter;
+
+	/** @var bool */
+	public $isNegated;
+
+	/** @var bool */
+	public $isEquality;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $parameter, bool $isNegated, string $description, bool $isEquality = false)
+	{
+		$this->type = $type;
+		$this->parameter = $parameter;
+		$this->isNegated = $isNegated;
+		$this->isEquality = $isEquality;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		$isNegated = $this->isNegated ? '!' : '';
+		$isEquality = $this->isEquality ? '=' : '';
+		return trim("{$isNegated}{$isEquality}{$this->type} {$this->parameter} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/DeprecatedTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/DeprecatedTagValueNode.php
new file mode 100644
index 0000000..abf2f1a
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/DeprecatedTagValueNode.php
@@ -0,0 +1,27 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function trim;
+
+class DeprecatedTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(string $description)
+	{
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim($this->description);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php
new file mode 100644
index 0000000..3a93f5a
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php
@@ -0,0 +1,35 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
+
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function implode;
+
+class DoctrineAnnotation implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $name;
+
+	/** @var list<DoctrineArgument> */
+	public $arguments;
+
+	/**
+	 * @param list<DoctrineArgument> $arguments
+	 */
+	public function __construct(string $name, array $arguments)
+	{
+		$this->name = $name;
+		$this->arguments = $arguments;
+	}
+
+	public function __toString(): string
+	{
+		$arguments = implode(', ', $this->arguments);
+		return $this->name . '(' . $arguments . ')';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php
new file mode 100644
index 0000000..f30812c
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php
@@ -0,0 +1,43 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
+
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+
+/**
+ * @phpstan-type ValueType = DoctrineAnnotation|IdentifierTypeNode|DoctrineArray|ConstExprNode
+ */
+class DoctrineArgument implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var IdentifierTypeNode|null */
+	public $key;
+
+	/** @var ValueType */
+	public $value;
+
+	/**
+	 * @param ValueType $value
+	 */
+	public function __construct(?IdentifierTypeNode $key, $value)
+	{
+		$this->key = $key;
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->key === null) {
+			return (string) $this->value;
+		}
+
+		return $this->key . '=' . $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArray.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArray.php
new file mode 100644
index 0000000..e740567
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArray.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
+
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function implode;
+
+class DoctrineArray implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var list<DoctrineArrayItem> */
+	public $items;
+
+	/**
+	 * @param list<DoctrineArrayItem> $items
+	 */
+	public function __construct(array $items)
+	{
+		$this->items = $items;
+	}
+
+	public function __toString(): string
+	{
+		$items = implode(', ', $this->items);
+
+		return '{' . $items . '}';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php
new file mode 100644
index 0000000..d2dbf2b
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php
@@ -0,0 +1,47 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
+
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+
+/**
+ * @phpstan-import-type ValueType from DoctrineArgument
+ * @phpstan-type KeyType = ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|ConstFetchNode|null
+ */
+class DoctrineArrayItem implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var KeyType */
+	public $key;
+
+	/** @var ValueType */
+	public $value;
+
+	/**
+	 * @param KeyType $key
+	 * @param ValueType $value
+	 */
+	public function __construct($key, $value)
+	{
+		$this->key = $key;
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->key === null) {
+			return (string) $this->value;
+		}
+
+		return $this->key . '=' . $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php
new file mode 100644
index 0000000..84f7b18
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
+use function trim;
+
+class DoctrineTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var DoctrineAnnotation */
+	public $annotation;
+
+	/** @var string (may be empty) */
+	public $description;
+
+
+	public function __construct(
+		DoctrineAnnotation $annotation,
+		string $description
+	)
+	{
+		$this->annotation = $annotation;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->annotation} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php
new file mode 100644
index 0000000..3bf53e1
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
+use function trim;
+
+class ExtendsTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var GenericTypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(GenericTypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php
new file mode 100644
index 0000000..026aa15
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class GenericTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var string (may be empty) */
+	public $value;
+
+	public function __construct(string $value)
+	{
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		return $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php
new file mode 100644
index 0000000..99043d9
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
+use function trim;
+
+class ImplementsTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var GenericTypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(GenericTypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php
new file mode 100644
index 0000000..ca7b4f2
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php
@@ -0,0 +1,53 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Parser\ParserException;
+use function sprintf;
+use function trigger_error;
+use const E_USER_WARNING;
+
+/**
+ * @property ParserException $exception
+ */
+class InvalidTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var string (may be empty) */
+	public $value;
+
+	/** @var mixed[] */
+	private $exceptionArgs;
+
+	public function __construct(string $value, ParserException $exception)
+	{
+		$this->value = $value;
+		$this->exceptionArgs = [
+			$exception->getCurrentTokenValue(),
+			$exception->getCurrentTokenType(),
+			$exception->getCurrentOffset(),
+			$exception->getExpectedTokenType(),
+			$exception->getExpectedTokenValue(),
+			$exception->getCurrentTokenLine(),
+		];
+	}
+
+	public function __get(string $name): ?ParserException
+	{
+		if ($name !== 'exception') {
+			trigger_error(sprintf('Undefined property: %s::$%s', self::class, $name), E_USER_WARNING);
+			return null;
+		}
+
+		return new ParserException(...$this->exceptionArgs);
+	}
+
+	public function __toString(): string
+	{
+		return $this->value;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php
new file mode 100644
index 0000000..211510b
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php
@@ -0,0 +1,58 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function count;
+use function implode;
+
+class MethodTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var bool */
+	public $isStatic;
+
+	/** @var TypeNode|null */
+	public $returnType;
+
+	/** @var string */
+	public $methodName;
+
+	/** @var TemplateTagValueNode[] */
+	public $templateTypes;
+
+	/** @var MethodTagValueParameterNode[] */
+	public $parameters;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	/**
+	 * @param MethodTagValueParameterNode[] $parameters
+	 * @param TemplateTagValueNode[] $templateTypes
+	 */
+	public function __construct(bool $isStatic, ?TypeNode $returnType, string $methodName, array $parameters, string $description, array $templateTypes = [])
+	{
+		$this->isStatic = $isStatic;
+		$this->returnType = $returnType;
+		$this->methodName = $methodName;
+		$this->parameters = $parameters;
+		$this->description = $description;
+		$this->templateTypes = $templateTypes;
+	}
+
+
+	public function __toString(): string
+	{
+		$static = $this->isStatic ? 'static ' : '';
+		$returnType = $this->returnType !== null ? "{$this->returnType} " : '';
+		$parameters = implode(', ', $this->parameters);
+		$description = $this->description !== '' ? " {$this->description}" : '';
+		$templateTypes = count($this->templateTypes) > 0 ? '<' . implode(', ', $this->templateTypes) . '>' : '';
+		return "{$static}{$returnType}{$this->methodName}{$templateTypes}({$parameters}){$description}";
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php
new file mode 100644
index 0000000..7c17e44
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+
+class MethodTagValueParameterNode implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode|null */
+	public $type;
+
+	/** @var bool */
+	public $isReference;
+
+	/** @var bool */
+	public $isVariadic;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var ConstExprNode|null */
+	public $defaultValue;
+
+	public function __construct(?TypeNode $type, bool $isReference, bool $isVariadic, string $parameterName, ?ConstExprNode $defaultValue)
+	{
+		$this->type = $type;
+		$this->isReference = $isReference;
+		$this->isVariadic = $isVariadic;
+		$this->parameterName = $parameterName;
+		$this->defaultValue = $defaultValue;
+	}
+
+
+	public function __toString(): string
+	{
+		$type = $this->type !== null ? "{$this->type} " : '';
+		$isReference = $this->isReference ? '&' : '';
+		$isVariadic = $this->isVariadic ? '...' : '';
+		$default = $this->defaultValue !== null ? " = {$this->defaultValue}" : '';
+		return "{$type}{$isReference}{$isVariadic}{$this->parameterName}{$default}";
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php
new file mode 100644
index 0000000..d9b7d78
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class MixinTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php
new file mode 100644
index 0000000..0ac2131
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php
@@ -0,0 +1,35 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class ParamClosureThisTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $parameterName, string $description)
+	{
+		$this->type = $type;
+		$this->parameterName = $parameterName;
+		$this->description = $description;
+	}
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->parameterName} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php
new file mode 100644
index 0000000..0f480f7
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php
@@ -0,0 +1,30 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function trim;
+
+class ParamImmediatelyInvokedCallableTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(string $parameterName, string $description)
+	{
+		$this->parameterName = $parameterName;
+		$this->description = $description;
+	}
+
+	public function __toString(): string
+	{
+		return trim("{$this->parameterName} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php
new file mode 100644
index 0000000..eab353f
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php
@@ -0,0 +1,30 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function trim;
+
+class ParamLaterInvokedCallableTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(string $parameterName, string $description)
+	{
+		$this->parameterName = $parameterName;
+		$this->description = $description;
+	}
+
+	public function __toString(): string
+	{
+		return trim("{$this->parameterName} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamOutTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamOutTagValueNode.php
new file mode 100644
index 0000000..9f374bf
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamOutTagValueNode.php
@@ -0,0 +1,35 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class ParamOutTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $parameterName, string $description)
+	{
+		$this->type = $type;
+		$this->parameterName = $parameterName;
+		$this->description = $description;
+	}
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->parameterName} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php
new file mode 100644
index 0000000..f93af0e
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php
@@ -0,0 +1,46 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class ParamTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var bool */
+	public $isReference;
+
+	/** @var bool */
+	public $isVariadic;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
+	{
+		$this->type = $type;
+		$this->isReference = $isReference;
+		$this->isVariadic = $isVariadic;
+		$this->parameterName = $parameterName;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		$reference = $this->isReference ? '&' : '';
+		$variadic = $this->isVariadic ? '...' : '';
+		return trim("{$this->type} {$reference}{$variadic}{$this->parameterName} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php
new file mode 100644
index 0000000..6162f92
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php
@@ -0,0 +1,10 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\Node;
+
+interface PhpDocChildNode extends Node
+{
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocNode.php
new file mode 100644
index 0000000..ade55b7
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocNode.php
@@ -0,0 +1,438 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function array_column;
+use function array_filter;
+use function array_map;
+use function implode;
+
+class PhpDocNode implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var PhpDocChildNode[] */
+	public $children;
+
+	/**
+	 * @param PhpDocChildNode[] $children
+	 */
+	public function __construct(array $children)
+	{
+		$this->children = $children;
+	}
+
+
+	/**
+	 * @return PhpDocTagNode[]
+	 */
+	public function getTags(): array
+	{
+		return array_filter($this->children, static function (PhpDocChildNode $child): bool {
+			return $child instanceof PhpDocTagNode;
+		});
+	}
+
+
+	/**
+	 * @return PhpDocTagNode[]
+	 */
+	public function getTagsByName(string $tagName): array
+	{
+		return array_filter($this->getTags(), static function (PhpDocTagNode $tag) use ($tagName): bool {
+			return $tag->name === $tagName;
+		});
+	}
+
+
+	/**
+	 * @return VarTagValueNode[]
+	 */
+	public function getVarTagValues(string $tagName = '@var'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof VarTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ParamTagValueNode[]
+	 */
+	public function getParamTagValues(string $tagName = '@param'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ParamTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return TypelessParamTagValueNode[]
+	 */
+	public function getTypelessParamTagValues(string $tagName = '@param'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof TypelessParamTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ParamImmediatelyInvokedCallableTagValueNode[]
+	 */
+	public function getParamImmediatelyInvokedCallableTagValues(string $tagName = '@param-immediately-invoked-callable'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ParamImmediatelyInvokedCallableTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ParamLaterInvokedCallableTagValueNode[]
+	 */
+	public function getParamLaterInvokedCallableTagValues(string $tagName = '@param-later-invoked-callable'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ParamLaterInvokedCallableTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ParamClosureThisTagValueNode[]
+	 */
+	public function getParamClosureThisTagValues(string $tagName = '@param-closure-this'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ParamClosureThisTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return TemplateTagValueNode[]
+	 */
+	public function getTemplateTagValues(string $tagName = '@template'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof TemplateTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ExtendsTagValueNode[]
+	 */
+	public function getExtendsTagValues(string $tagName = '@extends'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ExtendsTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ImplementsTagValueNode[]
+	 */
+	public function getImplementsTagValues(string $tagName = '@implements'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ImplementsTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return UsesTagValueNode[]
+	 */
+	public function getUsesTagValues(string $tagName = '@use'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof UsesTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ReturnTagValueNode[]
+	 */
+	public function getReturnTagValues(string $tagName = '@return'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ReturnTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ThrowsTagValueNode[]
+	 */
+	public function getThrowsTagValues(string $tagName = '@throws'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ThrowsTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return MixinTagValueNode[]
+	 */
+	public function getMixinTagValues(string $tagName = '@mixin'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof MixinTagValueNode;
+			}
+		);
+	}
+
+	/**
+	 * @return RequireExtendsTagValueNode[]
+	 */
+	public function getRequireExtendsTagValues(string $tagName = '@phpstan-require-extends'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof RequireExtendsTagValueNode;
+			}
+		);
+	}
+
+	/**
+	 * @return RequireImplementsTagValueNode[]
+	 */
+	public function getRequireImplementsTagValues(string $tagName = '@phpstan-require-implements'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof RequireImplementsTagValueNode;
+			}
+		);
+	}
+
+	/**
+	 * @return DeprecatedTagValueNode[]
+	 */
+	public function getDeprecatedTagValues(): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName('@deprecated'), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof DeprecatedTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return PropertyTagValueNode[]
+	 */
+	public function getPropertyTagValues(string $tagName = '@property'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof PropertyTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return PropertyTagValueNode[]
+	 */
+	public function getPropertyReadTagValues(string $tagName = '@property-read'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof PropertyTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return PropertyTagValueNode[]
+	 */
+	public function getPropertyWriteTagValues(string $tagName = '@property-write'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof PropertyTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return MethodTagValueNode[]
+	 */
+	public function getMethodTagValues(string $tagName = '@method'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof MethodTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return TypeAliasTagValueNode[]
+	 */
+	public function getTypeAliasTagValues(string $tagName = '@phpstan-type'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof TypeAliasTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return TypeAliasImportTagValueNode[]
+	 */
+	public function getTypeAliasImportTagValues(string $tagName = '@phpstan-import-type'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof TypeAliasImportTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return AssertTagValueNode[]
+	 */
+	public function getAssertTagValues(string $tagName = '@phpstan-assert'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof AssertTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return AssertTagPropertyValueNode[]
+	 */
+	public function getAssertPropertyTagValues(string $tagName = '@phpstan-assert'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof AssertTagPropertyValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return AssertTagMethodValueNode[]
+	 */
+	public function getAssertMethodTagValues(string $tagName = '@phpstan-assert'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof AssertTagMethodValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return SelfOutTagValueNode[]
+	 */
+	public function getSelfOutTypeTagValues(string $tagName = '@phpstan-this-out'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof SelfOutTagValueNode;
+			}
+		);
+	}
+
+
+	/**
+	 * @return ParamOutTagValueNode[]
+	 */
+	public function getParamOutTypeTagValues(string $tagName = '@param-out'): array
+	{
+		return array_filter(
+			array_column($this->getTagsByName($tagName), 'value'),
+			static function (PhpDocTagValueNode $value): bool {
+				return $value instanceof ParamOutTagValueNode;
+			}
+		);
+	}
+
+
+	public function __toString(): string
+	{
+		$children = array_map(
+			static function (PhpDocChildNode $child): string {
+				$s = (string) $child;
+				return $s === '' ? '' : ' ' . $s;
+			},
+			$this->children
+		);
+		return "/**\n *" . implode("\n *", $children) . "\n */";
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php
new file mode 100644
index 0000000..d20746f
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineTagValueNode;
+use function trim;
+
+class PhpDocTagNode implements PhpDocChildNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $name;
+
+	/** @var PhpDocTagValueNode */
+	public $value;
+
+	public function __construct(string $name, PhpDocTagValueNode $value)
+	{
+		$this->name = $name;
+		$this->value = $value;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->value instanceof DoctrineTagValueNode) {
+			return (string) $this->value;
+		}
+
+		return trim("{$this->name} {$this->value}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php
new file mode 100644
index 0000000..7723fa0
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php
@@ -0,0 +1,10 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\Node;
+
+interface PhpDocTagValueNode extends Node
+{
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTextNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTextNode.php
new file mode 100644
index 0000000..0bca3c9
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTextNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class PhpDocTextNode implements PhpDocChildNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $text;
+
+	public function __construct(string $text)
+	{
+		$this->text = $text;
+	}
+
+
+	public function __toString(): string
+	{
+		return $this->text;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php
new file mode 100644
index 0000000..046003d
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class PropertyTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string */
+	public $propertyName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $propertyName, string $description)
+	{
+		$this->type = $type;
+		$this->propertyName = $propertyName;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->propertyName} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireExtendsTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireExtendsTagValueNode.php
new file mode 100644
index 0000000..91c2689
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireExtendsTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class RequireExtendsTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireImplementsTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireImplementsTagValueNode.php
new file mode 100644
index 0000000..65c9213
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireImplementsTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class RequireImplementsTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php
new file mode 100644
index 0000000..d53c8c7
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class ReturnTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/SelfOutTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/SelfOutTagValueNode.php
new file mode 100644
index 0000000..83169af
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/SelfOutTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class SelfOutTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim($this->type . ' ' . $this->description);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php
new file mode 100644
index 0000000..8bc01f6
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php
@@ -0,0 +1,50 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class TemplateTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var non-empty-string */
+	public $name;
+
+	/** @var TypeNode|null */
+	public $bound;
+
+	/** @var TypeNode|null */
+	public $lowerBound;
+
+	/** @var TypeNode|null */
+	public $default;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	/**
+	 * @param non-empty-string $name
+	 */
+	public function __construct(string $name, ?TypeNode $bound, string $description, ?TypeNode $default = null, ?TypeNode $lowerBound = null)
+	{
+		$this->name = $name;
+		$this->bound = $bound;
+		$this->lowerBound = $lowerBound;
+		$this->default = $default;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		$upperBound = $this->bound !== null ? " of {$this->bound}" : '';
+		$lowerBound = $this->lowerBound !== null ? " super {$this->lowerBound}" : '';
+		$default = $this->default !== null ? " = {$this->default}" : '';
+		return trim("{$this->name}{$upperBound}{$lowerBound}{$default} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php
new file mode 100644
index 0000000..62d2aed
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class ThrowsTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php
new file mode 100644
index 0000000..ad6b85a
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php
@@ -0,0 +1,38 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use function trim;
+
+class TypeAliasImportTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $importedAlias;
+
+	/** @var IdentifierTypeNode */
+	public $importedFrom;
+
+	/** @var string|null */
+	public $importedAs;
+
+	public function __construct(string $importedAlias, IdentifierTypeNode $importedFrom, ?string $importedAs)
+	{
+		$this->importedAlias = $importedAlias;
+		$this->importedFrom = $importedFrom;
+		$this->importedAs = $importedAs;
+	}
+
+	public function __toString(): string
+	{
+		return trim(
+			"{$this->importedAlias} from {$this->importedFrom}"
+			. ($this->importedAs !== null ? " as {$this->importedAs}" : '')
+		);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasTagValueNode.php
new file mode 100644
index 0000000..4ccaaac
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class TypeAliasTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $alias;
+
+	/** @var TypeNode */
+	public $type;
+
+	public function __construct(string $alias, TypeNode $type)
+	{
+		$this->alias = $alias;
+		$this->type = $type;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->alias} {$this->type}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypelessParamTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypelessParamTagValueNode.php
new file mode 100644
index 0000000..8b98295
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypelessParamTagValueNode.php
@@ -0,0 +1,41 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function trim;
+
+class TypelessParamTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var bool */
+	public $isReference;
+
+	/** @var bool */
+	public $isVariadic;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(bool $isVariadic, string $parameterName, string $description, bool $isReference = false)
+	{
+		$this->isReference = $isReference;
+		$this->isVariadic = $isVariadic;
+		$this->parameterName = $parameterName;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		$reference = $this->isReference ? '&' : '';
+		$variadic = $this->isVariadic ? '...' : '';
+		return trim("{$reference}{$variadic}{$this->parameterName} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php
new file mode 100644
index 0000000..cd573d9
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php
@@ -0,0 +1,32 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
+use function trim;
+
+class UsesTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var GenericTypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(GenericTypeNode $type, string $description)
+	{
+		$this->type = $type;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("{$this->type} {$this->description}");
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.php
new file mode 100644
index 0000000..afb941a
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\PhpDoc;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use function trim;
+
+class VarTagValueNode implements PhpDocTagValueNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var string (may be empty) */
+	public $variableName;
+
+	/** @var string (may be empty) */
+	public $description;
+
+	public function __construct(TypeNode $type, string $variableName, string $description)
+	{
+		$this->type = $type;
+		$this->variableName = $variableName;
+		$this->description = $description;
+	}
+
+
+	public function __toString(): string
+	{
+		return trim("$this->type " . trim("{$this->variableName} {$this->description}"));
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php
new file mode 100644
index 0000000..660c6c9
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function sprintf;
+
+class ArrayShapeItemNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null */
+	public $keyName;
+
+	/** @var bool */
+	public $optional;
+
+	/** @var TypeNode */
+	public $valueType;
+
+	/**
+	 * @param ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null $keyName
+	 */
+	public function __construct($keyName, bool $optional, TypeNode $valueType)
+	{
+		$this->keyName = $keyName;
+		$this->optional = $optional;
+		$this->valueType = $valueType;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->keyName !== null) {
+			return sprintf(
+				'%s%s: %s',
+				(string) $this->keyName,
+				$this->optional ? '?' : '',
+				(string) $this->valueType
+			);
+		}
+
+		return (string) $this->valueType;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php
new file mode 100644
index 0000000..1f4ed4a
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php
@@ -0,0 +1,57 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function implode;
+
+class ArrayShapeNode implements TypeNode
+{
+
+	public const KIND_ARRAY = 'array';
+	public const KIND_LIST = 'list';
+
+	use NodeAttributes;
+
+	/** @var ArrayShapeItemNode[] */
+	public $items;
+
+	/** @var bool */
+	public $sealed;
+
+	/** @var self::KIND_* */
+	public $kind;
+
+	/** @var ArrayShapeUnsealedTypeNode|null */
+	public $unsealedType;
+
+	/**
+	 * @param ArrayShapeItemNode[] $items
+	 * @param self::KIND_* $kind
+	 */
+	public function __construct(
+		array $items,
+		bool $sealed = true,
+		string $kind = self::KIND_ARRAY,
+		?ArrayShapeUnsealedTypeNode $unsealedType = null
+	)
+	{
+		$this->items = $items;
+		$this->sealed = $sealed;
+		$this->kind = $kind;
+		$this->unsealedType = $unsealedType;
+	}
+
+
+	public function __toString(): string
+	{
+		$items = $this->items;
+
+		if (! $this->sealed) {
+			$items[] = '...' . $this->unsealedType;
+		}
+
+		return $this->kind . '{' . implode(', ', $items) . '}';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeUnsealedTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeUnsealedTypeNode.php
new file mode 100644
index 0000000..7ffdf1d
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeUnsealedTypeNode.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function sprintf;
+
+class ArrayShapeUnsealedTypeNode implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $valueType;
+
+	/** @var TypeNode|null */
+	public $keyType;
+
+	public function __construct(TypeNode $valueType, ?TypeNode $keyType)
+	{
+		$this->valueType = $valueType;
+		$this->keyType = $keyType;
+	}
+
+	public function __toString(): string
+	{
+		if ($this->keyType !== null) {
+			return sprintf('<%s, %s>', $this->keyType, $this->valueType);
+		}
+		return sprintf('<%s>', $this->valueType);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php
new file mode 100644
index 0000000..d203103
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ArrayTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	public function __construct(TypeNode $type)
+	{
+		$this->type = $type;
+	}
+
+
+	public function __toString(): string
+	{
+		if (
+			$this->type instanceof CallableTypeNode
+			|| $this->type instanceof ConstTypeNode
+			|| $this->type instanceof NullableTypeNode
+		) {
+			return '(' . $this->type . ')[]';
+		}
+
+		return $this->type . '[]';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php
new file mode 100644
index 0000000..4c91319
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php
@@ -0,0 +1,52 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
+use function implode;
+
+class CallableTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var IdentifierTypeNode */
+	public $identifier;
+
+	/** @var TemplateTagValueNode[] */
+	public $templateTypes;
+
+	/** @var CallableTypeParameterNode[] */
+	public $parameters;
+
+	/** @var TypeNode */
+	public $returnType;
+
+	/**
+	 * @param CallableTypeParameterNode[] $parameters
+	 * @param TemplateTagValueNode[]  $templateTypes
+	 */
+	public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType, array $templateTypes = [])
+	{
+		$this->identifier = $identifier;
+		$this->parameters = $parameters;
+		$this->returnType = $returnType;
+		$this->templateTypes = $templateTypes;
+	}
+
+
+	public function __toString(): string
+	{
+		$returnType = $this->returnType;
+		if ($returnType instanceof self) {
+			$returnType = "({$returnType})";
+		}
+		$template = $this->templateTypes !== []
+			? '<' . implode(', ', $this->templateTypes) . '>'
+			: '';
+		$parameters = implode(', ', $this->parameters);
+		return "{$this->identifier}{$template}({$parameters}): {$returnType}";
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php
new file mode 100644
index 0000000..c78d4c7
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php
@@ -0,0 +1,48 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function trim;
+
+class CallableTypeParameterNode implements Node
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var bool */
+	public $isReference;
+
+	/** @var bool */
+	public $isVariadic;
+
+	/** @var string (may be empty) */
+	public $parameterName;
+
+	/** @var bool */
+	public $isOptional;
+
+	public function __construct(TypeNode $type, bool $isReference, bool $isVariadic, string $parameterName, bool $isOptional)
+	{
+		$this->type = $type;
+		$this->isReference = $isReference;
+		$this->isVariadic = $isVariadic;
+		$this->parameterName = $parameterName;
+		$this->isOptional = $isOptional;
+	}
+
+
+	public function __toString(): string
+	{
+		$type = "{$this->type} ";
+		$isReference = $this->isReference ? '&' : '';
+		$isVariadic = $this->isVariadic ? '...' : '';
+		$isOptional = $this->isOptional ? '=' : '';
+		return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $isOptional;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeForParameterNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeForParameterNode.php
new file mode 100644
index 0000000..fbfcae9
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeForParameterNode.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function sprintf;
+
+class ConditionalTypeForParameterNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $parameterName;
+
+	/** @var TypeNode */
+	public $targetType;
+
+	/** @var TypeNode */
+	public $if;
+
+	/** @var TypeNode */
+	public $else;
+
+	/** @var bool */
+	public $negated;
+
+	public function __construct(string $parameterName, TypeNode $targetType, TypeNode $if, TypeNode $else, bool $negated)
+	{
+		$this->parameterName = $parameterName;
+		$this->targetType = $targetType;
+		$this->if = $if;
+		$this->else = $else;
+		$this->negated = $negated;
+	}
+
+	public function __toString(): string
+	{
+		return sprintf(
+			'(%s %s %s ? %s : %s)',
+			$this->parameterName,
+			$this->negated ? 'is not' : 'is',
+			$this->targetType,
+			$this->if,
+			$this->else
+		);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeNode.php
new file mode 100644
index 0000000..bfdb0db
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeNode.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function sprintf;
+
+class ConditionalTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $subjectType;
+
+	/** @var TypeNode */
+	public $targetType;
+
+	/** @var TypeNode */
+	public $if;
+
+	/** @var TypeNode */
+	public $else;
+
+	/** @var bool */
+	public $negated;
+
+	public function __construct(TypeNode $subjectType, TypeNode $targetType, TypeNode $if, TypeNode $else, bool $negated)
+	{
+		$this->subjectType = $subjectType;
+		$this->targetType = $targetType;
+		$this->if = $if;
+		$this->else = $else;
+		$this->negated = $negated;
+	}
+
+	public function __toString(): string
+	{
+		return sprintf(
+			'(%s %s %s ? %s : %s)',
+			$this->subjectType,
+			$this->negated ? 'is not' : 'is',
+			$this->targetType,
+			$this->if,
+			$this->else
+		);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php
new file mode 100644
index 0000000..0096055
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ConstTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var ConstExprNode */
+	public $constExpr;
+
+	public function __construct(ConstExprNode $constExpr)
+	{
+		$this->constExpr = $constExpr;
+	}
+
+	public function __toString(): string
+	{
+		return $this->constExpr->__toString();
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php
new file mode 100644
index 0000000..44e1d16
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php
@@ -0,0 +1,58 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function implode;
+use function sprintf;
+
+class GenericTypeNode implements TypeNode
+{
+
+	public const VARIANCE_INVARIANT = 'invariant';
+	public const VARIANCE_COVARIANT = 'covariant';
+	public const VARIANCE_CONTRAVARIANT = 'contravariant';
+	public const VARIANCE_BIVARIANT = 'bivariant';
+
+	use NodeAttributes;
+
+	/** @var IdentifierTypeNode */
+	public $type;
+
+	/** @var TypeNode[] */
+	public $genericTypes;
+
+	/** @var (self::VARIANCE_*)[] */
+	public $variances;
+
+	/**
+	 * @param TypeNode[] $genericTypes
+	 * @param (self::VARIANCE_*)[] $variances
+	 */
+	public function __construct(IdentifierTypeNode $type, array $genericTypes, array $variances = [])
+	{
+		$this->type = $type;
+		$this->genericTypes = $genericTypes;
+		$this->variances = $variances;
+	}
+
+
+	public function __toString(): string
+	{
+		$genericTypes = [];
+
+		foreach ($this->genericTypes as $index => $type) {
+			$variance = $this->variances[$index] ?? self::VARIANCE_INVARIANT;
+			if ($variance === self::VARIANCE_INVARIANT) {
+				$genericTypes[] = (string) $type;
+			} elseif ($variance === self::VARIANCE_BIVARIANT) {
+				$genericTypes[] = '*';
+			} else {
+				$genericTypes[] = sprintf('%s %s', $variance, $type);
+			}
+		}
+
+		return $this->type . '<' . implode(', ', $genericTypes) . '>';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php
new file mode 100644
index 0000000..29bac30
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class IdentifierTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var string */
+	public $name;
+
+	public function __construct(string $name)
+	{
+		$this->name = $name;
+	}
+
+
+	public function __toString(): string
+	{
+		return $this->name;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php
new file mode 100644
index 0000000..fd761cf
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php
@@ -0,0 +1,37 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function array_map;
+use function implode;
+
+class IntersectionTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode[] */
+	public $types;
+
+	/**
+	 * @param TypeNode[] $types
+	 */
+	public function __construct(array $types)
+	{
+		$this->types = $types;
+	}
+
+
+	public function __toString(): string
+	{
+		return '(' . implode(' & ', array_map(static function (TypeNode $type): string {
+			if ($type instanceof NullableTypeNode) {
+				return '(' . $type . ')';
+			}
+
+			return (string) $type;
+		}, $this->types)) . ')';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.php
new file mode 100644
index 0000000..1ec47cf
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.php
@@ -0,0 +1,38 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use PHPStan\PhpDocParser\Parser\ParserException;
+
+class InvalidTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var mixed[] */
+	private $exceptionArgs;
+
+	public function __construct(ParserException $exception)
+	{
+		$this->exceptionArgs = [
+			$exception->getCurrentTokenValue(),
+			$exception->getCurrentTokenType(),
+			$exception->getCurrentOffset(),
+			$exception->getExpectedTokenType(),
+			$exception->getExpectedTokenValue(),
+			$exception->getCurrentTokenLine(),
+		];
+	}
+
+	public function getException(): ParserException
+	{
+		return new ParserException(...$this->exceptionArgs);
+	}
+
+	public function __toString(): string
+	{
+		return '*Invalid type*';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php
new file mode 100644
index 0000000..73f438c
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php
@@ -0,0 +1,26 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class NullableTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	public function __construct(TypeNode $type)
+	{
+		$this->type = $type;
+	}
+
+
+	public function __toString(): string
+	{
+		return '?' . $this->type;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php
new file mode 100644
index 0000000..2f01240
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php
@@ -0,0 +1,48 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function sprintf;
+
+class ObjectShapeItemNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var ConstExprStringNode|IdentifierTypeNode */
+	public $keyName;
+
+	/** @var bool */
+	public $optional;
+
+	/** @var TypeNode */
+	public $valueType;
+
+	/**
+	 * @param ConstExprStringNode|IdentifierTypeNode $keyName
+	 */
+	public function __construct($keyName, bool $optional, TypeNode $valueType)
+	{
+		$this->keyName = $keyName;
+		$this->optional = $optional;
+		$this->valueType = $valueType;
+	}
+
+
+	public function __toString(): string
+	{
+		if ($this->keyName !== null) {
+			return sprintf(
+				'%s%s: %s',
+				(string) $this->keyName,
+				$this->optional ? '?' : '',
+				(string) $this->valueType
+			);
+		}
+
+		return (string) $this->valueType;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php
new file mode 100644
index 0000000..f418bc3
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function implode;
+
+class ObjectShapeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var ObjectShapeItemNode[] */
+	public $items;
+
+	/**
+	 * @param ObjectShapeItemNode[] $items
+	 */
+	public function __construct(array $items)
+	{
+		$this->items = $items;
+	}
+
+	public function __toString(): string
+	{
+		$items = $this->items;
+
+		return 'object{' . implode(', ', $items) . '}';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php
new file mode 100644
index 0000000..c27ec0a
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class OffsetAccessTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode */
+	public $type;
+
+	/** @var TypeNode */
+	public $offset;
+
+	public function __construct(TypeNode $type, TypeNode $offset)
+	{
+		$this->type = $type;
+		$this->offset = $offset;
+	}
+
+	public function __toString(): string
+	{
+		if (
+			$this->type instanceof CallableTypeNode
+			|| $this->type instanceof NullableTypeNode
+		) {
+			return '(' . $this->type . ')[' . $this->offset . ']';
+		}
+
+		return $this->type . '[' . $this->offset . ']';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php
new file mode 100644
index 0000000..d94e6f8
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php
@@ -0,0 +1,17 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+
+class ThisTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	public function __toString(): string
+	{
+		return '$this';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/TypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/TypeNode.php
new file mode 100644
index 0000000..849e04e
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/TypeNode.php
@@ -0,0 +1,10 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\Node;
+
+interface TypeNode extends Node
+{
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php
new file mode 100644
index 0000000..c552dab
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php
@@ -0,0 +1,37 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Ast\Type;
+
+use PHPStan\PhpDocParser\Ast\NodeAttributes;
+use function array_map;
+use function implode;
+
+class UnionTypeNode implements TypeNode
+{
+
+	use NodeAttributes;
+
+	/** @var TypeNode[] */
+	public $types;
+
+	/**
+	 * @param TypeNode[] $types
+	 */
+	public function __construct(array $types)
+	{
+		$this->types = $types;
+	}
+
+
+	public function __toString(): string
+	{
+		return '(' . implode(' | ', array_map(static function (TypeNode $type): string {
+			if ($type instanceof NullableTypeNode) {
+				return '(' . $type . ')';
+			}
+
+				return (string) $type;
+		}, $this->types)) . ')';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php b/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php
new file mode 100644
index 0000000..32539fa
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php
@@ -0,0 +1,198 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Lexer;
+
+use function implode;
+use function preg_match_all;
+use const PREG_SET_ORDER;
+
+/**
+ * Implementation based on Nette Tokenizer (New BSD License; https://github.com/nette/tokenizer)
+ */
+class Lexer
+{
+
+	public const TOKEN_REFERENCE = 0;
+	public const TOKEN_UNION = 1;
+	public const TOKEN_INTERSECTION = 2;
+	public const TOKEN_NULLABLE = 3;
+	public const TOKEN_OPEN_PARENTHESES = 4;
+	public const TOKEN_CLOSE_PARENTHESES = 5;
+	public const TOKEN_OPEN_ANGLE_BRACKET = 6;
+	public const TOKEN_CLOSE_ANGLE_BRACKET = 7;
+	public const TOKEN_OPEN_SQUARE_BRACKET = 8;
+	public const TOKEN_CLOSE_SQUARE_BRACKET = 9;
+	public const TOKEN_COMMA = 10;
+	public const TOKEN_VARIADIC = 11;
+	public const TOKEN_DOUBLE_COLON = 12;
+	public const TOKEN_DOUBLE_ARROW = 13;
+	public const TOKEN_EQUAL = 14;
+	public const TOKEN_OPEN_PHPDOC = 15;
+	public const TOKEN_CLOSE_PHPDOC = 16;
+	public const TOKEN_PHPDOC_TAG = 17;
+	public const TOKEN_DOCTRINE_TAG = 18;
+	public const TOKEN_FLOAT = 19;
+	public const TOKEN_INTEGER = 20;
+	public const TOKEN_SINGLE_QUOTED_STRING = 21;
+	public const TOKEN_DOUBLE_QUOTED_STRING = 22;
+	public const TOKEN_DOCTRINE_ANNOTATION_STRING = 23;
+	public const TOKEN_IDENTIFIER = 24;
+	public const TOKEN_THIS_VARIABLE = 25;
+	public const TOKEN_VARIABLE = 26;
+	public const TOKEN_HORIZONTAL_WS = 27;
+	public const TOKEN_PHPDOC_EOL = 28;
+	public const TOKEN_OTHER = 29;
+	public const TOKEN_END = 30;
+	public const TOKEN_COLON = 31;
+	public const TOKEN_WILDCARD = 32;
+	public const TOKEN_OPEN_CURLY_BRACKET = 33;
+	public const TOKEN_CLOSE_CURLY_BRACKET = 34;
+	public const TOKEN_NEGATED = 35;
+	public const TOKEN_ARROW = 36;
+
+	public const TOKEN_LABELS = [
+		self::TOKEN_REFERENCE => '\'&\'',
+		self::TOKEN_UNION => '\'|\'',
+		self::TOKEN_INTERSECTION => '\'&\'',
+		self::TOKEN_NULLABLE => '\'?\'',
+		self::TOKEN_NEGATED => '\'!\'',
+		self::TOKEN_OPEN_PARENTHESES => '\'(\'',
+		self::TOKEN_CLOSE_PARENTHESES => '\')\'',
+		self::TOKEN_OPEN_ANGLE_BRACKET => '\'<\'',
+		self::TOKEN_CLOSE_ANGLE_BRACKET => '\'>\'',
+		self::TOKEN_OPEN_SQUARE_BRACKET => '\'[\'',
+		self::TOKEN_CLOSE_SQUARE_BRACKET => '\']\'',
+		self::TOKEN_OPEN_CURLY_BRACKET => '\'{\'',
+		self::TOKEN_CLOSE_CURLY_BRACKET => '\'}\'',
+		self::TOKEN_COMMA => '\',\'',
+		self::TOKEN_COLON => '\':\'',
+		self::TOKEN_VARIADIC => '\'...\'',
+		self::TOKEN_DOUBLE_COLON => '\'::\'',
+		self::TOKEN_DOUBLE_ARROW => '\'=>\'',
+		self::TOKEN_ARROW => '\'->\'',
+		self::TOKEN_EQUAL => '\'=\'',
+		self::TOKEN_OPEN_PHPDOC => '\'/**\'',
+		self::TOKEN_CLOSE_PHPDOC => '\'*/\'',
+		self::TOKEN_PHPDOC_TAG => 'TOKEN_PHPDOC_TAG',
+		self::TOKEN_DOCTRINE_TAG => 'TOKEN_DOCTRINE_TAG',
+		self::TOKEN_PHPDOC_EOL => 'TOKEN_PHPDOC_EOL',
+		self::TOKEN_FLOAT => 'TOKEN_FLOAT',
+		self::TOKEN_INTEGER => 'TOKEN_INTEGER',
+		self::TOKEN_SINGLE_QUOTED_STRING => 'TOKEN_SINGLE_QUOTED_STRING',
+		self::TOKEN_DOUBLE_QUOTED_STRING => 'TOKEN_DOUBLE_QUOTED_STRING',
+		self::TOKEN_DOCTRINE_ANNOTATION_STRING => 'TOKEN_DOCTRINE_ANNOTATION_STRING',
+		self::TOKEN_IDENTIFIER => 'type',
+		self::TOKEN_THIS_VARIABLE => '\'$this\'',
+		self::TOKEN_VARIABLE => 'variable',
+		self::TOKEN_HORIZONTAL_WS => 'TOKEN_HORIZONTAL_WS',
+		self::TOKEN_OTHER => 'TOKEN_OTHER',
+		self::TOKEN_END => 'TOKEN_END',
+		self::TOKEN_WILDCARD => '*',
+	];
+
+	public const VALUE_OFFSET = 0;
+	public const TYPE_OFFSET = 1;
+	public const LINE_OFFSET = 2;
+
+	/** @var bool */
+	private $parseDoctrineAnnotations;
+
+	/** @var string|null */
+	private $regexp;
+
+	public function __construct(bool $parseDoctrineAnnotations = false)
+	{
+		$this->parseDoctrineAnnotations = $parseDoctrineAnnotations;
+	}
+
+	/**
+	 * @return list<array{string, int, int}>
+	 */
+	public function tokenize(string $s): array
+	{
+		if ($this->regexp === null) {
+			$this->regexp = $this->generateRegexp();
+		}
+
+		preg_match_all($this->regexp, $s, $matches, PREG_SET_ORDER);
+
+		$tokens = [];
+		$line = 1;
+		foreach ($matches as $match) {
+			$type = (int) $match['MARK'];
+			$tokens[] = [$match[0], $type, $line];
+			if ($type !== self::TOKEN_PHPDOC_EOL) {
+				continue;
+			}
+
+			$line++;
+		}
+
+		$tokens[] = ['', self::TOKEN_END, $line];
+
+		return $tokens;
+	}
+
+
+	private function generateRegexp(): string
+	{
+		$patterns = [
+			self::TOKEN_HORIZONTAL_WS => '[\\x09\\x20]++',
+
+			self::TOKEN_IDENTIFIER => '(?:[\\\\]?+[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF-]*+)++',
+			self::TOKEN_THIS_VARIABLE => '\\$this(?![0-9a-z_\\x80-\\xFF])',
+			self::TOKEN_VARIABLE => '\\$[a-z_\\x80-\\xFF][0-9a-z_\\x80-\\xFF]*+',
+
+			// '&' followed by TOKEN_VARIADIC, TOKEN_VARIABLE, TOKEN_EQUAL, TOKEN_EQUAL or TOKEN_CLOSE_PARENTHESES
+			self::TOKEN_REFERENCE => '&(?=\\s*+(?:[.,=)]|(?:\\$(?!this(?![0-9a-z_\\x80-\\xFF])))))',
+			self::TOKEN_UNION => '\\|',
+			self::TOKEN_INTERSECTION => '&',
+			self::TOKEN_NULLABLE => '\\?',
+			self::TOKEN_NEGATED => '!',
+
+			self::TOKEN_OPEN_PARENTHESES => '\\(',
+			self::TOKEN_CLOSE_PARENTHESES => '\\)',
+			self::TOKEN_OPEN_ANGLE_BRACKET => '<',
+			self::TOKEN_CLOSE_ANGLE_BRACKET => '>',
+			self::TOKEN_OPEN_SQUARE_BRACKET => '\\[',
+			self::TOKEN_CLOSE_SQUARE_BRACKET => '\\]',
+			self::TOKEN_OPEN_CURLY_BRACKET => '\\{',
+			self::TOKEN_CLOSE_CURLY_BRACKET => '\\}',
+
+			self::TOKEN_COMMA => ',',
+			self::TOKEN_VARIADIC => '\\.\\.\\.',
+			self::TOKEN_DOUBLE_COLON => '::',
+			self::TOKEN_DOUBLE_ARROW => '=>',
+			self::TOKEN_ARROW => '->',
+			self::TOKEN_EQUAL => '=',
+			self::TOKEN_COLON => ':',
+
+			self::TOKEN_OPEN_PHPDOC => '/\\*\\*(?=\\s)\\x20?+',
+			self::TOKEN_CLOSE_PHPDOC => '\\*/',
+			self::TOKEN_PHPDOC_TAG => '@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+',
+			self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/)\\x20?+)?',
+
+			self::TOKEN_FLOAT => '[+\-]?(?:(?:[0-9]++(_[0-9]++)*\\.[0-9]*+(_[0-9]++)*(?:e[+\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e[+\-]?[0-9]++(_[0-9]++)*)?)|(?:[0-9]++(_[0-9]++)*e[+\-]?[0-9]++(_[0-9]++)*))',
+			self::TOKEN_INTEGER => '[+\-]?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))',
+			self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'',
+			self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"',
+
+			self::TOKEN_WILDCARD => '\\*',
+		];
+
+		if ($this->parseDoctrineAnnotations) {
+			$patterns[self::TOKEN_DOCTRINE_TAG] = '@[a-z_\\\\][a-z0-9_\:\\\\]*[a-z_][a-z0-9_]*';
+			$patterns[self::TOKEN_DOCTRINE_ANNOTATION_STRING] = '"(?:""|[^"])*+"';
+		}
+
+		// anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL
+		$patterns[self::TOKEN_OTHER] = '(?:(?!\\*/)[^\\s])++';
+
+		foreach ($patterns as $type => &$pattern) {
+			$pattern = '(?:' . $pattern . ')(*MARK:' . $type . ')';
+		}
+
+		return '~' . implode('|', $patterns) . '~Asi';
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php b/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php
new file mode 100644
index 0000000..f6a7306
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php
@@ -0,0 +1,333 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Parser;
+
+use PHPStan\PhpDocParser\Ast;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use function str_replace;
+use function strtolower;
+use function substr;
+
+class ConstExprParser
+{
+
+	/** @var bool */
+	private $unescapeStrings;
+
+	/** @var bool */
+	private $quoteAwareConstExprString;
+
+	/** @var bool */
+	private $useLinesAttributes;
+
+	/** @var bool */
+	private $useIndexAttributes;
+
+	/** @var bool */
+	private $parseDoctrineStrings;
+
+	/**
+	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
+	 */
+	public function __construct(
+		bool $unescapeStrings = false,
+		bool $quoteAwareConstExprString = false,
+		array $usedAttributes = []
+	)
+	{
+		$this->unescapeStrings = $unescapeStrings;
+		$this->quoteAwareConstExprString = $quoteAwareConstExprString;
+		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
+		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
+		$this->parseDoctrineStrings = false;
+	}
+
+	/**
+	 * @internal
+	 */
+	public function toDoctrine(): self
+	{
+		$self = new self(
+			$this->unescapeStrings,
+			$this->quoteAwareConstExprString,
+			[
+				'lines' => $this->useLinesAttributes,
+				'indexes' => $this->useIndexAttributes,
+			]
+		);
+		$self->parseDoctrineStrings = true;
+		return $self;
+	}
+
+	public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) {
+			$value = $tokens->currentTokenValue();
+			$tokens->next();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)),
+				$startLine,
+				$startIndex
+			);
+		}
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
+			$value = $tokens->currentTokenValue();
+			$tokens->next();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)),
+				$startLine,
+				$startIndex
+			);
+		}
+
+		if ($this->parseDoctrineStrings && $tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
+			$value = $tokens->currentTokenValue();
+			$tokens->next();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($value)),
+				$startLine,
+				$startIndex
+			);
+		}
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING, Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
+			if ($this->parseDoctrineStrings) {
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
+					throw new ParserException(
+						$tokens->currentTokenValue(),
+						$tokens->currentTokenType(),
+						$tokens->currentTokenOffset(),
+						Lexer::TOKEN_DOUBLE_QUOTED_STRING,
+						null,
+						$tokens->currentTokenLine()
+					);
+				}
+
+				$value = $tokens->currentTokenValue();
+				$tokens->next();
+
+				return $this->enrichWithAttributes(
+					$tokens,
+					$this->parseDoctrineString($value, $tokens),
+					$startLine,
+					$startIndex
+				);
+			}
+			$value = $tokens->currentTokenValue();
+			$type = $tokens->currentTokenType();
+			if ($trimStrings) {
+				if ($this->unescapeStrings) {
+					$value = StringUnescaper::unescapeString($value);
+				} else {
+					$value = substr($value, 1, -1);
+				}
+			}
+			$tokens->next();
+
+			if ($this->quoteAwareConstExprString) {
+				return $this->enrichWithAttributes(
+					$tokens,
+					new Ast\ConstExpr\QuoteAwareConstExprStringNode(
+						$value,
+						$type === Lexer::TOKEN_SINGLE_QUOTED_STRING
+							? Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED
+							: Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED
+					),
+					$startLine,
+					$startIndex
+				);
+			}
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Ast\ConstExpr\ConstExprStringNode($value),
+				$startLine,
+				$startIndex
+			);
+
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
+			$identifier = $tokens->currentTokenValue();
+			$tokens->next();
+
+			switch (strtolower($identifier)) {
+				case 'true':
+					return $this->enrichWithAttributes(
+						$tokens,
+						new Ast\ConstExpr\ConstExprTrueNode(),
+						$startLine,
+						$startIndex
+					);
+				case 'false':
+					return $this->enrichWithAttributes(
+						$tokens,
+						new Ast\ConstExpr\ConstExprFalseNode(),
+						$startLine,
+						$startIndex
+					);
+				case 'null':
+					return $this->enrichWithAttributes(
+						$tokens,
+						new Ast\ConstExpr\ConstExprNullNode(),
+						$startLine,
+						$startIndex
+					);
+				case 'array':
+					$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
+					return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES, $startIndex);
+			}
+
+			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
+				$classConstantName = '';
+				$lastType = null;
+				while (true) {
+					if ($lastType !== Lexer::TOKEN_IDENTIFIER && $tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) {
+						$classConstantName .= $tokens->currentTokenValue();
+						$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+						$lastType = Lexer::TOKEN_IDENTIFIER;
+
+						continue;
+					}
+
+					if ($lastType !== Lexer::TOKEN_WILDCARD && $tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) {
+						$classConstantName .= '*';
+						$lastType = Lexer::TOKEN_WILDCARD;
+
+						if ($tokens->getSkippedHorizontalWhiteSpaceIfAny() !== '') {
+							break;
+						}
+
+						continue;
+					}
+
+					if ($lastType === null) {
+						// trigger parse error if nothing valid was consumed
+						$tokens->consumeTokenType(Lexer::TOKEN_WILDCARD);
+					}
+
+					break;
+				}
+
+				return $this->enrichWithAttributes(
+					$tokens,
+					new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName),
+					$startLine,
+					$startIndex
+				);
+
+			}
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Ast\ConstExpr\ConstFetchNode('', $identifier),
+				$startLine,
+				$startIndex
+			);
+
+		} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+			return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET, $startIndex);
+		}
+
+		throw new ParserException(
+			$tokens->currentTokenValue(),
+			$tokens->currentTokenType(),
+			$tokens->currentTokenOffset(),
+			Lexer::TOKEN_IDENTIFIER,
+			null,
+			$tokens->currentTokenLine()
+		);
+	}
+
+
+	private function parseArray(TokenIterator $tokens, int $endToken, int $startIndex): Ast\ConstExpr\ConstExprArrayNode
+	{
+		$items = [];
+
+		$startLine = $tokens->currentTokenLine();
+
+		if (!$tokens->tryConsumeTokenType($endToken)) {
+			do {
+				$items[] = $this->parseArrayItem($tokens);
+			} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA) && !$tokens->isCurrentTokenType($endToken));
+			$tokens->consumeTokenType($endToken);
+		}
+
+		return $this->enrichWithAttributes(
+			$tokens,
+			new Ast\ConstExpr\ConstExprArrayNode($items),
+			$startLine,
+			$startIndex
+		);
+	}
+
+
+	/**
+	 * This method is supposed to be called with TokenIterator after reading TOKEN_DOUBLE_QUOTED_STRING and shifting
+	 * to the next token.
+	 */
+	public function parseDoctrineString(string $text, TokenIterator $tokens): Ast\ConstExpr\DoctrineConstExprStringNode
+	{
+		// Because of how Lexer works, a valid Doctrine string
+		// can consist of a sequence of TOKEN_DOUBLE_QUOTED_STRING and TOKEN_DOCTRINE_ANNOTATION_STRING
+		while ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING, Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
+			$text .= $tokens->currentTokenValue();
+			$tokens->next();
+		}
+
+		return new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($text));
+	}
+
+
+	private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		$expr = $this->parse($tokens);
+
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) {
+			$key = $expr;
+			$value = $this->parse($tokens);
+
+		} else {
+			$key = null;
+			$value = $expr;
+		}
+
+		return $this->enrichWithAttributes(
+			$tokens,
+			new Ast\ConstExpr\ConstExprArrayItemNode($key, $value),
+			$startLine,
+			$startIndex
+		);
+	}
+
+	/**
+	 * @template T of Ast\ConstExpr\ConstExprNode
+	 * @param T $node
+	 * @return T
+	 */
+	private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex): Ast\ConstExpr\ConstExprNode
+	{
+		if ($this->useLinesAttributes) {
+			$node->setAttribute(Ast\Attribute::START_LINE, $startLine);
+			$node->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
+		}
+
+		if ($this->useIndexAttributes) {
+			$node->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
+			$node->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
+		}
+
+		return $node;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php b/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php
new file mode 100644
index 0000000..6ab5cc0
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php
@@ -0,0 +1,106 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Parser;
+
+use Exception;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use function assert;
+use function json_encode;
+use function sprintf;
+use const JSON_INVALID_UTF8_SUBSTITUTE;
+use const JSON_UNESCAPED_SLASHES;
+use const JSON_UNESCAPED_UNICODE;
+
+class ParserException extends Exception
+{
+
+	/** @var string */
+	private $currentTokenValue;
+
+	/** @var int */
+	private $currentTokenType;
+
+	/** @var int */
+	private $currentOffset;
+
+	/** @var int */
+	private $expectedTokenType;
+
+	/** @var string|null */
+	private $expectedTokenValue;
+
+	/** @var int|null */
+	private $currentTokenLine;
+
+	public function __construct(
+		string $currentTokenValue,
+		int $currentTokenType,
+		int $currentOffset,
+		int $expectedTokenType,
+		?string $expectedTokenValue = null,
+		?int $currentTokenLine = null
+	)
+	{
+		$this->currentTokenValue = $currentTokenValue;
+		$this->currentTokenType = $currentTokenType;
+		$this->currentOffset = $currentOffset;
+		$this->expectedTokenType = $expectedTokenType;
+		$this->expectedTokenValue = $expectedTokenValue;
+		$this->currentTokenLine = $currentTokenLine;
+
+		parent::__construct(sprintf(
+			'Unexpected token %s, expected %s%s at offset %d%s',
+			$this->formatValue($currentTokenValue),
+			Lexer::TOKEN_LABELS[$expectedTokenType],
+			$expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '',
+			$currentOffset,
+			$currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine)
+		));
+	}
+
+
+	public function getCurrentTokenValue(): string
+	{
+		return $this->currentTokenValue;
+	}
+
+
+	public function getCurrentTokenType(): int
+	{
+		return $this->currentTokenType;
+	}
+
+
+	public function getCurrentOffset(): int
+	{
+		return $this->currentOffset;
+	}
+
+
+	public function getExpectedTokenType(): int
+	{
+		return $this->expectedTokenType;
+	}
+
+
+	public function getExpectedTokenValue(): ?string
+	{
+		return $this->expectedTokenValue;
+	}
+
+
+	public function getCurrentTokenLine(): ?int
+	{
+		return $this->currentTokenLine;
+	}
+
+
+	private function formatValue(string $value): string
+	{
+		$json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
+		assert($json !== false);
+
+		return $json;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php b/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php
new file mode 100644
index 0000000..f6ef5db
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php
@@ -0,0 +1,1289 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Parser;
+
+use LogicException;
+use PHPStan\PhpDocParser\Ast;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\ShouldNotHappenException;
+use function array_key_exists;
+use function array_values;
+use function count;
+use function rtrim;
+use function str_replace;
+use function trim;
+
+/**
+ * @phpstan-import-type ValueType from Doctrine\DoctrineArgument as DoctrineValueType
+ */
+class PhpDocParser
+{
+
+	private const DISALLOWED_DESCRIPTION_START_TOKENS = [
+		Lexer::TOKEN_UNION,
+		Lexer::TOKEN_INTERSECTION,
+	];
+
+	/** @var TypeParser */
+	private $typeParser;
+
+	/** @var ConstExprParser */
+	private $constantExprParser;
+
+	/** @var ConstExprParser */
+	private $doctrineConstantExprParser;
+
+	/** @var bool */
+	private $requireWhitespaceBeforeDescription;
+
+	/** @var bool */
+	private $preserveTypeAliasesWithInvalidTypes;
+
+	/** @var bool */
+	private $parseDoctrineAnnotations;
+
+	/** @var bool */
+	private $useLinesAttributes;
+
+	/** @var bool */
+	private $useIndexAttributes;
+
+	/** @var bool */
+	private $textBetweenTagsBelongsToDescription;
+
+	/**
+	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
+	 */
+	public function __construct(
+		TypeParser $typeParser,
+		ConstExprParser $constantExprParser,
+		bool $requireWhitespaceBeforeDescription = false,
+		bool $preserveTypeAliasesWithInvalidTypes = false,
+		array $usedAttributes = [],
+		bool $parseDoctrineAnnotations = false,
+		bool $textBetweenTagsBelongsToDescription = false
+	)
+	{
+		$this->typeParser = $typeParser;
+		$this->constantExprParser = $constantExprParser;
+		$this->doctrineConstantExprParser = $constantExprParser->toDoctrine();
+		$this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription;
+		$this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes;
+		$this->parseDoctrineAnnotations = $parseDoctrineAnnotations;
+		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
+		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
+		$this->textBetweenTagsBelongsToDescription = $textBetweenTagsBelongsToDescription;
+	}
+
+
+	public function parse(TokenIterator $tokens): Ast\PhpDoc\PhpDocNode
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PHPDOC);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$children = [];
+
+		if ($this->parseDoctrineAnnotations) {
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+				$lastChild = $this->parseChild($tokens);
+				$children[] = $lastChild;
+				while (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+					if (
+						$lastChild instanceof Ast\PhpDoc\PhpDocTagNode
+						&& (
+							$lastChild->value instanceof Doctrine\DoctrineTagValueNode
+							|| $lastChild->value instanceof Ast\PhpDoc\GenericTagValueNode
+						)
+					) {
+						$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+						if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+							break;
+						}
+						$lastChild = $this->parseChild($tokens);
+						$children[] = $lastChild;
+						continue;
+					}
+
+					if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+						break;
+					}
+					if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+						break;
+					}
+
+					$lastChild = $this->parseChild($tokens);
+					$children[] = $lastChild;
+				}
+			}
+		} else {
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+				$children[] = $this->parseChild($tokens);
+				while ($tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+					$children[] = $this->parseChild($tokens);
+				}
+			}
+		}
+
+		try {
+			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC);
+		} catch (ParserException $e) {
+			$name = '';
+			$startLine = $tokens->currentTokenLine();
+			$startIndex = $tokens->currentTokenIndex();
+			if (count($children) > 0) {
+				$lastChild = $children[count($children) - 1];
+				if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode) {
+					$name = $lastChild->name;
+					$startLine = $tokens->currentTokenLine();
+					$startIndex = $tokens->currentTokenIndex();
+				}
+			}
+
+			$tag = new Ast\PhpDoc\PhpDocTagNode(
+				$name,
+				$this->enrichWithAttributes(
+					$tokens,
+					new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e),
+					$startLine,
+					$startIndex
+				)
+			);
+
+			$tokens->forwardToTheEnd();
+
+			return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0);
+		}
+
+		return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode(array_values($children)), 1, 0);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode
+	{
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
+			$startLine = $tokens->currentTokenLine();
+			$startIndex = $tokens->currentTokenIndex();
+			return $this->enrichWithAttributes($tokens, $this->parseTag($tokens), $startLine, $startIndex);
+		}
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_TAG)) {
+			$startLine = $tokens->currentTokenLine();
+			$startIndex = $tokens->currentTokenIndex();
+			$tag = $tokens->currentTokenValue();
+			$tokens->next();
+
+			$tagStartLine = $tokens->currentTokenLine();
+			$tagStartIndex = $tokens->currentTokenIndex();
+
+			return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocTagNode(
+				$tag,
+				$this->enrichWithAttributes(
+					$tokens,
+					$this->parseDoctrineTagValue($tokens, $tag),
+					$tagStartLine,
+					$tagStartIndex
+				)
+			), $startLine, $startIndex);
+		}
+
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		$text = $this->parseText($tokens);
+
+		return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex);
+	}
+
+	/**
+	 * @template T of Ast\Node
+	 * @param T $tag
+	 * @return T
+	 */
+	private function enrichWithAttributes(TokenIterator $tokens, Ast\Node $tag, int $startLine, int $startIndex): Ast\Node
+	{
+		if ($this->useLinesAttributes) {
+			$tag->setAttribute(Ast\Attribute::START_LINE, $startLine);
+			$tag->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
+		}
+
+		if ($this->useIndexAttributes) {
+			$tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
+			$tag->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
+		}
+
+		return $tag;
+	}
+
+
+	private function parseText(TokenIterator $tokens): Ast\PhpDoc\PhpDocTextNode
+	{
+		$text = '';
+
+		$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
+		if ($this->textBetweenTagsBelongsToDescription) {
+			$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
+		}
+
+		$savepoint = false;
+
+		// if the next token is EOL, everything below is skipped and empty string is returned
+		while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+			$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
+			$text .= $tmpText;
+
+			// stop if we're not at EOL - meaning it's the end of PHPDoc
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC)) {
+				break;
+			}
+
+			if ($this->textBetweenTagsBelongsToDescription) {
+				if (!$savepoint) {
+					$tokens->pushSavePoint();
+					$savepoint = true;
+				} elseif ($tmpText !== '') {
+					$tokens->dropSavePoint();
+					$tokens->pushSavePoint();
+				}
+			}
+
+			$tokens->pushSavePoint();
+			$tokens->next();
+
+			// if we're at EOL, check what's next
+			// if next is a PHPDoc tag, EOL, or end of PHPDoc, stop
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) {
+				$tokens->rollback();
+				break;
+			}
+
+			// otherwise if the next is text, continue building the description string
+
+			$tokens->dropSavePoint();
+			$text .= $tokens->getDetectedNewline() ?? "\n";
+		}
+
+		if ($savepoint) {
+			$tokens->rollback();
+			$text = rtrim($text, $tokens->getDetectedNewline() ?? "\n");
+		}
+
+		return new Ast\PhpDoc\PhpDocTextNode(trim($text, " \t"));
+	}
+
+
+	private function parseOptionalDescriptionAfterDoctrineTag(TokenIterator $tokens): string
+	{
+		$text = '';
+
+		$endTokens = [Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
+		if ($this->textBetweenTagsBelongsToDescription) {
+			$endTokens = [Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END];
+		}
+
+		$savepoint = false;
+
+		// if the next token is EOL, everything below is skipped and empty string is returned
+		while ($this->textBetweenTagsBelongsToDescription || !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+			$tmpText = $tokens->getSkippedHorizontalWhiteSpaceIfAny() . $tokens->joinUntil(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, Lexer::TOKEN_PHPDOC_EOL, ...$endTokens);
+			$text .= $tmpText;
+
+			// stop if we're not at EOL - meaning it's the end of PHPDoc
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC)) {
+				if (!$tokens->isPrecededByHorizontalWhitespace()) {
+					return trim($text . $this->parseText($tokens)->text, " \t");
+				}
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) {
+					$tokens->pushSavePoint();
+					$child = $this->parseChild($tokens);
+					if ($child instanceof Ast\PhpDoc\PhpDocTagNode) {
+						if (
+							$child->value instanceof Ast\PhpDoc\GenericTagValueNode
+							|| $child->value instanceof Doctrine\DoctrineTagValueNode
+						) {
+							$tokens->rollback();
+							break;
+						}
+						if ($child->value instanceof Ast\PhpDoc\InvalidTagValueNode) {
+							$tokens->rollback();
+							$tokens->pushSavePoint();
+							$tokens->next();
+							if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+								$tokens->rollback();
+								break;
+							}
+							$tokens->rollback();
+							return trim($text . $this->parseText($tokens)->text, " \t");
+						}
+					}
+
+					$tokens->rollback();
+					return trim($text . $this->parseText($tokens)->text, " \t");
+				}
+				break;
+			}
+
+			if ($this->textBetweenTagsBelongsToDescription) {
+				if (!$savepoint) {
+					$tokens->pushSavePoint();
+					$savepoint = true;
+				} elseif ($tmpText !== '') {
+					$tokens->dropSavePoint();
+					$tokens->pushSavePoint();
+				}
+			}
+
+			$tokens->pushSavePoint();
+			$tokens->next();
+
+			// if we're at EOL, check what's next
+			// if next is a PHPDoc tag, EOL, or end of PHPDoc, stop
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG, ...$endTokens)) {
+				$tokens->rollback();
+				break;
+			}
+
+			// otherwise if the next is text, continue building the description string
+
+			$tokens->dropSavePoint();
+			$text .= $tokens->getDetectedNewline() ?? "\n";
+		}
+
+		if ($savepoint) {
+			$tokens->rollback();
+			$text = rtrim($text, $tokens->getDetectedNewline() ?? "\n");
+		}
+
+		return trim($text, " \t");
+	}
+
+
+	public function parseTag(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagNode
+	{
+		$tag = $tokens->currentTokenValue();
+		$tokens->next();
+		$value = $this->parseTagValue($tokens, $tag);
+
+		return new Ast\PhpDoc\PhpDocTagNode($tag, $value);
+	}
+
+
+	public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\PhpDocTagValueNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		try {
+			$tokens->pushSavePoint();
+
+			switch ($tag) {
+				case '@param':
+				case '@phpstan-param':
+				case '@psalm-param':
+				case '@phan-param':
+					$tagValue = $this->parseParamTagValue($tokens);
+					break;
+
+				case '@param-immediately-invoked-callable':
+				case '@phpstan-param-immediately-invoked-callable':
+					$tagValue = $this->parseParamImmediatelyInvokedCallableTagValue($tokens);
+					break;
+
+				case '@param-later-invoked-callable':
+				case '@phpstan-param-later-invoked-callable':
+					$tagValue = $this->parseParamLaterInvokedCallableTagValue($tokens);
+					break;
+
+				case '@param-closure-this':
+				case '@phpstan-param-closure-this':
+					$tagValue = $this->parseParamClosureThisTagValue($tokens);
+					break;
+
+				case '@var':
+				case '@phpstan-var':
+				case '@psalm-var':
+				case '@phan-var':
+					$tagValue = $this->parseVarTagValue($tokens);
+					break;
+
+				case '@return':
+				case '@phpstan-return':
+				case '@psalm-return':
+				case '@phan-return':
+				case '@phan-real-return':
+					$tagValue = $this->parseReturnTagValue($tokens);
+					break;
+
+				case '@throws':
+				case '@phpstan-throws':
+					$tagValue = $this->parseThrowsTagValue($tokens);
+					break;
+
+				case '@mixin':
+				case '@phan-mixin':
+					$tagValue = $this->parseMixinTagValue($tokens);
+					break;
+
+				case '@psalm-require-extends':
+				case '@phpstan-require-extends':
+					$tagValue = $this->parseRequireExtendsTagValue($tokens);
+					break;
+
+				case '@psalm-require-implements':
+				case '@phpstan-require-implements':
+					$tagValue = $this->parseRequireImplementsTagValue($tokens);
+					break;
+
+				case '@deprecated':
+					$tagValue = $this->parseDeprecatedTagValue($tokens);
+					break;
+
+				case '@property':
+				case '@property-read':
+				case '@property-write':
+				case '@phpstan-property':
+				case '@phpstan-property-read':
+				case '@phpstan-property-write':
+				case '@psalm-property':
+				case '@psalm-property-read':
+				case '@psalm-property-write':
+				case '@phan-property':
+				case '@phan-property-read':
+				case '@phan-property-write':
+					$tagValue = $this->parsePropertyTagValue($tokens);
+					break;
+
+				case '@method':
+				case '@phpstan-method':
+				case '@psalm-method':
+				case '@phan-method':
+					$tagValue = $this->parseMethodTagValue($tokens);
+					break;
+
+				case '@template':
+				case '@phpstan-template':
+				case '@psalm-template':
+				case '@phan-template':
+				case '@template-covariant':
+				case '@phpstan-template-covariant':
+				case '@psalm-template-covariant':
+				case '@template-contravariant':
+				case '@phpstan-template-contravariant':
+				case '@psalm-template-contravariant':
+					$tagValue = $this->typeParser->parseTemplateTagValue(
+						$tokens,
+						function ($tokens) {
+							return $this->parseOptionalDescription($tokens);
+						}
+					);
+					break;
+
+				case '@extends':
+				case '@phpstan-extends':
+				case '@phan-extends':
+				case '@phan-inherits':
+				case '@template-extends':
+					$tagValue = $this->parseExtendsTagValue('@extends', $tokens);
+					break;
+
+				case '@implements':
+				case '@phpstan-implements':
+				case '@template-implements':
+					$tagValue = $this->parseExtendsTagValue('@implements', $tokens);
+					break;
+
+				case '@use':
+				case '@phpstan-use':
+				case '@template-use':
+					$tagValue = $this->parseExtendsTagValue('@use', $tokens);
+					break;
+
+				case '@phpstan-type':
+				case '@psalm-type':
+				case '@phan-type':
+					$tagValue = $this->parseTypeAliasTagValue($tokens);
+					break;
+
+				case '@phpstan-import-type':
+				case '@psalm-import-type':
+					$tagValue = $this->parseTypeAliasImportTagValue($tokens);
+					break;
+
+				case '@phpstan-assert':
+				case '@phpstan-assert-if-true':
+				case '@phpstan-assert-if-false':
+				case '@psalm-assert':
+				case '@psalm-assert-if-true':
+				case '@psalm-assert-if-false':
+				case '@phan-assert':
+				case '@phan-assert-if-true':
+				case '@phan-assert-if-false':
+					$tagValue = $this->parseAssertTagValue($tokens);
+					break;
+
+				case '@phpstan-this-out':
+				case '@phpstan-self-out':
+				case '@psalm-this-out':
+				case '@psalm-self-out':
+					$tagValue = $this->parseSelfOutTagValue($tokens);
+					break;
+
+				case '@param-out':
+				case '@phpstan-param-out':
+				case '@psalm-param-out':
+					$tagValue = $this->parseParamOutTagValue($tokens);
+					break;
+
+				default:
+					if ($this->parseDoctrineAnnotations) {
+						if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+							$tagValue = $this->parseDoctrineTagValue($tokens, $tag);
+						} else {
+							$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescriptionAfterDoctrineTag($tokens));
+						}
+						break;
+					}
+
+					$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
+
+					break;
+			}
+
+			$tokens->dropSavePoint();
+
+		} catch (ParserException $e) {
+			$tokens->rollback();
+			$tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e);
+		}
+
+		return $this->enrichWithAttributes($tokens, $tagValue, $startLine, $startIndex);
+	}
+
+
+	private function parseDoctrineTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\PhpDocTagValueNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		return new Doctrine\DoctrineTagValueNode(
+			$this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineAnnotation($tag, $this->parseDoctrineArguments($tokens, false)),
+				$startLine,
+				$startIndex
+			),
+			$this->parseOptionalDescriptionAfterDoctrineTag($tokens)
+		);
+	}
+
+
+	/**
+	 * @return list<Doctrine\DoctrineArgument>
+	 */
+	private function parseDoctrineArguments(TokenIterator $tokens, bool $deep): array
+	{
+		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+			return [];
+		}
+
+		if (!$deep) {
+			$tokens->addEndOfLineToSkippedTokens();
+		}
+
+		$arguments = [];
+
+		try {
+			$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
+
+			do {
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
+					break;
+				}
+				$arguments[] = $this->parseDoctrineArgument($tokens);
+			} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
+		} finally {
+			if (!$deep) {
+				$tokens->removeEndOfLineFromSkippedTokens();
+			}
+		}
+
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
+
+		return $arguments;
+	}
+
+
+	private function parseDoctrineArgument(TokenIterator $tokens): Doctrine\DoctrineArgument
+	{
+		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
+			$startLine = $tokens->currentTokenLine();
+			$startIndex = $tokens->currentTokenIndex();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)),
+				$startLine,
+				$startIndex
+			);
+		}
+
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		try {
+			$tokens->pushSavePoint();
+			$currentValue = $tokens->currentTokenValue();
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+
+			$key = $this->enrichWithAttributes(
+				$tokens,
+				new IdentifierTypeNode($currentValue),
+				$startLine,
+				$startIndex
+			);
+			$tokens->consumeTokenType(Lexer::TOKEN_EQUAL);
+
+			$value = $this->parseDoctrineArgumentValue($tokens);
+
+			$tokens->dropSavePoint();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineArgument($key, $value),
+				$startLine,
+				$startIndex
+			);
+		} catch (ParserException $e) {
+			$tokens->rollback();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineArgument(null, $this->parseDoctrineArgumentValue($tokens)),
+				$startLine,
+				$startIndex
+			);
+		}
+	}
+
+
+	/**
+	 * @return DoctrineValueType
+	 */
+	private function parseDoctrineArgumentValue(TokenIterator $tokens)
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG, Lexer::TOKEN_DOCTRINE_TAG)) {
+			$name = $tokens->currentTokenValue();
+			$tokens->next();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineAnnotation($name, $this->parseDoctrineArguments($tokens, true)),
+				$startLine,
+				$startIndex
+			);
+		}
+
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET)) {
+			$items = [];
+			do {
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
+					break;
+				}
+				$items[] = $this->parseDoctrineArrayItem($tokens);
+			} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
+
+			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineArray($items),
+				$startLine,
+				$startIndex
+			);
+		}
+
+		$currentTokenValue = $tokens->currentTokenValue();
+		$tokens->pushSavePoint(); // because of ConstFetchNode
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
+			$identifier = $this->enrichWithAttributes(
+				$tokens,
+				new Ast\Type\IdentifierTypeNode($currentTokenValue),
+				$startLine,
+				$startIndex
+			);
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
+				$tokens->dropSavePoint();
+				return $identifier;
+			}
+
+			$tokens->rollback(); // because of ConstFetchNode
+		} else {
+			$tokens->dropSavePoint(); // because of ConstFetchNode
+		}
+
+		$currentTokenValue = $tokens->currentTokenValue();
+		$currentTokenType = $tokens->currentTokenType();
+		$currentTokenOffset = $tokens->currentTokenOffset();
+		$currentTokenLine = $tokens->currentTokenLine();
+
+		try {
+			$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
+			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
+				throw new ParserException(
+					$currentTokenValue,
+					$currentTokenType,
+					$currentTokenOffset,
+					Lexer::TOKEN_IDENTIFIER,
+					null,
+					$currentTokenLine
+				);
+			}
+
+			return $constExpr;
+		} catch (LogicException $e) {
+			throw new ParserException(
+				$currentTokenValue,
+				$currentTokenType,
+				$currentTokenOffset,
+				Lexer::TOKEN_IDENTIFIER,
+				null,
+				$currentTokenLine
+			);
+		}
+	}
+
+
+	private function parseDoctrineArrayItem(TokenIterator $tokens): Doctrine\DoctrineArrayItem
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		try {
+			$tokens->pushSavePoint();
+
+			$key = $this->parseDoctrineArrayKey($tokens);
+			if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) {
+				if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_COLON)) {
+					$tokens->consumeTokenType(Lexer::TOKEN_EQUAL); // will throw exception
+				}
+			}
+
+			$value = $this->parseDoctrineArgumentValue($tokens);
+
+			$tokens->dropSavePoint();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineArrayItem($key, $value),
+				$startLine,
+				$startIndex
+			);
+		} catch (ParserException $e) {
+			$tokens->rollback();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Doctrine\DoctrineArrayItem(null, $this->parseDoctrineArgumentValue($tokens)),
+				$startLine,
+				$startIndex
+			);
+		}
+	}
+
+
+	/**
+	 * @return ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|ConstFetchNode
+	 */
+	private function parseDoctrineArrayKey(TokenIterator $tokens)
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
+			$key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue()));
+			$tokens->next();
+
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOCTRINE_ANNOTATION_STRING)) {
+			$key = new Ast\ConstExpr\DoctrineConstExprStringNode(Ast\ConstExpr\DoctrineConstExprStringNode::unescape($tokens->currentTokenValue()));
+
+			$tokens->next();
+
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
+			$value = $tokens->currentTokenValue();
+			$tokens->next();
+			$key = $this->doctrineConstantExprParser->parseDoctrineString($value, $tokens);
+
+		} else {
+			$currentTokenValue = $tokens->currentTokenValue();
+			$tokens->pushSavePoint(); // because of ConstFetchNode
+			if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
+				$tokens->dropSavePoint();
+				throw new ParserException(
+					$tokens->currentTokenValue(),
+					$tokens->currentTokenType(),
+					$tokens->currentTokenOffset(),
+					Lexer::TOKEN_IDENTIFIER,
+					null,
+					$tokens->currentTokenLine()
+				);
+			}
+
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
+				$tokens->dropSavePoint();
+
+				return $this->enrichWithAttributes(
+					$tokens,
+					new IdentifierTypeNode($currentTokenValue),
+					$startLine,
+					$startIndex
+				);
+			}
+
+			$tokens->rollback();
+			$constExpr = $this->doctrineConstantExprParser->parse($tokens, true);
+			if (!$constExpr instanceof Ast\ConstExpr\ConstFetchNode) {
+				throw new ParserException(
+					$tokens->currentTokenValue(),
+					$tokens->currentTokenType(),
+					$tokens->currentTokenOffset(),
+					Lexer::TOKEN_IDENTIFIER,
+					null,
+					$tokens->currentTokenLine()
+				);
+			}
+
+			return $constExpr;
+		}
+
+		return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex);
+	}
+
+
+	/**
+	 * @return Ast\PhpDoc\ParamTagValueNode|Ast\PhpDoc\TypelessParamTagValueNode
+	 */
+	private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
+	{
+		if (
+			$tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE, Lexer::TOKEN_VARIADIC, Lexer::TOKEN_VARIABLE)
+		) {
+			$type = null;
+		} else {
+			$type = $this->typeParser->parse($tokens);
+		}
+
+		$isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE);
+		$isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC);
+		$parameterName = $this->parseRequiredVariableName($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+
+		if ($type !== null) {
+			return new Ast\PhpDoc\ParamTagValueNode($type, $isVariadic, $parameterName, $description, $isReference);
+		}
+
+		return new Ast\PhpDoc\TypelessParamTagValueNode($isVariadic, $parameterName, $description, $isReference);
+	}
+
+
+	private function parseParamImmediatelyInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode
+	{
+		$parameterName = $this->parseRequiredVariableName($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+
+		return new Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode($parameterName, $description);
+	}
+
+
+	private function parseParamLaterInvokedCallableTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode
+	{
+		$parameterName = $this->parseRequiredVariableName($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+
+		return new Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode($parameterName, $description);
+	}
+
+
+	private function parseParamClosureThisTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamClosureThisTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$parameterName = $this->parseRequiredVariableName($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+
+		return new Ast\PhpDoc\ParamClosureThisTagValueNode($type, $parameterName, $description);
+	}
+
+
+	private function parseVarTagValue(TokenIterator $tokens): Ast\PhpDoc\VarTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$variableName = $this->parseOptionalVariableName($tokens);
+		$description = $this->parseOptionalDescription($tokens, $variableName === '');
+		return new Ast\PhpDoc\VarTagValueNode($type, $variableName, $description);
+	}
+
+
+	private function parseReturnTagValue(TokenIterator $tokens): Ast\PhpDoc\ReturnTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
+		return new Ast\PhpDoc\ReturnTagValueNode($type, $description);
+	}
+
+
+	private function parseThrowsTagValue(TokenIterator $tokens): Ast\PhpDoc\ThrowsTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
+		return new Ast\PhpDoc\ThrowsTagValueNode($type, $description);
+	}
+
+	private function parseMixinTagValue(TokenIterator $tokens): Ast\PhpDoc\MixinTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
+		return new Ast\PhpDoc\MixinTagValueNode($type, $description);
+	}
+
+	private function parseRequireExtendsTagValue(TokenIterator $tokens): Ast\PhpDoc\RequireExtendsTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
+		return new Ast\PhpDoc\RequireExtendsTagValueNode($type, $description);
+	}
+
+	private function parseRequireImplementsTagValue(TokenIterator $tokens): Ast\PhpDoc\RequireImplementsTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$description = $this->parseOptionalDescription($tokens, true);
+		return new Ast\PhpDoc\RequireImplementsTagValueNode($type, $description);
+	}
+
+	private function parseDeprecatedTagValue(TokenIterator $tokens): Ast\PhpDoc\DeprecatedTagValueNode
+	{
+		$description = $this->parseOptionalDescription($tokens);
+		return new Ast\PhpDoc\DeprecatedTagValueNode($description);
+	}
+
+
+	private function parsePropertyTagValue(TokenIterator $tokens): Ast\PhpDoc\PropertyTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$parameterName = $this->parseRequiredVariableName($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+		return new Ast\PhpDoc\PropertyTagValueNode($type, $parameterName, $description);
+	}
+
+
+	private function parseMethodTagValue(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueNode
+	{
+		$staticKeywordOrReturnTypeOrMethodName = $this->typeParser->parse($tokens);
+
+		if ($staticKeywordOrReturnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode && $staticKeywordOrReturnTypeOrMethodName->name === 'static') {
+			$isStatic = true;
+			$returnTypeOrMethodName = $this->typeParser->parse($tokens);
+
+		} else {
+			$isStatic = false;
+			$returnTypeOrMethodName = $staticKeywordOrReturnTypeOrMethodName;
+		}
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
+			$returnType = $returnTypeOrMethodName;
+			$methodName = $tokens->currentTokenValue();
+			$tokens->next();
+
+		} elseif ($returnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode) {
+			$returnType = $isStatic ? $staticKeywordOrReturnTypeOrMethodName : null;
+			$methodName = $returnTypeOrMethodName->name;
+			$isStatic = false;
+
+		} else {
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); // will throw exception
+			exit;
+		}
+
+		$templateTypes = [];
+
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
+			do {
+				$startLine = $tokens->currentTokenLine();
+				$startIndex = $tokens->currentTokenIndex();
+				$templateTypes[] = $this->enrichWithAttributes(
+					$tokens,
+					$this->typeParser->parseTemplateTagValue($tokens),
+					$startLine,
+					$startIndex
+				);
+			} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
+			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
+		}
+
+		$parameters = [];
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
+		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
+			$parameters[] = $this->parseMethodTagValueParameter($tokens);
+			while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
+				$parameters[] = $this->parseMethodTagValueParameter($tokens);
+			}
+		}
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
+
+		$description = $this->parseOptionalDescription($tokens);
+		return new Ast\PhpDoc\MethodTagValueNode($isStatic, $returnType, $methodName, $parameters, $description, $templateTypes);
+	}
+
+	private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueParameterNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		switch ($tokens->currentTokenType()) {
+			case Lexer::TOKEN_IDENTIFIER:
+			case Lexer::TOKEN_OPEN_PARENTHESES:
+			case Lexer::TOKEN_NULLABLE:
+				$parameterType = $this->typeParser->parse($tokens);
+				break;
+
+			default:
+				$parameterType = null;
+		}
+
+		$isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE);
+		$isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC);
+
+		$parameterName = $tokens->currentTokenValue();
+		$tokens->consumeTokenType(Lexer::TOKEN_VARIABLE);
+
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL)) {
+			$defaultValue = $this->constantExprParser->parse($tokens);
+
+		} else {
+			$defaultValue = null;
+		}
+
+		return $this->enrichWithAttributes(
+			$tokens,
+			new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue),
+			$startLine,
+			$startIndex
+		);
+	}
+
+	private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		$baseType = new IdentifierTypeNode($tokens->currentTokenValue());
+		$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+
+		$type = $this->typeParser->parseGeneric(
+			$tokens,
+			$this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex)
+		);
+
+		$description = $this->parseOptionalDescription($tokens);
+
+		switch ($tagName) {
+			case '@extends':
+				return new Ast\PhpDoc\ExtendsTagValueNode($type, $description);
+			case '@implements':
+				return new Ast\PhpDoc\ImplementsTagValueNode($type, $description);
+			case '@use':
+				return new Ast\PhpDoc\UsesTagValueNode($type, $description);
+		}
+
+		throw new ShouldNotHappenException();
+	}
+
+	private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeAliasTagValueNode
+	{
+		$alias = $tokens->currentTokenValue();
+		$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+
+		// support phan-type/psalm-type syntax
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
+
+		if ($this->preserveTypeAliasesWithInvalidTypes) {
+			$startLine = $tokens->currentTokenLine();
+			$startIndex = $tokens->currentTokenIndex();
+			try {
+				$type = $this->typeParser->parse($tokens);
+				if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) {
+					if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) {
+						throw new ParserException(
+							$tokens->currentTokenValue(),
+							$tokens->currentTokenType(),
+							$tokens->currentTokenOffset(),
+							Lexer::TOKEN_PHPDOC_EOL,
+							null,
+							$tokens->currentTokenLine()
+						);
+					}
+				}
+
+				return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
+			} catch (ParserException $e) {
+				$this->parseOptionalDescription($tokens);
+				return new Ast\PhpDoc\TypeAliasTagValueNode(
+					$alias,
+					$this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex)
+				);
+			}
+		}
+
+		$type = $this->typeParser->parse($tokens);
+
+		return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
+	}
+
+	private function parseTypeAliasImportTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeAliasImportTagValueNode
+	{
+		$importedAlias = $tokens->currentTokenValue();
+		$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+
+		$tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'from');
+
+		$identifierStartLine = $tokens->currentTokenLine();
+		$identifierStartIndex = $tokens->currentTokenIndex();
+		$importedFrom = $tokens->currentTokenValue();
+		$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+		$importedFromType = $this->enrichWithAttributes(
+			$tokens,
+			new IdentifierTypeNode($importedFrom),
+			$identifierStartLine,
+			$identifierStartIndex
+		);
+
+		$importedAs = null;
+		if ($tokens->tryConsumeTokenValue('as')) {
+			$importedAs = $tokens->currentTokenValue();
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+		}
+
+		return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, $importedFromType, $importedAs);
+	}
+
+	/**
+	 * @return Ast\PhpDoc\AssertTagValueNode|Ast\PhpDoc\AssertTagPropertyValueNode|Ast\PhpDoc\AssertTagMethodValueNode
+	 */
+	private function parseAssertTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
+	{
+		$isNegated = $tokens->tryConsumeTokenType(Lexer::TOKEN_NEGATED);
+		$isEquality = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
+		$type = $this->typeParser->parse($tokens);
+		$parameter = $this->parseAssertParameter($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+
+		if (array_key_exists('method', $parameter)) {
+			return new Ast\PhpDoc\AssertTagMethodValueNode($type, $parameter['parameter'], $parameter['method'], $isNegated, $description, $isEquality);
+		} elseif (array_key_exists('property', $parameter)) {
+			return new Ast\PhpDoc\AssertTagPropertyValueNode($type, $parameter['parameter'], $parameter['property'], $isNegated, $description, $isEquality);
+		}
+
+		return new Ast\PhpDoc\AssertTagValueNode($type, $parameter['parameter'], $isNegated, $description, $isEquality);
+	}
+
+	/**
+	 * @return array{parameter: string}|array{parameter: string, property: string}|array{parameter: string, method: string}
+	 */
+	private function parseAssertParameter(TokenIterator $tokens): array
+	{
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
+			$parameter = '$this';
+			$tokens->next();
+		} else {
+			$parameter = $tokens->currentTokenValue();
+			$tokens->consumeTokenType(Lexer::TOKEN_VARIABLE);
+		}
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_ARROW)) {
+			$tokens->consumeTokenType(Lexer::TOKEN_ARROW);
+
+			$propertyOrMethod = $tokens->currentTokenValue();
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+
+			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+				$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
+
+				return ['parameter' => $parameter, 'method' => $propertyOrMethod];
+			}
+
+			return ['parameter' => $parameter, 'property' => $propertyOrMethod];
+		}
+
+		return ['parameter' => $parameter];
+	}
+
+	private function parseSelfOutTagValue(TokenIterator $tokens): Ast\PhpDoc\SelfOutTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+
+		return new Ast\PhpDoc\SelfOutTagValueNode($type, $description);
+	}
+
+	private function parseParamOutTagValue(TokenIterator $tokens): Ast\PhpDoc\ParamOutTagValueNode
+	{
+		$type = $this->typeParser->parse($tokens);
+		$parameterName = $this->parseRequiredVariableName($tokens);
+		$description = $this->parseOptionalDescription($tokens);
+
+		return new Ast\PhpDoc\ParamOutTagValueNode($type, $parameterName, $description);
+	}
+
+	private function parseOptionalVariableName(TokenIterator $tokens): string
+	{
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {
+			$parameterName = $tokens->currentTokenValue();
+			$tokens->next();
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
+			$parameterName = '$this';
+			$tokens->next();
+
+		} else {
+			$parameterName = '';
+		}
+
+		return $parameterName;
+	}
+
+
+	private function parseRequiredVariableName(TokenIterator $tokens): string
+	{
+		$parameterName = $tokens->currentTokenValue();
+		$tokens->consumeTokenType(Lexer::TOKEN_VARIABLE);
+
+		return $parameterName;
+	}
+
+	private function parseOptionalDescription(TokenIterator $tokens, bool $limitStartToken = false): string
+	{
+		if ($limitStartToken) {
+			foreach (self::DISALLOWED_DESCRIPTION_START_TOKENS as $disallowedStartToken) {
+				if (!$tokens->isCurrentTokenType($disallowedStartToken)) {
+					continue;
+				}
+
+				$tokens->consumeTokenType(Lexer::TOKEN_OTHER); // will throw exception
+			}
+
+			if (
+				$this->requireWhitespaceBeforeDescription
+				&& !$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL, Lexer::TOKEN_CLOSE_PHPDOC, Lexer::TOKEN_END)
+				&& !$tokens->isPrecededByHorizontalWhitespace()
+			) {
+				$tokens->consumeTokenType(Lexer::TOKEN_HORIZONTAL_WS); // will throw exception
+			}
+		}
+
+		return $this->parseText($tokens)->text;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php b/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php
new file mode 100644
index 0000000..a3bbeed
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php
@@ -0,0 +1,100 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Parser;
+
+use PHPStan\ShouldNotHappenException;
+use function chr;
+use function hexdec;
+use function octdec;
+use function preg_replace_callback;
+use function str_replace;
+use function substr;
+
+class StringUnescaper
+{
+
+	private const REPLACEMENTS = [
+		'\\' => '\\',
+		'n' => "\n",
+		'r' => "\r",
+		't' => "\t",
+		'f' => "\f",
+		'v' => "\v",
+		'e' => "\x1B",
+	];
+
+	public static function unescapeString(string $string): string
+	{
+		$quote = $string[0];
+
+		if ($quote === '\'') {
+			return str_replace(
+				['\\\\', '\\\''],
+				['\\', '\''],
+				substr($string, 1, -1)
+			);
+		}
+
+		return self::parseEscapeSequences(substr($string, 1, -1), '"');
+	}
+
+	/**
+	 * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130
+	 */
+	private static function parseEscapeSequences(string $str, string $quote): string
+	{
+		$str = str_replace('\\' . $quote, $quote, $str);
+
+		return preg_replace_callback(
+			'~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\{([0-9a-fA-F]+)\})~',
+			static function ($matches) {
+				$str = $matches[1];
+
+				if (isset(self::REPLACEMENTS[$str])) {
+					return self::REPLACEMENTS[$str];
+				}
+				if ($str[0] === 'x' || $str[0] === 'X') {
+					return chr((int) hexdec(substr($str, 1)));
+				}
+				if ($str[0] === 'u') {
+					if (!isset($matches[2])) {
+						throw new ShouldNotHappenException();
+					}
+					return self::codePointToUtf8((int) hexdec($matches[2]));
+				}
+
+				return chr((int) octdec($str));
+			},
+			$str
+		);
+	}
+
+	/**
+	 * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154
+	 */
+	private static function codePointToUtf8(int $num): string
+	{
+		if ($num <= 0x7F) {
+			return chr($num);
+		}
+		if ($num <= 0x7FF) {
+			return chr(($num >> 6) + 0xC0)
+				. chr(($num & 0x3F) + 0x80);
+		}
+		if ($num <= 0xFFFF) {
+			return chr(($num >> 12) + 0xE0)
+				. chr((($num >> 6) & 0x3F) + 0x80)
+				. chr(($num & 0x3F) + 0x80);
+		}
+		if ($num <= 0x1FFFFF) {
+			return chr(($num >> 18) + 0xF0)
+				. chr((($num >> 12) & 0x3F) + 0x80)
+				. chr((($num >> 6) & 0x3F) + 0x80)
+				. chr(($num & 0x3F) + 0x80);
+		}
+
+		// Invalid UTF-8 codepoint escape sequence: Codepoint too large
+		return "\xef\xbf\xbd";
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php b/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php
new file mode 100644
index 0000000..9be7593
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php
@@ -0,0 +1,383 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Parser;
+
+use LogicException;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use function array_pop;
+use function assert;
+use function count;
+use function in_array;
+use function strlen;
+use function substr;
+
+class TokenIterator
+{
+
+	/** @var list<array{string, int, int}> */
+	private $tokens;
+
+	/** @var int */
+	private $index;
+
+	/** @var int[] */
+	private $savePoints = [];
+
+	/** @var list<int> */
+	private $skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
+
+	/** @var string|null */
+	private $newline = null;
+
+	/**
+	 * @param list<array{string, int, int}> $tokens
+	 */
+	public function __construct(array $tokens, int $index = 0)
+	{
+		$this->tokens = $tokens;
+		$this->index = $index;
+
+		$this->skipIrrelevantTokens();
+	}
+
+
+	/**
+	 * @return list<array{string, int, int}>
+	 */
+	public function getTokens(): array
+	{
+		return $this->tokens;
+	}
+
+
+	public function getContentBetween(int $startPos, int $endPos): string
+	{
+		if ($startPos < 0 || $endPos > count($this->tokens)) {
+			throw new LogicException();
+		}
+
+		$content = '';
+		for ($i = $startPos; $i < $endPos; $i++) {
+			$content .= $this->tokens[$i][Lexer::VALUE_OFFSET];
+		}
+
+		return $content;
+	}
+
+
+	public function getTokenCount(): int
+	{
+		return count($this->tokens);
+	}
+
+
+	public function currentTokenValue(): string
+	{
+		return $this->tokens[$this->index][Lexer::VALUE_OFFSET];
+	}
+
+
+	public function currentTokenType(): int
+	{
+		return $this->tokens[$this->index][Lexer::TYPE_OFFSET];
+	}
+
+
+	public function currentTokenOffset(): int
+	{
+		$offset = 0;
+		for ($i = 0; $i < $this->index; $i++) {
+			$offset += strlen($this->tokens[$i][Lexer::VALUE_OFFSET]);
+		}
+
+		return $offset;
+	}
+
+
+	public function currentTokenLine(): int
+	{
+		return $this->tokens[$this->index][Lexer::LINE_OFFSET];
+	}
+
+
+	public function currentTokenIndex(): int
+	{
+		return $this->index;
+	}
+
+
+	public function endIndexOfLastRelevantToken(): int
+	{
+		$endIndex = $this->currentTokenIndex();
+		$endIndex--;
+		while (in_array($this->tokens[$endIndex][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, true)) {
+			if (!isset($this->tokens[$endIndex - 1])) {
+				break;
+			}
+			$endIndex--;
+		}
+
+		return $endIndex;
+	}
+
+
+	public function isCurrentTokenValue(string $tokenValue): bool
+	{
+		return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue;
+	}
+
+
+	public function isCurrentTokenType(int ...$tokenType): bool
+	{
+		return in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, true);
+	}
+
+
+	public function isPrecededByHorizontalWhitespace(): bool
+	{
+		return ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] ?? -1) === Lexer::TOKEN_HORIZONTAL_WS;
+	}
+
+
+	/**
+	 * @throws ParserException
+	 */
+	public function consumeTokenType(int $tokenType): void
+	{
+		if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) {
+			$this->throwError($tokenType);
+		}
+
+		if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) {
+			if ($this->newline === null) {
+				$this->detectNewline();
+			}
+		}
+
+		$this->index++;
+		$this->skipIrrelevantTokens();
+	}
+
+
+	/**
+	 * @throws ParserException
+	 */
+	public function consumeTokenValue(int $tokenType, string $tokenValue): void
+	{
+		if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType || $this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) {
+			$this->throwError($tokenType, $tokenValue);
+		}
+
+		$this->index++;
+		$this->skipIrrelevantTokens();
+	}
+
+
+	/** @phpstan-impure */
+	public function tryConsumeTokenValue(string $tokenValue): bool
+	{
+		if ($this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) {
+			return false;
+		}
+
+		$this->index++;
+		$this->skipIrrelevantTokens();
+
+		return true;
+	}
+
+
+	/** @phpstan-impure */
+	public function tryConsumeTokenType(int $tokenType): bool
+	{
+		if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) {
+			return false;
+		}
+
+		if ($tokenType === Lexer::TOKEN_PHPDOC_EOL) {
+			if ($this->newline === null) {
+				$this->detectNewline();
+			}
+		}
+
+		$this->index++;
+		$this->skipIrrelevantTokens();
+
+		return true;
+	}
+
+
+	private function detectNewline(): void
+	{
+		$value = $this->currentTokenValue();
+		if (substr($value, 0, 2) === "\r\n") {
+			$this->newline = "\r\n";
+		} elseif (substr($value, 0, 1) === "\n") {
+			$this->newline = "\n";
+		}
+	}
+
+
+	public function getSkippedHorizontalWhiteSpaceIfAny(): string
+	{
+		if ($this->index > 0 && $this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) {
+			return $this->tokens[$this->index - 1][Lexer::VALUE_OFFSET];
+		}
+
+		return '';
+	}
+
+
+	/** @phpstan-impure */
+	public function joinUntil(int ...$tokenType): string
+	{
+		$s = '';
+		while (!in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $tokenType, true)) {
+			$s .= $this->tokens[$this->index++][Lexer::VALUE_OFFSET];
+		}
+		return $s;
+	}
+
+
+	public function next(): void
+	{
+		$this->index++;
+		$this->skipIrrelevantTokens();
+	}
+
+
+	private function skipIrrelevantTokens(): void
+	{
+		if (!isset($this->tokens[$this->index])) {
+			return;
+		}
+
+		while (in_array($this->tokens[$this->index][Lexer::TYPE_OFFSET], $this->skippedTokenTypes, true)) {
+			if (!isset($this->tokens[$this->index + 1])) {
+				break;
+			}
+			$this->index++;
+		}
+	}
+
+
+	public function addEndOfLineToSkippedTokens(): void
+	{
+		$this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS, Lexer::TOKEN_PHPDOC_EOL];
+	}
+
+
+	public function removeEndOfLineFromSkippedTokens(): void
+	{
+		$this->skippedTokenTypes = [Lexer::TOKEN_HORIZONTAL_WS];
+	}
+
+	/** @phpstan-impure */
+	public function forwardToTheEnd(): void
+	{
+		$lastToken = count($this->tokens) - 1;
+		$this->index = $lastToken;
+	}
+
+
+	public function pushSavePoint(): void
+	{
+		$this->savePoints[] = $this->index;
+	}
+
+
+	public function dropSavePoint(): void
+	{
+		array_pop($this->savePoints);
+	}
+
+
+	public function rollback(): void
+	{
+		$index = array_pop($this->savePoints);
+		assert($index !== null);
+		$this->index = $index;
+	}
+
+
+	/**
+	 * @throws ParserException
+	 */
+	private function throwError(int $expectedTokenType, ?string $expectedTokenValue = null): void
+	{
+		throw new ParserException(
+			$this->currentTokenValue(),
+			$this->currentTokenType(),
+			$this->currentTokenOffset(),
+			$expectedTokenType,
+			$expectedTokenValue,
+			$this->currentTokenLine()
+		);
+	}
+
+	/**
+	 * Check whether the position is directly preceded by a certain token type.
+	 *
+	 * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped
+	 */
+	public function hasTokenImmediatelyBefore(int $pos, int $expectedTokenType): bool
+	{
+		$tokens = $this->tokens;
+		$pos--;
+		for (; $pos >= 0; $pos--) {
+			$token = $tokens[$pos];
+			$type = $token[Lexer::TYPE_OFFSET];
+			if ($type === $expectedTokenType) {
+				return true;
+			}
+			if (!in_array($type, [
+				Lexer::TOKEN_HORIZONTAL_WS,
+				Lexer::TOKEN_PHPDOC_EOL,
+			], true)) {
+				break;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Check whether the position is directly followed by a certain token type.
+	 *
+	 * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped
+	 */
+	public function hasTokenImmediatelyAfter(int $pos, int $expectedTokenType): bool
+	{
+		$tokens = $this->tokens;
+		$pos++;
+		for ($c = count($tokens); $pos < $c; $pos++) {
+			$token = $tokens[$pos];
+			$type = $token[Lexer::TYPE_OFFSET];
+			if ($type === $expectedTokenType) {
+				return true;
+			}
+			if (!in_array($type, [
+				Lexer::TOKEN_HORIZONTAL_WS,
+				Lexer::TOKEN_PHPDOC_EOL,
+			], true)) {
+				break;
+			}
+		}
+
+		return false;
+	}
+
+	public function getDetectedNewline(): ?string
+	{
+		return $this->newline;
+	}
+
+	/**
+	 * Whether the given position is immediately surrounded by parenthesis.
+	 */
+	public function hasParentheses(int $startPos, int $endPos): bool
+	{
+		return $this->hasTokenImmediatelyBefore($startPos, Lexer::TOKEN_OPEN_PARENTHESES)
+			&& $this->hasTokenImmediatelyAfter($endPos, Lexer::TOKEN_CLOSE_PARENTHESES);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php b/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php
new file mode 100644
index 0000000..2be2839
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php
@@ -0,0 +1,1098 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Parser;
+
+use LogicException;
+use PHPStan\PhpDocParser\Ast;
+use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use function in_array;
+use function str_replace;
+use function strlen;
+use function strpos;
+use function substr_compare;
+use function trim;
+
+class TypeParser
+{
+
+	/** @var ConstExprParser|null */
+	private $constExprParser;
+
+	/** @var bool */
+	private $quoteAwareConstExprString;
+
+	/** @var bool */
+	private $useLinesAttributes;
+
+	/** @var bool */
+	private $useIndexAttributes;
+
+	/**
+	 * @param array{lines?: bool, indexes?: bool} $usedAttributes
+	 */
+	public function __construct(
+		?ConstExprParser $constExprParser = null,
+		bool $quoteAwareConstExprString = false,
+		array $usedAttributes = []
+	)
+	{
+		$this->constExprParser = $constExprParser;
+		$this->quoteAwareConstExprString = $quoteAwareConstExprString;
+		$this->useLinesAttributes = $usedAttributes['lines'] ?? false;
+		$this->useIndexAttributes = $usedAttributes['indexes'] ?? false;
+	}
+
+	/** @phpstan-impure */
+	public function parse(TokenIterator $tokens): Ast\Type\TypeNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
+			$type = $this->parseNullable($tokens);
+
+		} else {
+			$type = $this->parseAtomic($tokens);
+
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
+				$type = $this->parseUnion($tokens, $type);
+
+			} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
+				$type = $this->parseIntersection($tokens, $type);
+			}
+		}
+
+		return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
+	}
+
+	/**
+	 * @internal
+	 * @template T of Ast\Node
+	 * @param T $type
+	 * @return T
+	 */
+	public function enrichWithAttributes(TokenIterator $tokens, Ast\Node $type, int $startLine, int $startIndex): Ast\Node
+	{
+		if ($this->useLinesAttributes) {
+			$type->setAttribute(Ast\Attribute::START_LINE, $startLine);
+			$type->setAttribute(Ast\Attribute::END_LINE, $tokens->currentTokenLine());
+		}
+
+		if ($this->useIndexAttributes) {
+			$type->setAttribute(Ast\Attribute::START_INDEX, $startIndex);
+			$type->setAttribute(Ast\Attribute::END_INDEX, $tokens->endIndexOfLastRelevantToken());
+		}
+
+		return $type;
+	}
+
+	/** @phpstan-impure */
+	private function subParse(TokenIterator $tokens): Ast\Type\TypeNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
+			$type = $this->parseNullable($tokens);
+
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {
+			$type = $this->parseConditionalForParameter($tokens, $tokens->currentTokenValue());
+
+		} else {
+			$type = $this->parseAtomic($tokens);
+
+			if ($tokens->isCurrentTokenValue('is')) {
+				$type = $this->parseConditional($tokens, $type);
+			} else {
+				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_UNION)) {
+					$type = $this->subParseUnion($tokens, $type);
+
+				} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_INTERSECTION)) {
+					$type = $this->subParseIntersection($tokens, $type);
+				}
+			}
+		}
+
+		return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$type = $this->subParse($tokens);
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
+
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+				$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
+			}
+
+			return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
+		}
+
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
+			$type = $this->enrichWithAttributes($tokens, new Ast\Type\ThisTypeNode(), $startLine, $startIndex);
+
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+				$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
+			}
+
+			return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
+		}
+
+		$currentTokenValue = $tokens->currentTokenValue();
+		$tokens->pushSavePoint(); // because of ConstFetchNode
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
+			$type = $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode($currentTokenValue), $startLine, $startIndex);
+
+			if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
+				$tokens->dropSavePoint(); // because of ConstFetchNode
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
+					$tokens->pushSavePoint();
+
+					$isHtml = $this->isHtml($tokens);
+					$tokens->rollback();
+					if ($isHtml) {
+						return $type;
+					}
+
+					$origType = $type;
+					$type = $this->tryParseCallable($tokens, $type, true);
+					if ($type === $origType) {
+						$type = $this->parseGeneric($tokens, $type);
+
+						if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+							$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
+						}
+					}
+				} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+					$type = $this->tryParseCallable($tokens, $type, false);
+
+				} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+					$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
+
+				} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
+					if ($type->name === 'object') {
+						$type = $this->parseObjectShape($tokens);
+					} else {
+						$type = $this->parseArrayShape($tokens, $type, $type->name);
+					}
+
+					if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+						$type = $this->tryParseArrayOrOffsetAccess(
+							$tokens,
+							$this->enrichWithAttributes($tokens, $type, $startLine, $startIndex)
+						);
+					}
+				}
+
+				return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
+			} else {
+				$tokens->rollback(); // because of ConstFetchNode
+			}
+		} else {
+			$tokens->dropSavePoint(); // because of ConstFetchNode
+		}
+
+		$currentTokenValue = $tokens->currentTokenValue();
+		$currentTokenType = $tokens->currentTokenType();
+		$currentTokenOffset = $tokens->currentTokenOffset();
+		$currentTokenLine = $tokens->currentTokenLine();
+
+		if ($this->constExprParser === null) {
+			throw new ParserException(
+				$currentTokenValue,
+				$currentTokenType,
+				$currentTokenOffset,
+				Lexer::TOKEN_IDENTIFIER,
+				null,
+				$currentTokenLine
+			);
+		}
+
+		try {
+			$constExpr = $this->constExprParser->parse($tokens, true);
+			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
+				throw new ParserException(
+					$currentTokenValue,
+					$currentTokenType,
+					$currentTokenOffset,
+					Lexer::TOKEN_IDENTIFIER,
+					null,
+					$currentTokenLine
+				);
+			}
+
+			$type = $this->enrichWithAttributes(
+				$tokens,
+				new Ast\Type\ConstTypeNode($constExpr),
+				$startLine,
+				$startIndex
+			);
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+				$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
+			}
+
+			return $type;
+		} catch (LogicException $e) {
+			throw new ParserException(
+				$currentTokenValue,
+				$currentTokenType,
+				$currentTokenOffset,
+				Lexer::TOKEN_IDENTIFIER,
+				null,
+				$currentTokenLine
+			);
+		}
+	}
+
+
+	/** @phpstan-impure */
+	private function parseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
+	{
+		$types = [$type];
+
+		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
+			$types[] = $this->parseAtomic($tokens);
+		}
+
+		return new Ast\Type\UnionTypeNode($types);
+	}
+
+
+	/** @phpstan-impure */
+	private function subParseUnion(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
+	{
+		$types = [$type];
+
+		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$types[] = $this->parseAtomic($tokens);
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		}
+
+		return new Ast\Type\UnionTypeNode($types);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
+	{
+		$types = [$type];
+
+		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
+			$types[] = $this->parseAtomic($tokens);
+		}
+
+		return new Ast\Type\IntersectionTypeNode($types);
+	}
+
+
+	/** @phpstan-impure */
+	private function subParseIntersection(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
+	{
+		$types = [$type];
+
+		while ($tokens->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			$types[] = $this->parseAtomic($tokens);
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		}
+
+		return new Ast\Type\IntersectionTypeNode($types);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseConditional(TokenIterator $tokens, Ast\Type\TypeNode $subjectType): Ast\Type\TypeNode
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+
+		$negated = false;
+		if ($tokens->isCurrentTokenValue('not')) {
+			$negated = true;
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+		}
+
+		$targetType = $this->parse($tokens);
+
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$ifType = $this->parse($tokens);
+
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$elseType = $this->subParse($tokens);
+
+		return new Ast\Type\ConditionalTypeNode($subjectType, $targetType, $ifType, $elseType, $negated);
+	}
+
+	/** @phpstan-impure */
+	private function parseConditionalForParameter(TokenIterator $tokens, string $parameterName): Ast\Type\TypeNode
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_VARIABLE);
+		$tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'is');
+
+		$negated = false;
+		if ($tokens->isCurrentTokenValue('not')) {
+			$negated = true;
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+		}
+
+		$targetType = $this->parse($tokens);
+
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$ifType = $this->parse($tokens);
+
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$elseType = $this->subParse($tokens);
+
+		return new Ast\Type\ConditionalTypeForParameterNode($parameterName, $targetType, $ifType, $elseType, $negated);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseNullable(TokenIterator $tokens): Ast\Type\TypeNode
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_NULLABLE);
+
+		$type = $this->parseAtomic($tokens);
+
+		return new Ast\Type\NullableTypeNode($type);
+	}
+
+	/** @phpstan-impure */
+	public function isHtml(TokenIterator $tokens): bool
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
+
+		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) {
+			return false;
+		}
+
+		$htmlTagName = $tokens->currentTokenValue();
+
+		$tokens->next();
+
+		if (!$tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
+			return false;
+		}
+
+		$endTag = '</' . $htmlTagName . '>';
+		$endTagSearchOffset = - strlen($endTag);
+
+		while (!$tokens->isCurrentTokenType(Lexer::TOKEN_END)) {
+			if (
+				(
+					$tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)
+					&& strpos($tokens->currentTokenValue(), '/' . $htmlTagName . '>') !== false
+				)
+				|| substr_compare($tokens->currentTokenValue(), $endTag, $endTagSearchOffset) === 0
+			) {
+				return true;
+			}
+
+			$tokens->next();
+		}
+
+		return false;
+	}
+
+	/** @phpstan-impure */
+	public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType): Ast\Type\GenericTypeNode
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
+
+		$startLine = $baseType->getAttribute(Ast\Attribute::START_LINE);
+		$startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX);
+		$genericTypes = [];
+		$variances = [];
+
+		$isFirst = true;
+		while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+			// trailing comma case
+			if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
+				break;
+			}
+			$isFirst = false;
+
+			[$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens);
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		}
+
+		$type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances);
+		if ($startLine !== null && $startIndex !== null) {
+			$type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex);
+		}
+
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
+
+		return $type;
+	}
+
+
+	/**
+	 * @phpstan-impure
+	 * @return array{Ast\Type\TypeNode, Ast\Type\GenericTypeNode::VARIANCE_*}
+	 */
+	public function parseGenericTypeArgument(TokenIterator $tokens): array
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) {
+			return [
+				$this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode('mixed'), $startLine, $startIndex),
+				Ast\Type\GenericTypeNode::VARIANCE_BIVARIANT,
+			];
+		}
+
+		if ($tokens->tryConsumeTokenValue('contravariant')) {
+			$variance = Ast\Type\GenericTypeNode::VARIANCE_CONTRAVARIANT;
+		} elseif ($tokens->tryConsumeTokenValue('covariant')) {
+			$variance = Ast\Type\GenericTypeNode::VARIANCE_COVARIANT;
+		} else {
+			$variance = Ast\Type\GenericTypeNode::VARIANCE_INVARIANT;
+		}
+
+		$type = $this->parse($tokens);
+		return [$type, $variance];
+	}
+
+	/**
+	 * @throws ParserException
+	 * @param ?callable(TokenIterator): string $parseDescription
+	 */
+	public function parseTemplateTagValue(
+		TokenIterator $tokens,
+		?callable $parseDescription = null
+	): TemplateTagValueNode
+	{
+		$name = $tokens->currentTokenValue();
+		$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+
+		$upperBound = $lowerBound = null;
+
+		if ($tokens->tryConsumeTokenValue('of') || $tokens->tryConsumeTokenValue('as')) {
+			$upperBound = $this->parse($tokens);
+		}
+
+		if ($tokens->tryConsumeTokenValue('super')) {
+			$lowerBound = $this->parse($tokens);
+		}
+
+		if ($tokens->tryConsumeTokenValue('=')) {
+			$default = $this->parse($tokens);
+		} else {
+			$default = null;
+		}
+
+		if ($parseDescription !== null) {
+			$description = $parseDescription($tokens);
+		} else {
+			$description = '';
+		}
+
+		if ($name === '') {
+			throw new LogicException('Template tag name cannot be empty.');
+		}
+
+		return new Ast\PhpDoc\TemplateTagValueNode($name, $upperBound, $description, $default, $lowerBound);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier, bool $hasTemplate): Ast\Type\TypeNode
+	{
+		$templates = $hasTemplate
+			? $this->parseCallableTemplates($tokens)
+			: [];
+
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$parameters = [];
+		if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
+			$parameters[] = $this->parseCallableParameter($tokens);
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
+				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) {
+					break;
+				}
+				$parameters[] = $this->parseCallableParameter($tokens);
+				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+			}
+		}
+
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
+		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
+
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		$returnType = $this->enrichWithAttributes($tokens, $this->parseCallableReturnType($tokens), $startLine, $startIndex);
+
+		return new Ast\Type\CallableTypeNode($identifier, $parameters, $returnType, $templates);
+	}
+
+
+	/**
+	 * @return Ast\PhpDoc\TemplateTagValueNode[]
+	 *
+	 * @phpstan-impure
+	 */
+	private function parseCallableTemplates(TokenIterator $tokens): array
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
+
+		$templates = [];
+
+		$isFirst = true;
+		while ($isFirst || $tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+			// trailing comma case
+			if (!$isFirst && $tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) {
+				break;
+			}
+			$isFirst = false;
+
+			$templates[] = $this->parseCallableTemplateArgument($tokens);
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		}
+
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
+
+		return $templates;
+	}
+
+
+	private function parseCallableTemplateArgument(TokenIterator $tokens): Ast\PhpDoc\TemplateTagValueNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		return $this->enrichWithAttributes(
+			$tokens,
+			$this->parseTemplateTagValue($tokens),
+			$startLine,
+			$startIndex
+		);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseCallableParameter(TokenIterator $tokens): Ast\Type\CallableTypeParameterNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		$type = $this->parse($tokens);
+		$isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE);
+		$isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC);
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {
+			$parameterName = $tokens->currentTokenValue();
+			$tokens->consumeTokenType(Lexer::TOKEN_VARIABLE);
+
+		} else {
+			$parameterName = '';
+		}
+
+		$isOptional = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
+		return $this->enrichWithAttributes(
+			$tokens,
+			new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional),
+			$startLine,
+			$startIndex
+		);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
+			return $this->parseNullable($tokens);
+
+		} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
+			$type = $this->subParse($tokens);
+			$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+				$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
+			}
+
+			return $type;
+		} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
+			$type = new Ast\Type\ThisTypeNode();
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+				$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
+					$tokens,
+					$type,
+					$startLine,
+					$startIndex
+				));
+			}
+
+			return $type;
+		} else {
+			$currentTokenValue = $tokens->currentTokenValue();
+			$tokens->pushSavePoint(); // because of ConstFetchNode
+			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
+				$type = new Ast\Type\IdentifierTypeNode($currentTokenValue);
+
+				if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
+					if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
+						$type = $this->parseGeneric(
+							$tokens,
+							$this->enrichWithAttributes(
+								$tokens,
+								$type,
+								$startLine,
+								$startIndex
+							)
+						);
+						if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+							$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
+								$tokens,
+								$type,
+								$startLine,
+								$startIndex
+							));
+						}
+
+					} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+						$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
+							$tokens,
+							$type,
+							$startLine,
+							$startIndex
+						));
+
+					} elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
+						if ($type->name === 'object') {
+							$type = $this->parseObjectShape($tokens);
+						} else {
+							$type = $this->parseArrayShape($tokens, $this->enrichWithAttributes(
+								$tokens,
+								$type,
+								$startLine,
+								$startIndex
+							), $type->name);
+						}
+
+						if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+							$type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes(
+								$tokens,
+								$type,
+								$startLine,
+								$startIndex
+							));
+						}
+					}
+
+					return $type;
+				} else {
+					$tokens->rollback(); // because of ConstFetchNode
+				}
+			} else {
+				$tokens->dropSavePoint(); // because of ConstFetchNode
+			}
+		}
+
+		$currentTokenValue = $tokens->currentTokenValue();
+		$currentTokenType = $tokens->currentTokenType();
+		$currentTokenOffset = $tokens->currentTokenOffset();
+		$currentTokenLine = $tokens->currentTokenLine();
+
+		if ($this->constExprParser === null) {
+			throw new ParserException(
+				$currentTokenValue,
+				$currentTokenType,
+				$currentTokenOffset,
+				Lexer::TOKEN_IDENTIFIER,
+				null,
+				$currentTokenLine
+			);
+		}
+
+		try {
+			$constExpr = $this->constExprParser->parse($tokens, true);
+			if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
+				throw new ParserException(
+					$currentTokenValue,
+					$currentTokenType,
+					$currentTokenOffset,
+					Lexer::TOKEN_IDENTIFIER,
+					null,
+					$currentTokenLine
+				);
+			}
+
+			$type = $this->enrichWithAttributes(
+				$tokens,
+				new Ast\Type\ConstTypeNode($constExpr),
+				$startLine,
+				$startIndex
+			);
+			if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+				$type = $this->tryParseArrayOrOffsetAccess($tokens, $type);
+			}
+
+			return $type;
+		} catch (LogicException $e) {
+			throw new ParserException(
+				$currentTokenValue,
+				$currentTokenType,
+				$currentTokenOffset,
+				Lexer::TOKEN_IDENTIFIER,
+				null,
+				$currentTokenLine
+			);
+		}
+	}
+
+
+	/** @phpstan-impure */
+	private function tryParseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier, bool $hasTemplate): Ast\Type\TypeNode
+	{
+		try {
+			$tokens->pushSavePoint();
+			$type = $this->parseCallable($tokens, $identifier, $hasTemplate);
+			$tokens->dropSavePoint();
+
+		} catch (ParserException $e) {
+			$tokens->rollback();
+			$type = $identifier;
+		}
+
+		return $type;
+	}
+
+
+	/** @phpstan-impure */
+	private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode
+	{
+		$startLine = $type->getAttribute(Ast\Attribute::START_LINE);
+		$startIndex = $type->getAttribute(Ast\Attribute::START_INDEX);
+		try {
+			while ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
+				$tokens->pushSavePoint();
+
+				$canBeOffsetAccessType = !$tokens->isPrecededByHorizontalWhitespace();
+				$tokens->consumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET);
+
+				if ($canBeOffsetAccessType && !$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET)) {
+					$offset = $this->parse($tokens);
+					$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
+					$tokens->dropSavePoint();
+					$type = new Ast\Type\OffsetAccessTypeNode($type, $offset);
+
+					if ($startLine !== null && $startIndex !== null) {
+						$type = $this->enrichWithAttributes(
+							$tokens,
+							$type,
+							$startLine,
+							$startIndex
+						);
+					}
+				} else {
+					$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET);
+					$tokens->dropSavePoint();
+					$type = new Ast\Type\ArrayTypeNode($type);
+
+					if ($startLine !== null && $startIndex !== null) {
+						$type = $this->enrichWithAttributes(
+							$tokens,
+							$type,
+							$startLine,
+							$startIndex
+						);
+					}
+				}
+			}
+
+		} catch (ParserException $e) {
+			$tokens->rollback();
+		}
+
+		return $type;
+	}
+
+
+	/**
+	 * @phpstan-impure
+	 * @param Ast\Type\ArrayShapeNode::KIND_* $kind
+	 */
+	private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type, string $kind): Ast\Type\ArrayShapeNode
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET);
+
+		$items = [];
+		$sealed = true;
+		$unsealedType = null;
+
+		do {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
+				return new Ast\Type\ArrayShapeNode($items, true, $kind);
+			}
+
+			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC)) {
+				$sealed = false;
+
+				$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+				if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
+					if ($kind === Ast\Type\ArrayShapeNode::KIND_ARRAY) {
+						$unsealedType = $this->parseArrayShapeUnsealedType($tokens);
+					} else {
+						$unsealedType = $this->parseListShapeUnsealedType($tokens);
+					}
+					$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+				}
+
+				$tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA);
+				break;
+			}
+
+			$items[] = $this->parseArrayShapeItem($tokens);
+
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
+
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
+
+		return new Ast\Type\ArrayShapeNode($items, $sealed, $kind, $unsealedType);
+	}
+
+
+	/** @phpstan-impure */
+	private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShapeItemNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+		try {
+			$tokens->pushSavePoint();
+			$key = $this->parseArrayShapeKey($tokens);
+			$optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE);
+			$tokens->consumeTokenType(Lexer::TOKEN_COLON);
+			$value = $this->parse($tokens);
+			$tokens->dropSavePoint();
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Ast\Type\ArrayShapeItemNode($key, $optional, $value),
+				$startLine,
+				$startIndex
+			);
+		} catch (ParserException $e) {
+			$tokens->rollback();
+			$value = $this->parse($tokens);
+
+			return $this->enrichWithAttributes(
+				$tokens,
+				new Ast\Type\ArrayShapeItemNode(null, false, $value),
+				$startLine,
+				$startIndex
+			);
+		}
+	}
+
+	/**
+	 * @phpstan-impure
+	 * @return Ast\ConstExpr\ConstExprIntegerNode|Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode
+	 */
+	private function parseArrayShapeKey(TokenIterator $tokens)
+	{
+		$startIndex = $tokens->currentTokenIndex();
+		$startLine = $tokens->currentTokenLine();
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
+			$key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue()));
+			$tokens->next();
+
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
+			if ($this->quoteAwareConstExprString) {
+				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED);
+			} else {
+				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'"));
+			}
+			$tokens->next();
+
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
+			if ($this->quoteAwareConstExprString) {
+				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED);
+			} else {
+				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"'));
+			}
+
+			$tokens->next();
+
+		} else {
+			$key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue());
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+		}
+
+		return $this->enrichWithAttributes(
+			$tokens,
+			$key,
+			$startLine,
+			$startIndex
+		);
+	}
+
+	/**
+	 * @phpstan-impure
+	 */
+	private function parseArrayShapeUnsealedType(TokenIterator $tokens): Ast\Type\ArrayShapeUnsealedTypeNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$valueType = $this->parse($tokens);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$keyType = null;
+		if ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+			$keyType = $valueType;
+			$valueType = $this->parse($tokens);
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		}
+
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
+
+		return $this->enrichWithAttributes(
+			$tokens,
+			new Ast\Type\ArrayShapeUnsealedTypeNode($valueType, $keyType),
+			$startLine,
+			$startIndex
+		);
+	}
+
+	/**
+	 * @phpstan-impure
+	 */
+	private function parseListShapeUnsealedType(TokenIterator $tokens): Ast\Type\ArrayShapeUnsealedTypeNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$valueType = $this->parse($tokens);
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
+
+		return $this->enrichWithAttributes(
+			$tokens,
+			new Ast\Type\ArrayShapeUnsealedTypeNode($valueType, null),
+			$startLine,
+			$startIndex
+		);
+	}
+
+	/**
+	 * @phpstan-impure
+	 */
+	private function parseObjectShape(TokenIterator $tokens): Ast\Type\ObjectShapeNode
+	{
+		$tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET);
+
+		$items = [];
+
+		do {
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+
+			if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) {
+				return new Ast\Type\ObjectShapeNode($items);
+			}
+
+			$items[] = $this->parseObjectShapeItem($tokens);
+
+			$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		} while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA));
+
+		$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
+		$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
+
+		return new Ast\Type\ObjectShapeNode($items);
+	}
+
+	/** @phpstan-impure */
+	private function parseObjectShapeItem(TokenIterator $tokens): Ast\Type\ObjectShapeItemNode
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		$key = $this->parseObjectShapeKey($tokens);
+		$optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE);
+		$tokens->consumeTokenType(Lexer::TOKEN_COLON);
+		$value = $this->parse($tokens);
+
+		return $this->enrichWithAttributes($tokens, new Ast\Type\ObjectShapeItemNode($key, $optional, $value), $startLine, $startIndex);
+	}
+
+	/**
+	 * @phpstan-impure
+	 * @return Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode
+	 */
+	private function parseObjectShapeKey(TokenIterator $tokens)
+	{
+		$startLine = $tokens->currentTokenLine();
+		$startIndex = $tokens->currentTokenIndex();
+
+		if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
+			if ($this->quoteAwareConstExprString) {
+				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED);
+			} else {
+				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'"));
+			}
+			$tokens->next();
+
+		} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
+			if ($this->quoteAwareConstExprString) {
+				$key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED);
+			} else {
+				$key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"'));
+			}
+			$tokens->next();
+
+		} else {
+			$key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue());
+			$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
+		}
+
+		return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex);
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php b/vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php
new file mode 100644
index 0000000..2684dfc
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php
@@ -0,0 +1,44 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Printer;
+
+/**
+ * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
+ *
+ * Copyright (c) 2011, Nikita Popov
+ * All rights reserved.
+ *
+ * Implements the Myers diff algorithm.
+ *
+ * @internal
+ */
+class DiffElem
+{
+
+	public const TYPE_KEEP = 0;
+	public const TYPE_REMOVE = 1;
+	public const TYPE_ADD = 2;
+	public const TYPE_REPLACE = 3;
+
+	/** @var self::TYPE_* */
+	public $type;
+
+	/** @var mixed Is null for add operations */
+	public $old;
+
+	/** @var mixed Is null for remove operations */
+	public $new;
+
+	/**
+	 * @param self::TYPE_* $type
+	 * @param mixed $old Is null for add operations
+	 * @param mixed $new Is null for remove operations
+	 */
+	public function __construct(int $type, $old, $new)
+	{
+		$this->type = $type;
+		$this->old = $old;
+		$this->new = $new;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Printer/Differ.php b/vendor/phpstan/phpdoc-parser/src/Printer/Differ.php
new file mode 100644
index 0000000..ab10be5
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Printer/Differ.php
@@ -0,0 +1,196 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Printer;
+
+use Exception;
+use function array_reverse;
+use function count;
+
+/**
+ * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
+ *
+ * Copyright (c) 2011, Nikita Popov
+ * All rights reserved.
+ *
+ * Implements the Myers diff algorithm.
+ *
+ * Myers, Eugene W. "An O (ND) difference algorithm and its variations."
+ * Algorithmica 1.1 (1986): 251-266.
+ *
+ * @template T
+ * @internal
+ */
+class Differ
+{
+
+	/** @var callable(T, T): bool */
+	private $isEqual;
+
+	/**
+	 * Create differ over the given equality relation.
+	 *
+	 * @param callable(T, T): bool $isEqual Equality relation
+	 */
+	public function __construct(callable $isEqual)
+	{
+		$this->isEqual = $isEqual;
+	}
+
+	/**
+	 * Calculate diff (edit script) from $old to $new.
+	 *
+	 * @param T[] $old Original array
+	 * @param T[] $new New array
+	 *
+	 * @return DiffElem[] Diff (edit script)
+	 */
+	public function diff(array $old, array $new): array
+	{
+		[$trace, $x, $y] = $this->calculateTrace($old, $new);
+		return $this->extractDiff($trace, $x, $y, $old, $new);
+	}
+
+	/**
+	 * Calculate diff, including "replace" operations.
+	 *
+	 * If a sequence of remove operations is followed by the same number of add operations, these
+	 * will be coalesced into replace operations.
+	 *
+	 * @param T[] $old Original array
+	 * @param T[] $new New array
+	 *
+	 * @return DiffElem[] Diff (edit script), including replace operations
+	 */
+	public function diffWithReplacements(array $old, array $new): array
+	{
+		return $this->coalesceReplacements($this->diff($old, $new));
+	}
+
+	/**
+	 * @param T[] $old
+	 * @param T[] $new
+	 * @return array{array<int, array<int, int>>, int, int}
+	 */
+	private function calculateTrace(array $old, array $new): array
+	{
+		$n = count($old);
+		$m = count($new);
+		$max = $n + $m;
+		$v = [1 => 0];
+		$trace = [];
+		for ($d = 0; $d <= $max; $d++) {
+			$trace[] = $v;
+			for ($k = -$d; $k <= $d; $k += 2) {
+				if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
+					$x = $v[$k + 1];
+				} else {
+					$x = $v[$k - 1] + 1;
+				}
+
+				$y = $x - $k;
+				while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) {
+					$x++;
+					$y++;
+				}
+
+				$v[$k] = $x;
+				if ($x >= $n && $y >= $m) {
+					return [$trace, $x, $y];
+				}
+			}
+		}
+		throw new Exception('Should not happen');
+	}
+
+	/**
+	 * @param array<int, array<int, int>> $trace
+	 * @param T[] $old
+	 * @param T[] $new
+	 * @return DiffElem[]
+	 */
+	private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array
+	{
+		$result = [];
+		for ($d = count($trace) - 1; $d >= 0; $d--) {
+			$v = $trace[$d];
+			$k = $x - $y;
+
+			if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
+				$prevK = $k + 1;
+			} else {
+				$prevK = $k - 1;
+			}
+
+			$prevX = $v[$prevK];
+			$prevY = $prevX - $prevK;
+
+			while ($x > $prevX && $y > $prevY) {
+				$result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]);
+				$x--;
+				$y--;
+			}
+
+			if ($d === 0) {
+				break;
+			}
+
+			while ($x > $prevX) {
+				$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null);
+				$x--;
+			}
+
+			while ($y > $prevY) {
+				$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]);
+				$y--;
+			}
+		}
+		return array_reverse($result);
+	}
+
+	/**
+	 * Coalesce equal-length sequences of remove+add into a replace operation.
+	 *
+	 * @param DiffElem[] $diff
+	 * @return DiffElem[]
+	 */
+	private function coalesceReplacements(array $diff): array
+	{
+		$newDiff = [];
+		$c = count($diff);
+		for ($i = 0; $i < $c; $i++) {
+			$diffType = $diff[$i]->type;
+			if ($diffType !== DiffElem::TYPE_REMOVE) {
+				$newDiff[] = $diff[$i];
+				continue;
+			}
+
+			$j = $i;
+			while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) {
+				$j++;
+			}
+
+			$k = $j;
+			while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) {
+				$k++;
+			}
+
+			if ($j - $i === $k - $j) {
+				$len = $j - $i;
+				for ($n = 0; $n < $len; $n++) {
+					$newDiff[] = new DiffElem(
+						DiffElem::TYPE_REPLACE,
+						$diff[$i + $n]->old,
+						$diff[$j + $n]->new
+					);
+				}
+			} else {
+				for (; $i < $k; $i++) {
+					$newDiff[] = $diff[$i];
+				}
+			}
+			$i = $k - 1;
+		}
+		return $newDiff;
+	}
+
+}
diff --git a/vendor/phpstan/phpdoc-parser/src/Printer/Printer.php b/vendor/phpstan/phpdoc-parser/src/Printer/Printer.php
new file mode 100644
index 0000000..7550078
--- /dev/null
+++ b/vendor/phpstan/phpdoc-parser/src/Printer/Printer.php
@@ -0,0 +1,866 @@
+<?php declare(strict_types = 1);
+
+namespace PHPStan\PhpDocParser\Printer;
+
+use LogicException;
+use PHPStan\PhpDocParser\Ast\Attribute;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
+use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
+use PHPStan\PhpDocParser\Ast\Node;
+use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagMethodValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagPropertyValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\AssertTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineAnnotation;
+use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArgument;
+use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArray;
+use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineArrayItem;
+use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ImplementsTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ParamClosureThisTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ParamImmediatelyInvokedCallableTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ParamLaterInvokedCallableTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ParamOutTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\RequireExtendsTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\RequireImplementsTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\SelfOutTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasImportTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
+use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
+use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
+use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
+use PHPStan\PhpDocParser\Ast\Type\ArrayShapeUnsealedTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
+use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode;
+use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\InvalidTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode;
+use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode;
+use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
+use PHPStan\PhpDocParser\Ast\Type\TypeNode;
+use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
+use PHPStan\PhpDocParser\Lexer\Lexer;
+use PHPStan\PhpDocParser\Parser\TokenIterator;
+use function array_keys;
+use function array_map;
+use function count;
+use function get_class;
+use function get_object_vars;
+use function implode;
+use function in_array;
+use function is_array;
+use function preg_match_all;
+use function sprintf;
+use function strlen;
+use function strpos;
+use function trim;
+use const PREG_SET_ORDER;
+
+/**
+ * Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
+ *
+ * Copyright (c) 2011, Nikita Popov
+ * All rights reserved.
+ */
+final class Printer
+{
+
+	/** @var Differ<Node> */
+	private $differ;
+
+	/**
+	 * Map From "{$class}->{$subNode}" to string that should be inserted
+	 * between elements of this list subnode
+	 *
+	 * @var array<string, string>
+	 */
+	private $listInsertionMap = [
+		PhpDocNode::class . '->children' => "\n * ",
+		UnionTypeNode::class . '->types' => '|',
+		IntersectionTypeNode::class . '->types' => '&',
+		ArrayShapeNode::class . '->items' => ', ',
+		ObjectShapeNode::class . '->items' => ', ',
+		CallableTypeNode::class . '->parameters' => ', ',
+		CallableTypeNode::class . '->templateTypes' => ', ',
+		GenericTypeNode::class . '->genericTypes' => ', ',
+		ConstExprArrayNode::class . '->items' => ', ',
+		MethodTagValueNode::class . '->parameters' => ', ',
+		DoctrineArray::class . '->items' => ', ',
+		DoctrineAnnotation::class . '->arguments' => ', ',
+	];
+
+	/**
+	 * [$find, $extraLeft, $extraRight]
+	 *
+	 * @var array<string, array{string|null, string, string}>
+	 */
+	private $emptyListInsertionMap = [
+		CallableTypeNode::class . '->parameters' => ['(', '', ''],
+		ArrayShapeNode::class . '->items' => ['{', '', ''],
+		ObjectShapeNode::class . '->items' => ['{', '', ''],
+		DoctrineArray::class . '->items' => ['{', '', ''],
+		DoctrineAnnotation::class . '->arguments' => ['(', '', ''],
+	];
+
+	/** @var array<string, list<class-string<TypeNode>>> */
+	private $parenthesesMap = [
+		CallableTypeNode::class . '->returnType' => [
+			CallableTypeNode::class,
+			UnionTypeNode::class,
+			IntersectionTypeNode::class,
+		],
+		ArrayTypeNode::class . '->type' => [
+			CallableTypeNode::class,
+			UnionTypeNode::class,
+			IntersectionTypeNode::class,
+			ConstTypeNode::class,
+			NullableTypeNode::class,
+		],
+		OffsetAccessTypeNode::class . '->type' => [
+			CallableTypeNode::class,
+			UnionTypeNode::class,
+			IntersectionTypeNode::class,
+			NullableTypeNode::class,
+		],
+	];
+
+	/** @var array<string, list<class-string<TypeNode>>> */
+	private $parenthesesListMap = [
+		IntersectionTypeNode::class . '->types' => [
+			IntersectionTypeNode::class,
+			UnionTypeNode::class,
+			NullableTypeNode::class,
+		],
+		UnionTypeNode::class . '->types' => [
+			IntersectionTypeNode::class,
+			UnionTypeNode::class,
+			NullableTypeNode::class,
+		],
+	];
+
+	public function printFormatPreserving(PhpDocNode $node, PhpDocNode $originalNode, TokenIterator $originalTokens): string
+	{
+		$this->differ = new Differ(static function ($a, $b) {
+			if ($a instanceof Node && $b instanceof Node) {
+				return $a === $b->getAttribute(Attribute::ORIGINAL_NODE);
+			}
+
+			return false;
+		});
+
+		$tokenIndex = 0;
+		$result = $this->printArrayFormatPreserving(
+			$node->children,
+			$originalNode->children,
+			$originalTokens,
+			$tokenIndex,
+			PhpDocNode::class,
+			'children'
+		);
+		if ($result !== null) {
+			return $result . $originalTokens->getContentBetween($tokenIndex, $originalTokens->getTokenCount());
+		}
+
+		return $this->print($node);
+	}
+
+	public function print(Node $node): string
+	{
+		if ($node instanceof PhpDocNode) {
+			return "/**\n *" . implode("\n *", array_map(
+				function (PhpDocChildNode $child): string {
+					$s = $this->print($child);
+					return $s === '' ? '' : ' ' . $s;
+				},
+				$node->children
+			)) . "\n */";
+		}
+		if ($node instanceof PhpDocTextNode) {
+			return $node->text;
+		}
+		if ($node instanceof PhpDocTagNode) {
+			if ($node->value instanceof DoctrineTagValueNode) {
+				return $this->print($node->value);
+			}
+
+			return trim(sprintf('%s %s', $node->name, $this->print($node->value)));
+		}
+		if ($node instanceof PhpDocTagValueNode) {
+			return $this->printTagValue($node);
+		}
+		if ($node instanceof TypeNode) {
+			return $this->printType($node);
+		}
+		if ($node instanceof ConstExprNode) {
+			return $this->printConstExpr($node);
+		}
+		if ($node instanceof MethodTagValueParameterNode) {
+			$type = $node->type !== null ? $this->print($node->type) . ' ' : '';
+			$isReference = $node->isReference ? '&' : '';
+			$isVariadic = $node->isVariadic ? '...' : '';
+			$default = $node->defaultValue !== null ? ' = ' . $this->print($node->defaultValue) : '';
+			return "{$type}{$isReference}{$isVariadic}{$node->parameterName}{$default}";
+		}
+		if ($node instanceof CallableTypeParameterNode) {
+			$type = $this->print($node->type) . ' ';
+			$isReference = $node->isReference ? '&' : '';
+			$isVariadic = $node->isVariadic ? '...' : '';
+			$isOptional = $node->isOptional ? '=' : '';
+			return trim("{$type}{$isReference}{$isVariadic}{$node->parameterName}") . $isOptional;
+		}
+		if ($node instanceof ArrayShapeUnsealedTypeNode) {
+			if ($node->keyType !== null) {
+				return sprintf('<%s, %s>', $this->printType($node->keyType), $this->printType($node->valueType));
+			}
+			return sprintf('<%s>', $this->printType($node->valueType));
+		}
+		if ($node instanceof DoctrineAnnotation) {
+			return (string) $node;
+		}
+		if ($node instanceof DoctrineArgument) {
+			return (string) $node;
+		}
+		if ($node instanceof DoctrineArray) {
+			return (string) $node;
+		}
+		if ($node instanceof DoctrineArrayItem) {
+			return (string) $node;
+		}
+
+		throw new LogicException(sprintf('Unknown node type %s', get_class($node)));
+	}
+
+	private function printTagValue(PhpDocTagValueNode $node): string
+	{
+		// only nodes that contain another node are handled here
+		// the rest falls back on (string) $node
+
+		if ($node instanceof AssertTagMethodValueNode) {
+			$isNegated = $node->isNegated ? '!' : '';
+			$isEquality = $node->isEquality ? '=' : '';
+			$type = $this->printType($node->type);
+			return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->method}() {$node->description}");
+		}
+		if ($node instanceof AssertTagPropertyValueNode) {
+			$isNegated = $node->isNegated ? '!' : '';
+			$isEquality = $node->isEquality ? '=' : '';
+			$type = $this->printType($node->type);
+			return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->property} {$node->description}");
+		}
+		if ($node instanceof AssertTagValueNode) {
+			$isNegated = $node->isNegated ? '!' : '';
+			$isEquality = $node->isEquality ? '=' : '';
+			$type = $this->printType($node->type);
+			return trim("{$isNegated}{$isEquality}{$type} {$node->parameter} {$node->description}");
+		}
+		if ($node instanceof ExtendsTagValueNode || $node instanceof ImplementsTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
+		if ($node instanceof MethodTagValueNode) {
+			$static = $node->isStatic ? 'static ' : '';
+			$returnType = $node->returnType !== null ? $this->printType($node->returnType) . ' ' : '';
+			$parameters = implode(', ', array_map(function (MethodTagValueParameterNode $parameter): string {
+				return $this->print($parameter);
+			}, $node->parameters));
+			$description = $node->description !== '' ? " {$node->description}" : '';
+			$templateTypes = count($node->templateTypes) > 0 ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateTag): string {
+				return $this->print($templateTag);
+			}, $node->templateTypes)) . '>' : '';
+			return "{$static}{$returnType}{$node->methodName}{$templateTypes}({$parameters}){$description}";
+		}
+		if ($node instanceof MixinTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
+		if ($node instanceof RequireExtendsTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
+		if ($node instanceof RequireImplementsTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
+		if ($node instanceof ParamOutTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->parameterName} {$node->description}");
+		}
+		if ($node instanceof ParamTagValueNode) {
+			$reference = $node->isReference ? '&' : '';
+			$variadic = $node->isVariadic ? '...' : '';
+			$type = $this->printType($node->type);
+			return trim("{$type} {$reference}{$variadic}{$node->parameterName} {$node->description}");
+		}
+		if ($node instanceof ParamImmediatelyInvokedCallableTagValueNode) {
+			return trim("{$node->parameterName} {$node->description}");
+		}
+		if ($node instanceof ParamLaterInvokedCallableTagValueNode) {
+			return trim("{$node->parameterName} {$node->description}");
+		}
+		if ($node instanceof ParamClosureThisTagValueNode) {
+			return trim("{$node->type} {$node->parameterName} {$node->description}");
+		}
+		if ($node instanceof PropertyTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->propertyName} {$node->description}");
+		}
+		if ($node instanceof ReturnTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
+		if ($node instanceof SelfOutTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim($type . ' ' . $node->description);
+		}
+		if ($node instanceof TemplateTagValueNode) {
+			$upperBound = $node->bound !== null ? ' of ' . $this->printType($node->bound) : '';
+			$lowerBound = $node->lowerBound !== null ? ' super ' . $this->printType($node->lowerBound) : '';
+			$default = $node->default !== null ? ' = ' . $this->printType($node->default) : '';
+			return trim("{$node->name}{$upperBound}{$lowerBound}{$default} {$node->description}");
+		}
+		if ($node instanceof ThrowsTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
+		if ($node instanceof TypeAliasImportTagValueNode) {
+			return trim(
+				"{$node->importedAlias} from " . $this->printType($node->importedFrom)
+				. ($node->importedAs !== null ? " as {$node->importedAs}" : '')
+			);
+		}
+		if ($node instanceof TypeAliasTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$node->alias} {$type}");
+		}
+		if ($node instanceof UsesTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} {$node->description}");
+		}
+		if ($node instanceof VarTagValueNode) {
+			$type = $this->printType($node->type);
+			return trim("{$type} " . trim("{$node->variableName} {$node->description}"));
+		}
+
+		return (string) $node;
+	}
+
+	private function printType(TypeNode $node): string
+	{
+		if ($node instanceof ArrayShapeNode) {
+			$items = array_map(function (ArrayShapeItemNode $item): string {
+				return $this->printType($item);
+			}, $node->items);
+
+			if (! $node->sealed) {
+				$items[] = '...' . ($node->unsealedType === null ? '' : $this->print($node->unsealedType));
+			}
+
+			return $node->kind . '{' . implode(', ', $items) . '}';
+		}
+		if ($node instanceof ArrayShapeItemNode) {
+			if ($node->keyName !== null) {
+				return sprintf(
+					'%s%s: %s',
+					$this->print($node->keyName),
+					$node->optional ? '?' : '',
+					$this->printType($node->valueType)
+				);
+			}
+
+			return $this->printType($node->valueType);
+		}
+		if ($node instanceof ArrayTypeNode) {
+			return $this->printOffsetAccessType($node->type) . '[]';
+		}
+		if ($node instanceof CallableTypeNode) {
+			if ($node->returnType instanceof CallableTypeNode || $node->returnType instanceof UnionTypeNode || $node->returnType instanceof IntersectionTypeNode) {
+				$returnType = $this->wrapInParentheses($node->returnType);
+			} else {
+				$returnType = $this->printType($node->returnType);
+			}
+			$template = $node->templateTypes !== []
+				? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateNode): string {
+					return $this->print($templateNode);
+				}, $node->templateTypes)) . '>'
+				: '';
+			$parameters = implode(', ', array_map(function (CallableTypeParameterNode $parameterNode): string {
+				return $this->print($parameterNode);
+			}, $node->parameters));
+			return "{$node->identifier}{$template}({$parameters}): {$returnType}";
+		}
+		if ($node instanceof ConditionalTypeForParameterNode) {
+			return sprintf(
+				'(%s %s %s ? %s : %s)',
+				$node->parameterName,
+				$node->negated ? 'is not' : 'is',
+				$this->printType($node->targetType),
+				$this->printType($node->if),
+				$this->printType($node->else)
+			);
+		}
+		if ($node instanceof ConditionalTypeNode) {
+			return sprintf(
+				'(%s %s %s ? %s : %s)',
+				$this->printType($node->subjectType),
+				$node->negated ? 'is not' : 'is',
+				$this->printType($node->targetType),
+				$this->printType($node->if),
+				$this->printType($node->else)
+			);
+		}
+		if ($node instanceof ConstTypeNode) {
+			return $this->printConstExpr($node->constExpr);
+		}
+		if ($node instanceof GenericTypeNode) {
+			$genericTypes = [];
+
+			foreach ($node->genericTypes as $index => $type) {
+				$variance = $node->variances[$index] ?? GenericTypeNode::VARIANCE_INVARIANT;
+				if ($variance === GenericTypeNode::VARIANCE_INVARIANT) {
+					$genericTypes[] = $this->printType($type);
+				} elseif ($variance === GenericTypeNode::VARIANCE_BIVARIANT) {
+					$genericTypes[] = '*';
+				} else {
+					$genericTypes[] = sprintf('%s %s', $variance, $this->print($type));
+				}
+			}
+
+			return $node->type . '<' . implode(', ', $genericTypes) . '>';
+		}
+		if ($node instanceof IdentifierTypeNode) {
+			return $node->name;
+		}
+		if ($node instanceof IntersectionTypeNode || $node instanceof UnionTypeNode) {
+			$items = [];
+			foreach ($node->types as $type) {
+				if (
+					$type instanceof IntersectionTypeNode
+					|| $type instanceof UnionTypeNode
+					|| $type instanceof NullableTypeNode
+				) {
+					$items[] = $this->wrapInParentheses($type);
+					continue;
+				}
+
+				$items[] = $this->printType($type);
+			}
+
+			return implode($node instanceof IntersectionTypeNode ? '&' : '|', $items);
+		}
+		if ($node instanceof InvalidTypeNode) {
+			return (string) $node;
+		}
+		if ($node instanceof NullableTypeNode) {
+			if ($node->type instanceof IntersectionTypeNode || $node->type instanceof UnionTypeNode) {
+				return '?(' . $this->printType($node->type) . ')';
+			}
+
+			return '?' . $this->printType($node->type);
+		}
+		if ($node instanceof ObjectShapeNode) {
+			$items = array_map(function (ObjectShapeItemNode $item): string {
+				return $this->printType($item);
+			}, $node->items);
+
+			return 'object{' . implode(', ', $items) . '}';
+		}
+		if ($node instanceof ObjectShapeItemNode) {
+			if ($node->keyName !== null) {
+				return sprintf(
+					'%s%s: %s',
+					$this->print($node->keyName),
+					$node->optional ? '?' : '',
+					$this->printType($node->valueType)
+				);
+			}
+
+			return $this->printType($node->valueType);
+		}
+		if ($node instanceof OffsetAccessTypeNode) {
+			return $this->printOffsetAccessType($node->type) . '[' . $this->printType($node->offset) . ']';
+		}
+		if ($node instanceof ThisTypeNode) {
+			return (string) $node;
+		}
+
+		throw new LogicException(sprintf('Unknown node type %s', get_class($node)));
+	}
+
+	private function wrapInParentheses(TypeNode $node): string
+	{
+		return '(' . $this->printType($node) . ')';
+	}
+
+	private function printOffsetAccessType(TypeNode $type): string
+	{
+		if (
+			$type instanceof CallableTypeNode
+			|| $type instanceof UnionTypeNode
+			|| $type instanceof IntersectionTypeNode
+			|| $type instanceof NullableTypeNode
+		) {
+			return $this->wrapInParentheses($type);
+		}
+
+		return $this->printType($type);
+	}
+
+	private function printConstExpr(ConstExprNode $node): string
+	{
+		// this is fine - ConstExprNode classes do not contain nodes that need smart printer logic
+		return (string) $node;
+	}
+
+	/**
+	 * @param Node[] $nodes
+	 * @param Node[] $originalNodes
+	 */
+	private function printArrayFormatPreserving(array $nodes, array $originalNodes, TokenIterator $originalTokens, int &$tokenIndex, string $parentNodeClass, string $subNodeName): ?string
+	{
+		$diff = $this->differ->diffWithReplacements($originalNodes, $nodes);
+		$mapKey = $parentNodeClass . '->' . $subNodeName;
+		$insertStr = $this->listInsertionMap[$mapKey] ?? null;
+		$result = '';
+		$beforeFirstKeepOrReplace = true;
+		$delayedAdd = [];
+
+		$insertNewline = false;
+		[$isMultiline, $beforeAsteriskIndent, $afterAsteriskIndent] = $this->isMultiline($tokenIndex, $originalNodes, $originalTokens);
+
+		if ($insertStr === "\n * ") {
+			$insertStr = sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+		}
+
+		foreach ($diff as $i => $diffElem) {
+			$diffType = $diffElem->type;
+			$newNode = $diffElem->new;
+			$originalNode = $diffElem->old;
+			if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
+				$beforeFirstKeepOrReplace = false;
+				if (!$newNode instanceof Node || !$originalNode instanceof Node) {
+					return null;
+				}
+				$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
+				$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
+				if ($itemStartPos < 0 || $itemEndPos < 0 || $itemStartPos < $tokenIndex) {
+					throw new LogicException();
+				}
+
+				$result .= $originalTokens->getContentBetween($tokenIndex, $itemStartPos);
+
+				if (count($delayedAdd) > 0) {
+					foreach ($delayedAdd as $delayedAddNode) {
+						$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
+							&& in_array(get_class($delayedAddNode), $this->parenthesesListMap[$mapKey], true);
+						if ($parenthesesNeeded) {
+							$result .= '(';
+						}
+						$result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens);
+						if ($parenthesesNeeded) {
+							$result .= ')';
+						}
+
+						if ($insertNewline) {
+							$result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+						} else {
+							$result .= $insertStr;
+						}
+					}
+
+					$delayedAdd = [];
+				}
+
+				$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
+					&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true)
+					&& !in_array(get_class($originalNode), $this->parenthesesListMap[$mapKey], true);
+				$addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($itemStartPos, $itemEndPos);
+				if ($addParentheses) {
+					$result .= '(';
+				}
+
+				$result .= $this->printNodeFormatPreserving($newNode, $originalTokens);
+				if ($addParentheses) {
+					$result .= ')';
+				}
+				$tokenIndex = $itemEndPos + 1;
+
+			} elseif ($diffType === DiffElem::TYPE_ADD) {
+				if ($insertStr === null) {
+					return null;
+				}
+				if (!$newNode instanceof Node) {
+					return null;
+				}
+
+				if ($insertStr === ', ' && $isMultiline) {
+					$insertStr = ',';
+					$insertNewline = true;
+				}
+
+				if ($beforeFirstKeepOrReplace) {
+					// Will be inserted at the next "replace" or "keep" element
+					$delayedAdd[] = $newNode;
+					continue;
+				}
+
+				$itemEndPos = $tokenIndex - 1;
+				if ($insertNewline) {
+					$result .= $insertStr . sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+				} else {
+					$result .= $insertStr;
+				}
+
+				$parenthesesNeeded = isset($this->parenthesesListMap[$mapKey])
+					&& in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true);
+				if ($parenthesesNeeded) {
+					$result .= '(';
+				}
+
+				$result .= $this->printNodeFormatPreserving($newNode, $originalTokens);
+				if ($parenthesesNeeded) {
+					$result .= ')';
+				}
+
+				$tokenIndex = $itemEndPos + 1;
+
+			} elseif ($diffType === DiffElem::TYPE_REMOVE) {
+				if (!$originalNode instanceof Node) {
+					return null;
+				}
+
+				$itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX);
+				$itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX);
+				if ($itemStartPos < 0 || $itemEndPos < 0) {
+					throw new LogicException();
+				}
+
+				if ($i === 0) {
+					// If we're removing from the start, keep the tokens before the node and drop those after it,
+					// instead of the other way around.
+					$originalTokensArray = $originalTokens->getTokens();
+					for ($j = $tokenIndex; $j < $itemStartPos; $j++) {
+						if ($originalTokensArray[$j][Lexer::TYPE_OFFSET] === Lexer::TOKEN_PHPDOC_EOL) {
+							break;
+						}
+						$result .= $originalTokensArray[$j][Lexer::VALUE_OFFSET];
+					}
+				}
+
+				$tokenIndex = $itemEndPos + 1;
+			}
+		}
+
+		if (count($delayedAdd) > 0) {
+			if (!isset($this->emptyListInsertionMap[$mapKey])) {
+				return null;
+			}
+
+			[$findToken, $extraLeft, $extraRight] = $this->emptyListInsertionMap[$mapKey];
+			if ($findToken !== null) {
+				$originalTokensArray = $originalTokens->getTokens();
+				for (; $tokenIndex < count($originalTokensArray); $tokenIndex++) {
+					$result .= $originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET];
+					if ($originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET] !== $findToken) {
+						continue;
+					}
+
+					$tokenIndex++;
+					break;
+				}
+			}
+			$first = true;
+			$result .= $extraLeft;
+			foreach ($delayedAdd as $delayedAddNode) {
+				if (!$first) {
+					$result .= $insertStr;
+					if ($insertNewline) {
+						$result .= sprintf('%s%s*%s', $originalTokens->getDetectedNewline() ?? "\n", $beforeAsteriskIndent, $afterAsteriskIndent);
+					}
+				}
+
+				$result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens);
+				$first = false;
+			}
+			$result .= $extraRight;
+		}
+
+		return $result;
+	}
+
+	/**
+	 * @param Node[] $nodes
+	 * @return array{bool, string, string}
+	 */
+	private function isMultiline(int $initialIndex, array $nodes, TokenIterator $originalTokens): array
+	{
+		$isMultiline = count($nodes) > 1;
+		$pos = $initialIndex;
+		$allText = '';
+		/** @var Node|null $node */
+		foreach ($nodes as $node) {
+			if (!$node instanceof Node) {
+				continue;
+			}
+
+			$endPos = $node->getAttribute(Attribute::END_INDEX) + 1;
+			$text = $originalTokens->getContentBetween($pos, $endPos);
+			$allText .= $text;
+			if (strpos($text, "\n") === false) {
+				// We require that a newline is present between *every* item. If the formatting
+				// is inconsistent, with only some items having newlines, we don't consider it
+				// as multiline
+				$isMultiline = false;
+			}
+			$pos = $endPos;
+		}
+
+		$c = preg_match_all('~\n(?<before>[\\x09\\x20]*)\*(?<after>\\x20*)~', $allText, $matches, PREG_SET_ORDER);
+		if ($c === 0) {
+			return [$isMultiline, '', ''];
+		}
+
+		$before = '';
+		$after = '';
+		foreach ($matches as $match) {
+			if (strlen($match['before']) > strlen($before)) {
+				$before = $match['before'];
+			}
+			if (strlen($match['after']) <= strlen($after)) {
+				continue;
+			}
+
+			$after = $match['after'];
+		}
+
+		return [$isMultiline, $before, $after];
+	}
+
+	private function printNodeFormatPreserving(Node $node, TokenIterator $originalTokens): string
+	{
+		/** @var Node|null $originalNode */
+		$originalNode = $node->getAttribute(Attribute::ORIGINAL_NODE);
+		if ($originalNode === null) {
+			return $this->print($node);
+		}
+
+		$class = get_class($node);
+		if ($class !== get_class($originalNode)) {
+			throw new LogicException();
+		}
+
+		$startPos = $originalNode->getAttribute(Attribute::START_INDEX);
+		$endPos = $originalNode->getAttribute(Attribute::END_INDEX);
+		if ($startPos < 0 || $endPos < 0) {
+			throw new LogicException();
+		}
+
+		$result = '';
+		$pos = $startPos;
+		$subNodeNames = array_keys(get_object_vars($node));
+		foreach ($subNodeNames as $subNodeName) {
+			$subNode = $node->$subNodeName;
+			$origSubNode = $originalNode->$subNodeName;
+
+			if (
+				(!$subNode instanceof Node && $subNode !== null)
+				|| (!$origSubNode instanceof Node && $origSubNode !== null)
+			) {
+				if ($subNode === $origSubNode) {
+					// Unchanged, can reuse old code
+					continue;
+				}
+
+				if (is_array($subNode) && is_array($origSubNode)) {
+					// Array subnode changed, we might be able to reconstruct it
+					$listResult = $this->printArrayFormatPreserving(
+						$subNode,
+						$origSubNode,
+						$originalTokens,
+						$pos,
+						$class,
+						$subNodeName
+					);
+
+					if ($listResult === null) {
+						return $this->print($node);
+					}
+
+					$result .= $listResult;
+					continue;
+				}
+
+				return $this->print($node);
+			}
+
+			if ($origSubNode === null) {
+				if ($subNode === null) {
+					// Both null, nothing to do
+					continue;
+				}
+
+				return $this->print($node);
+			}
+
+			$subStartPos = $origSubNode->getAttribute(Attribute::START_INDEX);
+			$subEndPos = $origSubNode->getAttribute(Attribute::END_INDEX);
+			if ($subStartPos < 0 || $subEndPos < 0) {
+				throw new LogicException();
+			}
+
+			if ($subEndPos < $subStartPos) {
+				return $this->print($node);
+			}
+
+			if ($subNode === null) {
+				return $this->print($node);
+			}
+
+			$result .= $originalTokens->getContentBetween($pos, $subStartPos);
+			$mapKey = get_class($node) . '->' . $subNodeName;
+			$parenthesesNeeded = isset($this->parenthesesMap[$mapKey])
+				&& in_array(get_class($subNode), $this->parenthesesMap[$mapKey], true);
+
+			if ($subNode->getAttribute(Attribute::ORIGINAL_NODE) !== null) {
+				$parenthesesNeeded = $parenthesesNeeded
+					&& !in_array(get_class($subNode->getAttribute(Attribute::ORIGINAL_NODE)), $this->parenthesesMap[$mapKey], true);
+			}
+
+			$addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($subStartPos, $subEndPos);
+			if ($addParentheses) {
+				$result .= '(';
+			}
+
+			$result .= $this->printNodeFormatPreserving($subNode, $originalTokens);
+			if ($addParentheses) {
+				$result .= ')';
+			}
+
+			$pos = $subEndPos + 1;
+		}
+
+		return $result . $originalTokens->getContentBetween($pos, $endPos + 1);
+	}
+
+}
diff --git a/vendor/psr/http-client/CHANGELOG.md b/vendor/psr/http-client/CHANGELOG.md
index e2dc25f..babba7c 100644
--- a/vendor/psr/http-client/CHANGELOG.md
+++ b/vendor/psr/http-client/CHANGELOG.md
@@ -2,6 +2,14 @@
 
 All notable changes to this project will be documented in this file, in reverse chronological order by release.
 
+## 1.0.3
+
+Add `source` link in composer.json. No code changes.
+
+## 1.0.2
+
+Allow PSR-7 (psr/http-message) 2.0. No code changes.
+
 ## 1.0.1
 
 Allow installation with PHP 8. No code changes.
diff --git a/vendor/psr/http-client/README.md b/vendor/psr/http-client/README.md
index 6876b84..84af5c5 100644
--- a/vendor/psr/http-client/README.md
+++ b/vendor/psr/http-client/README.md
@@ -7,6 +7,6 @@ Note that this is not a HTTP Client implementation of its own. It is merely abst
 
 The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.
 
-[psr-url]: http://www.php-fig.org/psr/psr-18
+[psr-url]: https://www.php-fig.org/psr/psr-18
 [package-url]: https://packagist.org/packages/psr/http-client
 [implementation-url]: https://packagist.org/providers/psr/http-client-implementation
diff --git a/vendor/psr/http-client/composer.json b/vendor/psr/http-client/composer.json
index c195f8f..6fed350 100644
--- a/vendor/psr/http-client/composer.json
+++ b/vendor/psr/http-client/composer.json
@@ -7,12 +7,15 @@
     "authors": [
         {
             "name": "PHP-FIG",
-            "homepage": "http://www.php-fig.org/"
+            "homepage": "https://www.php-fig.org/"
         }
     ],
+    "support": {
+        "source": "https://github.com/php-fig/http-client"
+    },
     "require": {
         "php": "^7.0 || ^8.0",
-        "psr/http-message": "^1.0"
+        "psr/http-message": "^1.0 || ^2.0"
     },
     "autoload": {
         "psr-4": {
diff --git a/vendor/psr/http-factory/.gitignore b/vendor/psr/http-factory/.gitignore
deleted file mode 100644
index d8a7996..0000000
--- a/vendor/psr/http-factory/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-composer.lock
-vendor/
diff --git a/vendor/psr/http-factory/.pullapprove.yml b/vendor/psr/http-factory/.pullapprove.yml
deleted file mode 100644
index 8cf0819..0000000
--- a/vendor/psr/http-factory/.pullapprove.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-extends: default
-reviewers:
-    -
-        name: contributors
-        required: 1
-        teams:
-            - http-factory-contributors
diff --git a/vendor/psr/http-factory/README.md b/vendor/psr/http-factory/README.md
index 41d362a..bf8913b 100644
--- a/vendor/psr/http-factory/README.md
+++ b/vendor/psr/http-factory/README.md
@@ -1,10 +1,12 @@
 HTTP Factories
 ==============
 
-This repository holds all interfaces related to [PSR-17 (HTTP Message Factories)][psr-17]. 
-Please refer to the specification for a description.
+This repository holds all interfaces related to [PSR-17 (HTTP Factories)][psr-url].
 
-You can find implementations of the specification by looking for packages providing the 
-[psr/http-factory-implementation](https://packagist.org/providers/psr/http-factory-implementation) virtual package.
+Note that this is not a HTTP Factory implementation of its own. It is merely interfaces that describe the components of a HTTP Factory.
 
-[psr-17]: https://www.php-fig.org/psr/psr-17/
+The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.
+
+[psr-url]: https://www.php-fig.org/psr/psr-17/
+[package-url]: https://packagist.org/packages/psr/http-factory
+[implementation-url]: https://packagist.org/providers/psr/http-factory-implementation
diff --git a/vendor/psr/http-factory/composer.json b/vendor/psr/http-factory/composer.json
index af62b29..82a1d32 100644
--- a/vendor/psr/http-factory/composer.json
+++ b/vendor/psr/http-factory/composer.json
@@ -1,6 +1,6 @@
 {
     "name": "psr/http-factory",
-    "description": "Common interfaces for PSR-7 HTTP message factories",
+    "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
     "keywords": [
         "psr",
         "psr-7",
@@ -15,12 +15,15 @@
     "authors": [
         {
             "name": "PHP-FIG",
-            "homepage": "http://www.php-fig.org/"
+            "homepage": "https://www.php-fig.org/"
         }
     ],
+    "support": {
+        "source": "https://github.com/php-fig/http-factory"
+    },
     "require": {
-        "php": ">=7.0.0",
-        "psr/http-message": "^1.0"
+        "php": ">=7.1",
+        "psr/http-message": "^1.0 || ^2.0"
     },
     "autoload": {
         "psr-4": {
diff --git a/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php b/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
index 7db4e30..d7adbf0 100644
--- a/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
+++ b/vendor/psr/http-factory/src/UploadedFileFactoryInterface.php
@@ -15,10 +15,10 @@ interface UploadedFileFactoryInterface
      *
      * @param StreamInterface $stream Underlying stream representing the
      *     uploaded file content.
-     * @param int $size in bytes
+     * @param int|null $size in bytes
      * @param int $error PHP file upload error
-     * @param string $clientFilename Filename as provided by the client, if any.
-     * @param string $clientMediaType Media type as provided by the client, if any.
+     * @param string|null $clientFilename Filename as provided by the client, if any.
+     * @param string|null $clientMediaType Media type as provided by the client, if any.
      *
      * @return UploadedFileInterface
      *
@@ -26,9 +26,9 @@ interface UploadedFileFactoryInterface
      */
     public function createUploadedFile(
         StreamInterface $stream,
-        int $size = null,
+        ?int $size = null,
         int $error = \UPLOAD_ERR_OK,
-        string $clientFilename = null,
-        string $clientMediaType = null
+        ?string $clientFilename = null,
+        ?string $clientMediaType = null
     ): UploadedFileInterface;
 }
diff --git a/vendor/psr/http-message/README.md b/vendor/psr/http-message/README.md
index 2818533..2668be6 100644
--- a/vendor/psr/http-message/README.md
+++ b/vendor/psr/http-message/README.md
@@ -10,4 +10,7 @@ interface that describes a HTTP message. See the specification for more details.
 Usage
 -----
 
-We'll certainly need some stuff in here.
\ No newline at end of file
+Before reading the usage guide we recommend reading the PSR-7 interfaces method list:
+
+* [`PSR-7 Interfaces Method List`](docs/PSR7-Interfaces.md)
+* [`PSR-7 Usage Guide`](docs/PSR7-Usage.md)
\ No newline at end of file
diff --git a/vendor/psr/http-message/composer.json b/vendor/psr/http-message/composer.json
index b0d2937..c66e5ab 100644
--- a/vendor/psr/http-message/composer.json
+++ b/vendor/psr/http-message/composer.json
@@ -7,11 +7,11 @@
     "authors": [
         {
             "name": "PHP-FIG",
-            "homepage": "http://www.php-fig.org/"
+            "homepage": "https://www.php-fig.org/"
         }
     ],
     "require": {
-        "php": ">=5.3.0"
+        "php": "^7.2 || ^8.0"
     },
     "autoload": {
         "psr-4": {
@@ -20,7 +20,7 @@
     },
     "extra": {
         "branch-alias": {
-            "dev-master": "1.0.x-dev"
+            "dev-master": "2.0.x-dev"
         }
     }
 }
diff --git a/vendor/psr/http-message/docs/PSR7-Interfaces.md b/vendor/psr/http-message/docs/PSR7-Interfaces.md
new file mode 100644
index 0000000..3a7e7dd
--- /dev/null
+++ b/vendor/psr/http-message/docs/PSR7-Interfaces.md
@@ -0,0 +1,130 @@
+# Interfaces
+
+The purpose of this list is to help in finding the methods when working with PSR-7. This can be considered as a cheatsheet for PSR-7 interfaces.
+
+The interfaces defined in PSR-7 are the following:
+
+| Class Name | Description |
+|---|---|
+| [Psr\Http\Message\MessageInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagemessageinterface) | Representation of a HTTP message |
+| [Psr\Http\Message\RequestInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagerequestinterface) | Representation of an outgoing, client-side request. |
+| [Psr\Http\Message\ServerRequestInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageserverrequestinterface) | Representation of an incoming, server-side HTTP request. | 
+| [Psr\Http\Message\ResponseInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageresponseinterface) | Representation of an outgoing, server-side response. |
+| [Psr\Http\Message\StreamInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessagestreaminterface) | Describes a data stream |
+| [Psr\Http\Message\UriInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageuriinterface) | Value object representing a URI. |
+| [Psr\Http\Message\UploadedFileInterface](http://www.php-fig.org/psr/psr-7/#psrhttpmessageuploadedfileinterface) | Value object representing a file uploaded through an HTTP request. |
+
+## `Psr\Http\Message\MessageInterface` Methods
+
+| Method Name                        | Description | Notes |
+|------------------------------------| ----------- | ----- |
+| `getProtocolVersion()`             | Retrieve HTTP protocol version          |  1.0 or 1.1 |
+| `withProtocolVersion($version)`    | Returns new message instance with given HTTP protocol version          |      |
+| `getHeaders()`                     | Retrieve all HTTP Headers               | [Request Header List](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields), [Response Header List](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields)      |
+| `hasHeader($name)`                 | Checks if HTTP Header with given name exists  | |
+| `getHeader($name)`                 | Retrieves a array with the values for a single header | |
+| `getHeaderLine($name)`             | Retrieves a comma-separated string of the values for a single header |  |
+| `withHeader($name, $value)`        | Returns new message instance with given HTTP Header | if the header existed in the original instance, replaces the header value from the original message with the value provided when creating the new instance. |
+| `withAddedHeader($name, $value)`   | Returns new message instance with appended value to given header | If header already exists value will be appended, if not a new header will be created |
+| `withoutHeader($name)`             | Removes HTTP Header with given name| |
+| `getBody()`                        | Retrieves the HTTP Message Body | Returns object implementing `StreamInterface`|
+| `withBody(StreamInterface $body)`  | Returns new message instance with given HTTP Message Body | |
+
+
+## `Psr\Http\Message\RequestInterface` Methods
+
+Same methods as `Psr\Http\Message\MessageInterface`  + the following methods:
+
+| Method Name                        | Description | Notes |
+|------------------------------------| ----------- | ----- |
+| `getRequestTarget()`                | Retrieves the message's request target              | origin-form, absolute-form, authority-form, asterisk-form ([RFC7230](https://www.rfc-editor.org/rfc/rfc7230.txt)) |
+| `withRequestTarget($requestTarget)` | Return a new message instance with the specific request-target |      |
+| `getMethod()`                       | Retrieves the HTTP method of the request.  |  GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE (defined in [RFC7231](https://tools.ietf.org/html/rfc7231)), PATCH (defined in [RFC5789](https://tools.ietf.org/html/rfc5789)) |
+| `withMethod($method)`               | Returns a new message instance with the provided HTTP method  | |
+| `getUri()`                 | Retrieves the URI instance | |
+| `withUri(UriInterface $uri, $preserveHost = false)` | Returns a new message instance with the provided URI |  |
+
+
+## `Psr\Http\Message\ServerRequestInterface` Methods
+
+Same methods as `Psr\Http\Message\RequestInterface`  + the following methods:
+
+| Method Name                        | Description | Notes |
+|------------------------------------| ----------- | ----- |
+| `getServerParams() `               | Retrieve server parameters  | Typically derived from `$_SERVER`  |
+| `getCookieParams()`                | Retrieves cookies sent by the client to the server. | Typically derived from `$_COOKIES` |
+| `withCookieParams(array $cookies)` |  Returns a new request instance with the specified cookies      |   | 
+| `withQueryParams(array $query)` | Returns a new request instance with the specified query string arguments  |  |
+| `getUploadedFiles()` | Retrieve normalized file upload data  |  |
+| `withUploadedFiles(array $uploadedFiles)` | Returns a new request instance with the specified uploaded files  |  |
+| `getParsedBody()` | Retrieve any parameters provided in the request body  |  |
+| `withParsedBody($data)` | Returns a new request instance with the specified body parameters  |  |
+| `getAttributes()` | Retrieve attributes derived from the request  |  |
+| `getAttribute($name, $default = null)` | Retrieve a single derived request attribute  |  |
+| `withAttribute($name, $value)` | Returns a new request instance with the specified derived request attribute  |  |
+| `withoutAttribute($name)` | Returns a new request instance that without the specified derived request attribute  |  |
+
+## `Psr\Http\Message\ResponseInterface` Methods:
+
+Same methods as `Psr\Http\Message\MessageInterface`  + the following methods:
+
+| Method Name                        | Description | Notes |
+|------------------------------------| ----------- | ----- |
+| `getStatusCode()` | Gets the response status code. | |
+| `withStatus($code, $reasonPhrase = '')` | Returns a new response instance with the specified status code and, optionally, reason phrase. | |
+| `getReasonPhrase()` | Gets the response reason phrase associated with the status code. | |
+
+##  `Psr\Http\Message\StreamInterface` Methods
+
+| Method Name                        | Description | Notes |
+|------------------------------------| ----------- | ----- |
+| `__toString()` | Reads all data from the stream into a string, from the beginning to end. | |
+| `close()` | Closes the stream and any underlying resources. | |
+| `detach()` | Separates any underlying resources from the stream. | |
+| `getSize()` | Get the size of the stream if known. | |
+| `eof()` | Returns true if the stream is at the end of the stream.| |
+| `isSeekable()` |  Returns whether or not the stream is seekable. | |
+| `seek($offset, $whence = SEEK_SET)` | Seek to a position in the stream. | |
+| `rewind()` | Seek to the beginning of the stream. | |
+| `isWritable()` | Returns whether or not the stream is writable. | |
+| `write($string)` | Write data to the stream. | |
+| `isReadable()` | Returns whether or not the stream is readable. | |
+| `read($length)` | Read data from the stream. | |
+| `getContents()` | Returns the remaining contents in a string | |
+| `getMetadata($key = null)()` | Get stream metadata as an associative array or retrieve a specific key. | |
+
+## `Psr\Http\Message\UriInterface` Methods
+
+| Method Name                        | Description | Notes |
+|------------------------------------| ----------- | ----- |
+| `getScheme()` | Retrieve the scheme component of the URI. | |
+| `getAuthority()` | Retrieve the authority component of the URI. | |
+| `getUserInfo()` | Retrieve the user information component of the URI. | |
+| `getHost()` | Retrieve the host component of the URI. | |
+| `getPort()` | Retrieve the port component of the URI. | |
+| `getPath()` | Retrieve the path component of the URI. | |
+| `getQuery()` | Retrieve the query string of the URI. | |
+| `getFragment()` | Retrieve the fragment component of the URI. | |
+| `withScheme($scheme)` | Return an instance with the specified scheme. | |
+| `withUserInfo($user, $password = null)` | Return an instance with the specified user information. | |
+| `withHost($host)` | Return an instance with the specified host. | |
+| `withPort($port)` | Return an instance with the specified port. | |
+| `withPath($path)` | Return an instance with the specified path. | |
+| `withQuery($query)` | Return an instance with the specified query string. | |
+| `withFragment($fragment)` | Return an instance with the specified URI fragment. | |
+| `__toString()` | Return the string representation as a URI reference. | |
+
+## `Psr\Http\Message\UploadedFileInterface` Methods
+
+| Method Name                        | Description | Notes |
+|------------------------------------| ----------- | ----- |
+| `getStream()` | Retrieve a stream representing the uploaded file. | |
+| `moveTo($targetPath)` | Move the uploaded file to a new location. | |
+| `getSize()` | Retrieve the file size. | |
+| `getError()` | Retrieve the error associated with the uploaded file. | |
+| `getClientFilename()` | Retrieve the filename sent by the client. | |
+| `getClientMediaType()` | Retrieve the media type sent by the client. | |
+
+> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface`  because the `Request` and the `Response` are `HTTP Messages`.
+> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered.
+
diff --git a/vendor/psr/http-message/docs/PSR7-Usage.md b/vendor/psr/http-message/docs/PSR7-Usage.md
new file mode 100644
index 0000000..b6d048a
--- /dev/null
+++ b/vendor/psr/http-message/docs/PSR7-Usage.md
@@ -0,0 +1,159 @@
+### PSR-7 Usage
+
+All PSR-7 applications comply with these interfaces 
+They were created to establish a standard between middleware implementations.
+
+> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface`  because the `Request` and the `Response` are `HTTP Messages`.
+> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered.
+
+
+The following examples will illustrate how basic operations are done in PSR-7.
+
+##### Examples
+
+
+For this examples to work (at least) a PSR-7 implementation package is required. (eg: zendframework/zend-diactoros, guzzlehttp/psr7, slim/slim, etc)
+All PSR-7 implementations should have the same behaviour.
+
+The following will be assumed: 
+`$request` is an object of `Psr\Http\Message\RequestInterface` and
+
+`$response` is an object implementing `Psr\Http\Message\RequestInterface`
+
+
+### Working with HTTP Headers
+
+#### Adding headers to response:
+
+```php
+$response->withHeader('My-Custom-Header', 'My Custom Message');
+```
+
+#### Appending values to headers
+
+```php
+$response->withAddedHeader('My-Custom-Header', 'The second message');
+```
+
+#### Checking if header exists:
+
+```php
+$request->hasHeader('My-Custom-Header'); // will return false
+$response->hasHeader('My-Custom-Header'); // will return true
+```
+
+> Note: My-Custom-Header was only added in the Response
+
+#### Getting comma-separated values from a header (also applies to request)
+
+```php
+// getting value from request headers
+$request->getHeaderLine('Content-Type'); // will return: "text/html; charset=UTF-8"
+// getting value from response headers
+$response->getHeaderLine('My-Custom-Header'); // will return:  "My Custom Message; The second message"
+```
+
+#### Getting array of value from a header (also applies to request)
+```php
+// getting value from request headers
+$request->getHeader('Content-Type'); // will return: ["text/html", "charset=UTF-8"]
+// getting value from response headers
+$response->getHeader('My-Custom-Header'); // will return:  ["My Custom Message",  "The second message"]
+```
+
+#### Removing headers from HTTP Messages
+```php
+// removing a header from Request, removing deprecated "Content-MD5" header
+$request->withoutHeader('Content-MD5'); 
+
+// removing a header from Response
+// effect: the browser won't know the size of the stream
+// the browser will download the stream till it ends
+$response->withoutHeader('Content-Length');
+```
+
+### Working with HTTP Message Body
+
+When working with the PSR-7 there are two methods of implementation:
+#### 1. Getting the body separately
+
+> This method makes the body handling easier to understand and is useful when repeatedly calling body methods. (You only call `getBody()` once). Using this method mistakes like `$response->write()` are also prevented.
+
+```php
+$body = $response->getBody();
+// operations on body, eg. read, write, seek
+// ...
+// replacing the old body
+$response->withBody($body); 
+// this last statement is optional as we working with objects
+// in this case the "new" body is same with the "old" one
+// the $body variable has the same value as the one in $request, only the reference is passed
+```
+
+#### 2. Working directly on response
+
+> This method is useful when only performing few operations as the `$request->getBody()` statement fragment is required
+
+```php
+$response->getBody()->write('hello');
+```
+
+### Getting the body contents
+
+The following snippet gets the contents of a stream contents.
+> Note: Streams must be rewinded, if content was written into streams, it will be ignored when calling `getContents()` because the stream pointer is set to the last character, which is `\0` - meaning end of stream.
+```php 
+$body = $response->getBody();
+$body->rewind(); // or $body->seek(0);
+$bodyText = $body->getContents();
+```
+> Note: If `$body->seek(1)` is called before `$body->getContents()`, the first character will be ommited as the starting pointer is set to `1`, not `0`. This is why using `$body->rewind()` is recommended.
+
+### Append to body
+
+```php
+$response->getBody()->write('Hello'); // writing directly
+$body = $request->getBody(); // which is a `StreamInterface`
+$body->write('xxxxx');
+```
+
+### Prepend to body
+Prepending is different when it comes to streams. The content must be copied before writing the content to be prepended.
+The following example will explain the behaviour of streams.
+
+```php
+// assuming our response is initially empty
+$body = $repsonse->getBody();
+// writing the string "abcd"
+$body->write('abcd');
+
+// seeking to start of stream
+$body->seek(0);
+// writing 'ef'
+$body->write('ef'); // at this point the stream contains "efcd"
+```
+
+#### Prepending by rewriting separately
+
+```php
+// assuming our response body stream only contains: "abcd"
+$body = $response->getBody();
+$body->rewind();
+$contents = $body->getContents(); // abcd
+// seeking the stream to beginning
+$body->rewind();
+$body->write('ef'); // stream contains "efcd"
+$body->write($contents); // stream contains "efabcd"
+```
+
+> Note: `getContents()` seeks the stream while reading it, therefore if the second `rewind()` method call was not present the stream would have resulted in `abcdefabcd` because the `write()` method appends to stream if not preceeded by `rewind()` or `seek(0)`.
+
+#### Prepending by using contents as a string
+```php
+$body = $response->getBody();
+$body->rewind();
+$contents = $body->getContents(); // efabcd
+$contents = 'ef'.$contents;
+$body->rewind();
+$body->write($contents);
+```
diff --git a/vendor/psr/http-message/src/MessageInterface.php b/vendor/psr/http-message/src/MessageInterface.php
index dd46e5e..a83c985 100644
--- a/vendor/psr/http-message/src/MessageInterface.php
+++ b/vendor/psr/http-message/src/MessageInterface.php
@@ -23,7 +23,7 @@ interface MessageInterface
      *
      * @return string HTTP protocol version.
      */
-    public function getProtocolVersion();
+    public function getProtocolVersion(): string;
 
     /**
      * Return an instance with the specified HTTP protocol version.
@@ -38,7 +38,7 @@ interface MessageInterface
      * @param string $version HTTP protocol version
      * @return static
      */
-    public function withProtocolVersion($version);
+    public function withProtocolVersion(string $version): MessageInterface;
 
     /**
      * Retrieves all message header values.
@@ -65,7 +65,7 @@ interface MessageInterface
      *     key MUST be a header name, and each value MUST be an array of strings
      *     for that header.
      */
-    public function getHeaders();
+    public function getHeaders(): array;
 
     /**
      * Checks if a header exists by the given case-insensitive name.
@@ -75,7 +75,7 @@ interface MessageInterface
      *     name using a case-insensitive string comparison. Returns false if
      *     no matching header name is found in the message.
      */
-    public function hasHeader($name);
+    public function hasHeader(string $name): bool;
 
     /**
      * Retrieves a message header value by the given case-insensitive name.
@@ -91,7 +91,7 @@ interface MessageInterface
      *    header. If the header does not appear in the message, this method MUST
      *    return an empty array.
      */
-    public function getHeader($name);
+    public function getHeader(string $name): array;
 
     /**
      * Retrieves a comma-separated string of the values for a single header.
@@ -112,7 +112,7 @@ interface MessageInterface
      *    concatenated together using a comma. If the header does not appear in
      *    the message, this method MUST return an empty string.
      */
-    public function getHeaderLine($name);
+    public function getHeaderLine(string $name): string;
 
     /**
      * Return an instance with the provided value replacing the specified header.
@@ -129,7 +129,7 @@ interface MessageInterface
      * @return static
      * @throws \InvalidArgumentException for invalid header names or values.
      */
-    public function withHeader($name, $value);
+    public function withHeader(string $name, $value): MessageInterface;
 
     /**
      * Return an instance with the specified header appended with the given value.
@@ -147,7 +147,7 @@ interface MessageInterface
      * @return static
      * @throws \InvalidArgumentException for invalid header names or values.
      */
-    public function withAddedHeader($name, $value);
+    public function withAddedHeader(string $name, $value): MessageInterface;
 
     /**
      * Return an instance without the specified header.
@@ -161,14 +161,14 @@ interface MessageInterface
      * @param string $name Case-insensitive header field name to remove.
      * @return static
      */
-    public function withoutHeader($name);
+    public function withoutHeader(string $name): MessageInterface;
 
     /**
      * Gets the body of the message.
      *
      * @return StreamInterface Returns the body as a stream.
      */
-    public function getBody();
+    public function getBody(): StreamInterface;
 
     /**
      * Return an instance with the specified message body.
@@ -183,5 +183,5 @@ interface MessageInterface
      * @return static
      * @throws \InvalidArgumentException When the body is not valid.
      */
-    public function withBody(StreamInterface $body);
+    public function withBody(StreamInterface $body): MessageInterface;
 }
diff --git a/vendor/psr/http-message/src/RequestInterface.php b/vendor/psr/http-message/src/RequestInterface.php
index a96d4fd..33f85e5 100644
--- a/vendor/psr/http-message/src/RequestInterface.php
+++ b/vendor/psr/http-message/src/RequestInterface.php
@@ -39,7 +39,7 @@ interface RequestInterface extends MessageInterface
      *
      * @return string
      */
-    public function getRequestTarget();
+    public function getRequestTarget(): string;
 
     /**
      * Return an instance with the specific request-target.
@@ -55,17 +55,18 @@ interface RequestInterface extends MessageInterface
      *
      * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
      *     request-target forms allowed in request messages)
-     * @param mixed $requestTarget
+     * @param string $requestTarget
      * @return static
      */
-    public function withRequestTarget($requestTarget);
+    public function withRequestTarget(string $requestTarget): RequestInterface;
+
 
     /**
      * Retrieves the HTTP method of the request.
      *
      * @return string Returns the request method.
      */
-    public function getMethod();
+    public function getMethod(): string;
 
     /**
      * Return an instance with the provided HTTP method.
@@ -82,7 +83,7 @@ interface RequestInterface extends MessageInterface
      * @return static
      * @throws \InvalidArgumentException for invalid HTTP methods.
      */
-    public function withMethod($method);
+    public function withMethod(string $method): RequestInterface;
 
     /**
      * Retrieves the URI instance.
@@ -93,7 +94,7 @@ interface RequestInterface extends MessageInterface
      * @return UriInterface Returns a UriInterface instance
      *     representing the URI of the request.
      */
-    public function getUri();
+    public function getUri(): UriInterface;
 
     /**
      * Returns an instance with the provided URI.
@@ -125,5 +126,5 @@ interface RequestInterface extends MessageInterface
      * @param bool $preserveHost Preserve the original state of the Host header.
      * @return static
      */
-    public function withUri(UriInterface $uri, $preserveHost = false);
+    public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface;
 }
diff --git a/vendor/psr/http-message/src/ResponseInterface.php b/vendor/psr/http-message/src/ResponseInterface.php
index c306514..e9299a9 100644
--- a/vendor/psr/http-message/src/ResponseInterface.php
+++ b/vendor/psr/http-message/src/ResponseInterface.php
@@ -27,7 +27,7 @@ interface ResponseInterface extends MessageInterface
      *
      * @return int Status code.
      */
-    public function getStatusCode();
+    public function getStatusCode(): int;
 
     /**
      * Return an instance with the specified status code and, optionally, reason phrase.
@@ -49,7 +49,7 @@ interface ResponseInterface extends MessageInterface
      * @return static
      * @throws \InvalidArgumentException For invalid status code arguments.
      */
-    public function withStatus($code, $reasonPhrase = '');
+    public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface;
 
     /**
      * Gets the response reason phrase associated with the status code.
@@ -64,5 +64,5 @@ interface ResponseInterface extends MessageInterface
      * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
      * @return string Reason phrase; must return an empty string if none present.
      */
-    public function getReasonPhrase();
+    public function getReasonPhrase(): string;
 }
diff --git a/vendor/psr/http-message/src/ServerRequestInterface.php b/vendor/psr/http-message/src/ServerRequestInterface.php
index 0251234..8625d0e 100644
--- a/vendor/psr/http-message/src/ServerRequestInterface.php
+++ b/vendor/psr/http-message/src/ServerRequestInterface.php
@@ -51,7 +51,7 @@ interface ServerRequestInterface extends RequestInterface
      *
      * @return array
      */
-    public function getServerParams();
+    public function getServerParams(): array;
 
     /**
      * Retrieve cookies.
@@ -63,7 +63,7 @@ interface ServerRequestInterface extends RequestInterface
      *
      * @return array
      */
-    public function getCookieParams();
+    public function getCookieParams(): array;
 
     /**
      * Return an instance with the specified cookies.
@@ -82,7 +82,7 @@ interface ServerRequestInterface extends RequestInterface
      * @param array $cookies Array of key/value pairs representing cookies.
      * @return static
      */
-    public function withCookieParams(array $cookies);
+    public function withCookieParams(array $cookies): ServerRequestInterface;
 
     /**
      * Retrieve query string arguments.
@@ -96,7 +96,7 @@ interface ServerRequestInterface extends RequestInterface
      *
      * @return array
      */
-    public function getQueryParams();
+    public function getQueryParams(): array;
 
     /**
      * Return an instance with the specified query string arguments.
@@ -120,7 +120,7 @@ interface ServerRequestInterface extends RequestInterface
      *     $_GET.
      * @return static
      */
-    public function withQueryParams(array $query);
+    public function withQueryParams(array $query): ServerRequestInterface;
 
     /**
      * Retrieve normalized file upload data.
@@ -134,7 +134,7 @@ interface ServerRequestInterface extends RequestInterface
      * @return array An array tree of UploadedFileInterface instances; an empty
      *     array MUST be returned if no data is present.
      */
-    public function getUploadedFiles();
+    public function getUploadedFiles(): array;
 
     /**
      * Create a new instance with the specified uploaded files.
@@ -147,7 +147,7 @@ interface ServerRequestInterface extends RequestInterface
      * @return static
      * @throws \InvalidArgumentException if an invalid structure is provided.
      */
-    public function withUploadedFiles(array $uploadedFiles);
+    public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface;
 
     /**
      * Retrieve any parameters provided in the request body.
@@ -194,7 +194,7 @@ interface ServerRequestInterface extends RequestInterface
      * @throws \InvalidArgumentException if an unsupported argument type is
      *     provided.
      */
-    public function withParsedBody($data);
+    public function withParsedBody($data): ServerRequestInterface;
 
     /**
      * Retrieve attributes derived from the request.
@@ -207,7 +207,7 @@ interface ServerRequestInterface extends RequestInterface
      *
      * @return array Attributes derived from the request.
      */
-    public function getAttributes();
+    public function getAttributes(): array;
 
     /**
      * Retrieve a single derived request attribute.
@@ -224,7 +224,7 @@ interface ServerRequestInterface extends RequestInterface
      * @param mixed $default Default value to return if the attribute does not exist.
      * @return mixed
      */
-    public function getAttribute($name, $default = null);
+    public function getAttribute(string $name, $default = null);
 
     /**
      * Return an instance with the specified derived request attribute.
@@ -241,7 +241,7 @@ interface ServerRequestInterface extends RequestInterface
      * @param mixed $value The value of the attribute.
      * @return static
      */
-    public function withAttribute($name, $value);
+    public function withAttribute(string $name, $value): ServerRequestInterface;
 
     /**
      * Return an instance that removes the specified derived request attribute.
@@ -257,5 +257,5 @@ interface ServerRequestInterface extends RequestInterface
      * @param string $name The attribute name.
      * @return static
      */
-    public function withoutAttribute($name);
+    public function withoutAttribute(string $name): ServerRequestInterface;
 }
diff --git a/vendor/psr/http-message/src/StreamInterface.php b/vendor/psr/http-message/src/StreamInterface.php
index f68f391..a62aabb 100644
--- a/vendor/psr/http-message/src/StreamInterface.php
+++ b/vendor/psr/http-message/src/StreamInterface.php
@@ -25,14 +25,14 @@ interface StreamInterface
      * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
      * @return string
      */
-    public function __toString();
+    public function __toString(): string;
 
     /**
      * Closes the stream and any underlying resources.
      *
      * @return void
      */
-    public function close();
+    public function close(): void;
 
     /**
      * Separates any underlying resources from the stream.
@@ -48,7 +48,7 @@ interface StreamInterface
      *
      * @return int|null Returns the size in bytes if known, or null if unknown.
      */
-    public function getSize();
+    public function getSize(): ?int;
 
     /**
      * Returns the current position of the file read/write pointer
@@ -56,21 +56,21 @@ interface StreamInterface
      * @return int Position of the file pointer
      * @throws \RuntimeException on error.
      */
-    public function tell();
+    public function tell(): int;
 
     /**
      * Returns true if the stream is at the end of the stream.
      *
      * @return bool
      */
-    public function eof();
+    public function eof(): bool;
 
     /**
      * Returns whether or not the stream is seekable.
      *
      * @return bool
      */
-    public function isSeekable();
+    public function isSeekable(): bool;
 
     /**
      * Seek to a position in the stream.
@@ -84,7 +84,7 @@ interface StreamInterface
      *     SEEK_END: Set position to end-of-stream plus offset.
      * @throws \RuntimeException on failure.
      */
-    public function seek($offset, $whence = SEEK_SET);
+    public function seek(int $offset, int $whence = SEEK_SET): void;
 
     /**
      * Seek to the beginning of the stream.
@@ -96,14 +96,14 @@ interface StreamInterface
      * @link http://www.php.net/manual/en/function.fseek.php
      * @throws \RuntimeException on failure.
      */
-    public function rewind();
+    public function rewind(): void;
 
     /**
      * Returns whether or not the stream is writable.
      *
      * @return bool
      */
-    public function isWritable();
+    public function isWritable(): bool;
 
     /**
      * Write data to the stream.
@@ -112,14 +112,14 @@ interface StreamInterface
      * @return int Returns the number of bytes written to the stream.
      * @throws \RuntimeException on failure.
      */
-    public function write($string);
+    public function write(string $string): int;
 
     /**
      * Returns whether or not the stream is readable.
      *
      * @return bool
      */
-    public function isReadable();
+    public function isReadable(): bool;
 
     /**
      * Read data from the stream.
@@ -131,7 +131,7 @@ interface StreamInterface
      *     if no bytes are available.
      * @throws \RuntimeException if an error occurs.
      */
-    public function read($length);
+    public function read(int $length): string;
 
     /**
      * Returns the remaining contents in a string
@@ -140,7 +140,7 @@ interface StreamInterface
      * @throws \RuntimeException if unable to read or an error occurs while
      *     reading.
      */
-    public function getContents();
+    public function getContents(): string;
 
     /**
      * Get stream metadata as an associative array or retrieve a specific key.
@@ -149,10 +149,10 @@ interface StreamInterface
      * stream_get_meta_data() function.
      *
      * @link http://php.net/manual/en/function.stream-get-meta-data.php
-     * @param string $key Specific metadata to retrieve.
+     * @param string|null $key Specific metadata to retrieve.
      * @return array|mixed|null Returns an associative array if no key is
      *     provided. Returns a specific key value if a key is provided and the
      *     value is found, or null if the key is not found.
      */
-    public function getMetadata($key = null);
+    public function getMetadata(?string $key = null);
 }
diff --git a/vendor/psr/http-message/src/UploadedFileInterface.php b/vendor/psr/http-message/src/UploadedFileInterface.php
index f8a6901..dd19d65 100644
--- a/vendor/psr/http-message/src/UploadedFileInterface.php
+++ b/vendor/psr/http-message/src/UploadedFileInterface.php
@@ -28,7 +28,7 @@ interface UploadedFileInterface
      * @throws \RuntimeException in cases when no stream is available or can be
      *     created.
      */
-    public function getStream();
+    public function getStream(): StreamInterface;
 
     /**
      * Move the uploaded file to a new location.
@@ -62,7 +62,7 @@ interface UploadedFileInterface
      * @throws \RuntimeException on any error during the move operation, or on
      *     the second or subsequent call to the method.
      */
-    public function moveTo($targetPath);
+    public function moveTo(string $targetPath): void;
     
     /**
      * Retrieve the file size.
@@ -73,7 +73,7 @@ interface UploadedFileInterface
      *
      * @return int|null The file size in bytes or null if unknown.
      */
-    public function getSize();
+    public function getSize(): ?int;
     
     /**
      * Retrieve the error associated with the uploaded file.
@@ -89,7 +89,7 @@ interface UploadedFileInterface
      * @see http://php.net/manual/en/features.file-upload.errors.php
      * @return int One of PHP's UPLOAD_ERR_XXX constants.
      */
-    public function getError();
+    public function getError(): int;
     
     /**
      * Retrieve the filename sent by the client.
@@ -104,7 +104,7 @@ interface UploadedFileInterface
      * @return string|null The filename sent by the client or null if none
      *     was provided.
      */
-    public function getClientFilename();
+    public function getClientFilename(): ?string;
     
     /**
      * Retrieve the media type sent by the client.
@@ -119,5 +119,5 @@ interface UploadedFileInterface
      * @return string|null The media type sent by the client or null if none
      *     was provided.
      */
-    public function getClientMediaType();
+    public function getClientMediaType(): ?string;
 }
diff --git a/vendor/psr/http-message/src/UriInterface.php b/vendor/psr/http-message/src/UriInterface.php
index 9d7ab9e..15e2cf2 100644
--- a/vendor/psr/http-message/src/UriInterface.php
+++ b/vendor/psr/http-message/src/UriInterface.php
@@ -1,4 +1,5 @@
 <?php
+
 namespace Psr\Http\Message;
 
 /**
@@ -37,7 +38,7 @@ interface UriInterface
      * @see https://tools.ietf.org/html/rfc3986#section-3.1
      * @return string The URI scheme.
      */
-    public function getScheme();
+    public function getScheme(): string;
 
     /**
      * Retrieve the authority component of the URI.
@@ -57,7 +58,7 @@ interface UriInterface
      * @see https://tools.ietf.org/html/rfc3986#section-3.2
      * @return string The URI authority, in "[user-info@]host[:port]" format.
      */
-    public function getAuthority();
+    public function getAuthority(): string;
 
     /**
      * Retrieve the user information component of the URI.
@@ -74,7 +75,7 @@ interface UriInterface
      *
      * @return string The URI user information, in "username[:password]" format.
      */
-    public function getUserInfo();
+    public function getUserInfo(): string;
 
     /**
      * Retrieve the host component of the URI.
@@ -87,7 +88,7 @@ interface UriInterface
      * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
      * @return string The URI host.
      */
-    public function getHost();
+    public function getHost(): string;
 
     /**
      * Retrieve the port component of the URI.
@@ -104,7 +105,7 @@ interface UriInterface
      *
      * @return null|int The URI port.
      */
-    public function getPort();
+    public function getPort(): ?int;
 
     /**
      * Retrieve the path component of the URI.
@@ -131,7 +132,7 @@ interface UriInterface
      * @see https://tools.ietf.org/html/rfc3986#section-3.3
      * @return string The URI path.
      */
-    public function getPath();
+    public function getPath(): string;
 
     /**
      * Retrieve the query string of the URI.
@@ -153,7 +154,7 @@ interface UriInterface
      * @see https://tools.ietf.org/html/rfc3986#section-3.4
      * @return string The URI query string.
      */
-    public function getQuery();
+    public function getQuery(): string;
 
     /**
      * Retrieve the fragment component of the URI.
@@ -171,7 +172,7 @@ interface UriInterface
      * @see https://tools.ietf.org/html/rfc3986#section-3.5
      * @return string The URI fragment.
      */
-    public function getFragment();
+    public function getFragment(): string;
 
     /**
      * Return an instance with the specified scheme.
@@ -188,7 +189,7 @@ interface UriInterface
      * @return static A new instance with the specified scheme.
      * @throws \InvalidArgumentException for invalid or unsupported schemes.
      */
-    public function withScheme($scheme);
+    public function withScheme(string $scheme): UriInterface;
 
     /**
      * Return an instance with the specified user information.
@@ -204,7 +205,7 @@ interface UriInterface
      * @param null|string $password The password associated with $user.
      * @return static A new instance with the specified user information.
      */
-    public function withUserInfo($user, $password = null);
+    public function withUserInfo(string $user, ?string $password = null): UriInterface;
 
     /**
      * Return an instance with the specified host.
@@ -218,7 +219,7 @@ interface UriInterface
      * @return static A new instance with the specified host.
      * @throws \InvalidArgumentException for invalid hostnames.
      */
-    public function withHost($host);
+    public function withHost(string $host): UriInterface;
 
     /**
      * Return an instance with the specified port.
@@ -237,7 +238,7 @@ interface UriInterface
      * @return static A new instance with the specified port.
      * @throws \InvalidArgumentException for invalid ports.
      */
-    public function withPort($port);
+    public function withPort(?int $port): UriInterface;
 
     /**
      * Return an instance with the specified path.
@@ -261,7 +262,7 @@ interface UriInterface
      * @return static A new instance with the specified path.
      * @throws \InvalidArgumentException for invalid paths.
      */
-    public function withPath($path);
+    public function withPath(string $path): UriInterface;
 
     /**
      * Return an instance with the specified query string.
@@ -278,7 +279,7 @@ interface UriInterface
      * @return static A new instance with the specified query string.
      * @throws \InvalidArgumentException for invalid query strings.
      */
-    public function withQuery($query);
+    public function withQuery(string $query): UriInterface;
 
     /**
      * Return an instance with the specified URI fragment.
@@ -294,7 +295,7 @@ interface UriInterface
      * @param string $fragment The fragment to use with the new instance.
      * @return static A new instance with the specified fragment.
      */
-    public function withFragment($fragment);
+    public function withFragment(string $fragment): UriInterface;
 
     /**
      * Return the string representation as a URI reference.
@@ -319,5 +320,5 @@ interface UriInterface
      * @see http://tools.ietf.org/html/rfc3986#section-4.1
      * @return string
      */
-    public function __toString();
+    public function __toString(): string;
 }
diff --git a/vendor/psr/log/Psr/Log/AbstractLogger.php b/vendor/psr/log/Psr/Log/AbstractLogger.php
deleted file mode 100644
index e02f9da..0000000
--- a/vendor/psr/log/Psr/Log/AbstractLogger.php
+++ /dev/null
@@ -1,128 +0,0 @@
-<?php
-
-namespace Psr\Log;
-
-/**
- * This is a simple Logger implementation that other Loggers can inherit from.
- *
- * It simply delegates all log-level-specific methods to the `log` method to
- * reduce boilerplate code that a simple Logger that does the same thing with
- * messages regardless of the error level has to implement.
- */
-abstract class AbstractLogger implements LoggerInterface
-{
-    /**
-     * System is unusable.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function emergency($message, array $context = array())
-    {
-        $this->log(LogLevel::EMERGENCY, $message, $context);
-    }
-
-    /**
-     * Action must be taken immediately.
-     *
-     * Example: Entire website down, database unavailable, etc. This should
-     * trigger the SMS alerts and wake you up.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function alert($message, array $context = array())
-    {
-        $this->log(LogLevel::ALERT, $message, $context);
-    }
-
-    /**
-     * Critical conditions.
-     *
-     * Example: Application component unavailable, unexpected exception.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function critical($message, array $context = array())
-    {
-        $this->log(LogLevel::CRITICAL, $message, $context);
-    }
-
-    /**
-     * Runtime errors that do not require immediate action but should typically
-     * be logged and monitored.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function error($message, array $context = array())
-    {
-        $this->log(LogLevel::ERROR, $message, $context);
-    }
-
-    /**
-     * Exceptional occurrences that are not errors.
-     *
-     * Example: Use of deprecated APIs, poor use of an API, undesirable things
-     * that are not necessarily wrong.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function warning($message, array $context = array())
-    {
-        $this->log(LogLevel::WARNING, $message, $context);
-    }
-
-    /**
-     * Normal but significant events.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function notice($message, array $context = array())
-    {
-        $this->log(LogLevel::NOTICE, $message, $context);
-    }
-
-    /**
-     * Interesting events.
-     *
-     * Example: User logs in, SQL logs.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function info($message, array $context = array())
-    {
-        $this->log(LogLevel::INFO, $message, $context);
-    }
-
-    /**
-     * Detailed debug information.
-     *
-     * @param string  $message
-     * @param mixed[] $context
-     *
-     * @return void
-     */
-    public function debug($message, array $context = array())
-    {
-        $this->log(LogLevel::DEBUG, $message, $context);
-    }
-}
diff --git a/vendor/psr/log/Psr/Log/Test/DummyTest.php b/vendor/psr/log/Psr/Log/Test/DummyTest.php
deleted file mode 100644
index 9638c11..0000000
--- a/vendor/psr/log/Psr/Log/Test/DummyTest.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-namespace Psr\Log\Test;
-
-/**
- * This class is internal and does not follow the BC promise.
- *
- * Do NOT use this class in any way.
- *
- * @internal
- */
-class DummyTest
-{
-    public function __toString()
-    {
-        return 'DummyTest';
-    }
-}
diff --git a/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php
deleted file mode 100644
index e1e5354..0000000
--- a/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Psr\Log\Test;
-
-use Psr\Log\LoggerInterface;
-use Psr\Log\LogLevel;
-use PHPUnit\Framework\TestCase;
-
-/**
- * Provides a base test class for ensuring compliance with the LoggerInterface.
- *
- * Implementors can extend the class and implement abstract methods to run this
- * as part of their test suite.
- */
-abstract class LoggerInterfaceTest extends TestCase
-{
-    /**
-     * @return LoggerInterface
-     */
-    abstract public function getLogger();
-
-    /**
-     * This must return the log messages in order.
-     *
-     * The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
-     *
-     * Example ->error('Foo') would yield "error Foo".
-     *
-     * @return string[]
-     */
-    abstract public function getLogs();
-
-    public function testImplements()
-    {
-        $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
-    }
-
-    /**
-     * @dataProvider provideLevelsAndMessages
-     */
-    public function testLogsAtAllLevels($level, $message)
-    {
-        $logger = $this->getLogger();
-        $logger->{$level}($message, array('user' => 'Bob'));
-        $logger->log($level, $message, array('user' => 'Bob'));
-
-        $expected = array(
-            $level.' message of level '.$level.' with context: Bob',
-            $level.' message of level '.$level.' with context: Bob',
-        );
-        $this->assertEquals($expected, $this->getLogs());
-    }
-
-    public function provideLevelsAndMessages()
-    {
-        return array(
-            LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
-            LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
-            LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
-            LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
-            LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
-            LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
-            LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
-            LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
-        );
-    }
-
-    /**
-     * @expectedException \Psr\Log\InvalidArgumentException
-     */
-    public function testThrowsOnInvalidLevel()
-    {
-        $logger = $this->getLogger();
-        $logger->log('invalid level', 'Foo');
-    }
-
-    public function testContextReplacement()
-    {
-        $logger = $this->getLogger();
-        $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
-
-        $expected = array('info {Message {nothing} Bob Bar a}');
-        $this->assertEquals($expected, $this->getLogs());
-    }
-
-    public function testObjectCastToString()
-    {
-        if (method_exists($this, 'createPartialMock')) {
-            $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString'));
-        } else {
-            $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
-        }
-        $dummy->expects($this->once())
-            ->method('__toString')
-            ->will($this->returnValue('DUMMY'));
-
-        $this->getLogger()->warning($dummy);
-
-        $expected = array('warning DUMMY');
-        $this->assertEquals($expected, $this->getLogs());
-    }
-
-    public function testContextCanContainAnything()
-    {
-        $closed = fopen('php://memory', 'r');
-        fclose($closed);
-
-        $context = array(
-            'bool' => true,
-            'null' => null,
-            'string' => 'Foo',
-            'int' => 0,
-            'float' => 0.5,
-            'nested' => array('with object' => new DummyTest),
-            'object' => new \DateTime,
-            'resource' => fopen('php://memory', 'r'),
-            'closed' => $closed,
-        );
-
-        $this->getLogger()->warning('Crazy context data', $context);
-
-        $expected = array('warning Crazy context data');
-        $this->assertEquals($expected, $this->getLogs());
-    }
-
-    public function testContextExceptionKeyCanBeExceptionOrOtherValues()
-    {
-        $logger = $this->getLogger();
-        $logger->warning('Random message', array('exception' => 'oops'));
-        $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));
-
-        $expected = array(
-            'warning Random message',
-            'critical Uncaught Exception!'
-        );
-        $this->assertEquals($expected, $this->getLogs());
-    }
-}
diff --git a/vendor/psr/log/Psr/Log/Test/TestLogger.php b/vendor/psr/log/Psr/Log/Test/TestLogger.php
deleted file mode 100644
index 1be3230..0000000
--- a/vendor/psr/log/Psr/Log/Test/TestLogger.php
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-
-namespace Psr\Log\Test;
-
-use Psr\Log\AbstractLogger;
-
-/**
- * Used for testing purposes.
- *
- * It records all records and gives you access to them for verification.
- *
- * @method bool hasEmergency($record)
- * @method bool hasAlert($record)
- * @method bool hasCritical($record)
- * @method bool hasError($record)
- * @method bool hasWarning($record)
- * @method bool hasNotice($record)
- * @method bool hasInfo($record)
- * @method bool hasDebug($record)
- *
- * @method bool hasEmergencyRecords()
- * @method bool hasAlertRecords()
- * @method bool hasCriticalRecords()
- * @method bool hasErrorRecords()
- * @method bool hasWarningRecords()
- * @method bool hasNoticeRecords()
- * @method bool hasInfoRecords()
- * @method bool hasDebugRecords()
- *
- * @method bool hasEmergencyThatContains($message)
- * @method bool hasAlertThatContains($message)
- * @method bool hasCriticalThatContains($message)
- * @method bool hasErrorThatContains($message)
- * @method bool hasWarningThatContains($message)
- * @method bool hasNoticeThatContains($message)
- * @method bool hasInfoThatContains($message)
- * @method bool hasDebugThatContains($message)
- *
- * @method bool hasEmergencyThatMatches($message)
- * @method bool hasAlertThatMatches($message)
- * @method bool hasCriticalThatMatches($message)
- * @method bool hasErrorThatMatches($message)
- * @method bool hasWarningThatMatches($message)
- * @method bool hasNoticeThatMatches($message)
- * @method bool hasInfoThatMatches($message)
- * @method bool hasDebugThatMatches($message)
- *
- * @method bool hasEmergencyThatPasses($message)
- * @method bool hasAlertThatPasses($message)
- * @method bool hasCriticalThatPasses($message)
- * @method bool hasErrorThatPasses($message)
- * @method bool hasWarningThatPasses($message)
- * @method bool hasNoticeThatPasses($message)
- * @method bool hasInfoThatPasses($message)
- * @method bool hasDebugThatPasses($message)
- */
-class TestLogger extends AbstractLogger
-{
-    /**
-     * @var array
-     */
-    public $records = [];
-
-    public $recordsByLevel = [];
-
-    /**
-     * @inheritdoc
-     */
-    public function log($level, $message, array $context = [])
-    {
-        $record = [
-            'level' => $level,
-            'message' => $message,
-            'context' => $context,
-        ];
-
-        $this->recordsByLevel[$record['level']][] = $record;
-        $this->records[] = $record;
-    }
-
-    public function hasRecords($level)
-    {
-        return isset($this->recordsByLevel[$level]);
-    }
-
-    public function hasRecord($record, $level)
-    {
-        if (is_string($record)) {
-            $record = ['message' => $record];
-        }
-        return $this->hasRecordThatPasses(function ($rec) use ($record) {
-            if ($rec['message'] !== $record['message']) {
-                return false;
-            }
-            if (isset($record['context']) && $rec['context'] !== $record['context']) {
-                return false;
-            }
-            return true;
-        }, $level);
-    }
-
-    public function hasRecordThatContains($message, $level)
-    {
-        return $this->hasRecordThatPasses(function ($rec) use ($message) {
-            return strpos($rec['message'], $message) !== false;
-        }, $level);
-    }
-
-    public function hasRecordThatMatches($regex, $level)
-    {
-        return $this->hasRecordThatPasses(function ($rec) use ($regex) {
-            return preg_match($regex, $rec['message']) > 0;
-        }, $level);
-    }
-
-    public function hasRecordThatPasses(callable $predicate, $level)
-    {
-        if (!isset($this->recordsByLevel[$level])) {
-            return false;
-        }
-        foreach ($this->recordsByLevel[$level] as $i => $rec) {
-            if (call_user_func($predicate, $rec, $i)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public function __call($method, $args)
-    {
-        if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
-            $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
-            $level = strtolower($matches[2]);
-            if (method_exists($this, $genericMethod)) {
-                $args[] = $level;
-                return call_user_func_array([$this, $genericMethod], $args);
-            }
-        }
-        throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()');
-    }
-
-    public function reset()
-    {
-        $this->records = [];
-        $this->recordsByLevel = [];
-    }
-}
diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json
index ca05695..879fc6f 100644
--- a/vendor/psr/log/composer.json
+++ b/vendor/psr/log/composer.json
@@ -11,16 +11,16 @@
         }
     ],
     "require": {
-        "php": ">=5.3.0"
+        "php": ">=8.0.0"
     },
     "autoload": {
         "psr-4": {
-            "Psr\\Log\\": "Psr/Log/"
+            "Psr\\Log\\": "src"
         }
     },
     "extra": {
         "branch-alias": {
-            "dev-master": "1.1.x-dev"
+            "dev-master": "3.x-dev"
         }
     }
 }
diff --git a/vendor/psr/log/src/AbstractLogger.php b/vendor/psr/log/src/AbstractLogger.php
new file mode 100644
index 0000000..d60a091
--- /dev/null
+++ b/vendor/psr/log/src/AbstractLogger.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * This is a simple Logger implementation that other Loggers can inherit from.
+ *
+ * It simply delegates all log-level-specific methods to the `log` method to
+ * reduce boilerplate code that a simple Logger that does the same thing with
+ * messages regardless of the error level has to implement.
+ */
+abstract class AbstractLogger implements LoggerInterface
+{
+    use LoggerTrait;
+}
diff --git a/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/vendor/psr/log/src/InvalidArgumentException.php
similarity index 100%
rename from vendor/psr/log/Psr/Log/InvalidArgumentException.php
rename to vendor/psr/log/src/InvalidArgumentException.php
diff --git a/vendor/psr/log/Psr/Log/LogLevel.php b/vendor/psr/log/src/LogLevel.php
similarity index 100%
rename from vendor/psr/log/Psr/Log/LogLevel.php
rename to vendor/psr/log/src/LogLevel.php
diff --git a/vendor/psr/log/Psr/Log/LoggerAwareInterface.php b/vendor/psr/log/src/LoggerAwareInterface.php
similarity index 56%
rename from vendor/psr/log/Psr/Log/LoggerAwareInterface.php
rename to vendor/psr/log/src/LoggerAwareInterface.php
index 4d64f47..0621870 100644
--- a/vendor/psr/log/Psr/Log/LoggerAwareInterface.php
+++ b/vendor/psr/log/src/LoggerAwareInterface.php
@@ -9,10 +9,6 @@ interface LoggerAwareInterface
 {
     /**
      * Sets a logger instance on the object.
-     *
-     * @param LoggerInterface $logger
-     *
-     * @return void
      */
-    public function setLogger(LoggerInterface $logger);
+    public function setLogger(LoggerInterface $logger): void;
 }
diff --git a/vendor/psr/log/Psr/Log/LoggerAwareTrait.php b/vendor/psr/log/src/LoggerAwareTrait.php
similarity index 59%
rename from vendor/psr/log/Psr/Log/LoggerAwareTrait.php
rename to vendor/psr/log/src/LoggerAwareTrait.php
index 82bf45c..85104db 100644
--- a/vendor/psr/log/Psr/Log/LoggerAwareTrait.php
+++ b/vendor/psr/log/src/LoggerAwareTrait.php
@@ -9,17 +9,13 @@ trait LoggerAwareTrait
 {
     /**
      * The logger instance.
-     *
-     * @var LoggerInterface|null
      */
-    protected $logger;
+    protected ?LoggerInterface $logger = null;
 
     /**
      * Sets a logger.
-     *
-     * @param LoggerInterface $logger
      */
-    public function setLogger(LoggerInterface $logger)
+    public function setLogger(LoggerInterface $logger): void
     {
         $this->logger = $logger;
     }
diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/src/LoggerInterface.php
similarity index 63%
rename from vendor/psr/log/Psr/Log/LoggerInterface.php
rename to vendor/psr/log/src/LoggerInterface.php
index 2206cfd..cb4cf64 100644
--- a/vendor/psr/log/Psr/Log/LoggerInterface.php
+++ b/vendor/psr/log/src/LoggerInterface.php
@@ -22,12 +22,9 @@ interface LoggerInterface
     /**
      * System is unusable.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function emergency($message, array $context = array());
+    public function emergency(string|\Stringable $message, array $context = []): void;
 
     /**
      * Action must be taken immediately.
@@ -35,35 +32,26 @@ interface LoggerInterface
      * Example: Entire website down, database unavailable, etc. This should
      * trigger the SMS alerts and wake you up.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function alert($message, array $context = array());
+    public function alert(string|\Stringable $message, array $context = []): void;
 
     /**
      * Critical conditions.
      *
      * Example: Application component unavailable, unexpected exception.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function critical($message, array $context = array());
+    public function critical(string|\Stringable $message, array $context = []): void;
 
     /**
      * Runtime errors that do not require immediate action but should typically
      * be logged and monitored.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function error($message, array $context = array());
+    public function error(string|\Stringable $message, array $context = []): void;
 
     /**
      * Exceptional occurrences that are not errors.
@@ -71,55 +59,40 @@ interface LoggerInterface
      * Example: Use of deprecated APIs, poor use of an API, undesirable things
      * that are not necessarily wrong.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function warning($message, array $context = array());
+    public function warning(string|\Stringable $message, array $context = []): void;
 
     /**
      * Normal but significant events.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function notice($message, array $context = array());
+    public function notice(string|\Stringable $message, array $context = []): void;
 
     /**
      * Interesting events.
      *
      * Example: User logs in, SQL logs.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function info($message, array $context = array());
+    public function info(string|\Stringable $message, array $context = []): void;
 
     /**
      * Detailed debug information.
      *
-     * @param string  $message
      * @param mixed[] $context
-     *
-     * @return void
      */
-    public function debug($message, array $context = array());
+    public function debug(string|\Stringable $message, array $context = []): void;
 
     /**
      * Logs with an arbitrary level.
      *
-     * @param mixed   $level
-     * @param string  $message
+     * @param mixed $level
      * @param mixed[] $context
      *
-     * @return void
-     *
      * @throws \Psr\Log\InvalidArgumentException
      */
-    public function log($level, $message, array $context = array());
+    public function log($level, string|\Stringable $message, array $context = []): void;
 }
diff --git a/vendor/psr/log/Psr/Log/LoggerTrait.php b/vendor/psr/log/src/LoggerTrait.php
similarity index 57%
rename from vendor/psr/log/Psr/Log/LoggerTrait.php
rename to vendor/psr/log/src/LoggerTrait.php
index e392fef..a5d9980 100644
--- a/vendor/psr/log/Psr/Log/LoggerTrait.php
+++ b/vendor/psr/log/src/LoggerTrait.php
@@ -14,13 +14,8 @@ trait LoggerTrait
 {
     /**
      * System is unusable.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function emergency($message, array $context = array())
+    public function emergency(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::EMERGENCY, $message, $context);
     }
@@ -30,13 +25,8 @@ trait LoggerTrait
      *
      * Example: Entire website down, database unavailable, etc. This should
      * trigger the SMS alerts and wake you up.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function alert($message, array $context = array())
+    public function alert(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::ALERT, $message, $context);
     }
@@ -45,13 +35,8 @@ trait LoggerTrait
      * Critical conditions.
      *
      * Example: Application component unavailable, unexpected exception.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function critical($message, array $context = array())
+    public function critical(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::CRITICAL, $message, $context);
     }
@@ -59,13 +44,8 @@ trait LoggerTrait
     /**
      * Runtime errors that do not require immediate action but should typically
      * be logged and monitored.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function error($message, array $context = array())
+    public function error(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::ERROR, $message, $context);
     }
@@ -75,26 +55,16 @@ trait LoggerTrait
      *
      * Example: Use of deprecated APIs, poor use of an API, undesirable things
      * that are not necessarily wrong.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function warning($message, array $context = array())
+    public function warning(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::WARNING, $message, $context);
     }
 
     /**
      * Normal but significant events.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function notice($message, array $context = array())
+    public function notice(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::NOTICE, $message, $context);
     }
@@ -103,26 +73,16 @@ trait LoggerTrait
      * Interesting events.
      *
      * Example: User logs in, SQL logs.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function info($message, array $context = array())
+    public function info(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::INFO, $message, $context);
     }
 
     /**
      * Detailed debug information.
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
      */
-    public function debug($message, array $context = array())
+    public function debug(string|\Stringable $message, array $context = []): void
     {
         $this->log(LogLevel::DEBUG, $message, $context);
     }
@@ -130,13 +90,9 @@ trait LoggerTrait
     /**
      * Logs with an arbitrary level.
      *
-     * @param mixed  $level
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
+     * @param mixed $level
      *
      * @throws \Psr\Log\InvalidArgumentException
      */
-    abstract public function log($level, $message, array $context = array());
+    abstract public function log($level, string|\Stringable $message, array $context = []): void;
 }
diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/src/NullLogger.php
similarity index 74%
rename from vendor/psr/log/Psr/Log/NullLogger.php
rename to vendor/psr/log/src/NullLogger.php
index c8f7293..de0561e 100644
--- a/vendor/psr/log/Psr/Log/NullLogger.php
+++ b/vendor/psr/log/src/NullLogger.php
@@ -15,15 +15,11 @@ class NullLogger extends AbstractLogger
     /**
      * Logs with an arbitrary level.
      *
-     * @param mixed  $level
-     * @param string $message
-     * @param array  $context
-     *
-     * @return void
+     * @param mixed[] $context
      *
      * @throws \Psr\Log\InvalidArgumentException
      */
-    public function log($level, $message, array $context = array())
+    public function log($level, string|\Stringable $message, array $context = []): void
     {
         // noop
     }
diff --git a/vendor/psr/simple-cache/composer.json b/vendor/psr/simple-cache/composer.json
index 2978fa5..f307a84 100644
--- a/vendor/psr/simple-cache/composer.json
+++ b/vendor/psr/simple-cache/composer.json
@@ -6,11 +6,11 @@
     "authors": [
         {
             "name": "PHP-FIG",
-            "homepage": "http://www.php-fig.org/"
+            "homepage": "https://www.php-fig.org/"
         }
     ],
     "require": {
-        "php": ">=5.3.0"
+        "php": ">=8.0.0"
     },
     "autoload": {
         "psr-4": {
@@ -19,7 +19,7 @@
     },
     "extra": {
         "branch-alias": {
-            "dev-master": "1.0.x-dev"
+            "dev-master": "3.0.x-dev"
         }
     }
 }
diff --git a/vendor/psr/simple-cache/src/CacheException.php b/vendor/psr/simple-cache/src/CacheException.php
index eba5381..f61b24c 100644
--- a/vendor/psr/simple-cache/src/CacheException.php
+++ b/vendor/psr/simple-cache/src/CacheException.php
@@ -5,6 +5,6 @@ namespace Psr\SimpleCache;
 /**
  * Interface used for all types of exceptions thrown by the implementing library.
  */
-interface CacheException
+interface CacheException extends \Throwable
 {
 }
diff --git a/vendor/psr/simple-cache/src/CacheInterface.php b/vendor/psr/simple-cache/src/CacheInterface.php
index 99e8d95..671e340 100644
--- a/vendor/psr/simple-cache/src/CacheInterface.php
+++ b/vendor/psr/simple-cache/src/CacheInterface.php
@@ -15,7 +15,7 @@ interface CacheInterface
      * @throws \Psr\SimpleCache\InvalidArgumentException
      *   MUST be thrown if the $key string is not a legal value.
      */
-    public function get($key, $default = null);
+    public function get(string $key, mixed $default = null): mixed;
 
     /**
      * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
@@ -31,7 +31,7 @@ interface CacheInterface
      * @throws \Psr\SimpleCache\InvalidArgumentException
      *   MUST be thrown if the $key string is not a legal value.
      */
-    public function set($key, $value, $ttl = null);
+    public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool;
 
     /**
      * Delete an item from the cache by its unique key.
@@ -43,28 +43,28 @@ interface CacheInterface
      * @throws \Psr\SimpleCache\InvalidArgumentException
      *   MUST be thrown if the $key string is not a legal value.
      */
-    public function delete($key);
+    public function delete(string $key): bool;
 
     /**
      * Wipes clean the entire cache's keys.
      *
      * @return bool True on success and false on failure.
      */
-    public function clear();
+    public function clear(): bool;
 
     /**
      * Obtains multiple cache items by their unique keys.
      *
-     * @param iterable $keys    A list of keys that can obtained in a single operation.
-     * @param mixed    $default Default value to return for keys that do not exist.
+     * @param iterable<string> $keys    A list of keys that can be obtained in a single operation.
+     * @param mixed            $default Default value to return for keys that do not exist.
      *
-     * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
+     * @return iterable<string, mixed> A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
      *
      * @throws \Psr\SimpleCache\InvalidArgumentException
      *   MUST be thrown if $keys is neither an array nor a Traversable,
      *   or if any of the $keys are not a legal value.
      */
-    public function getMultiple($keys, $default = null);
+    public function getMultiple(iterable $keys, mixed $default = null): iterable;
 
     /**
      * Persists a set of key => value pairs in the cache, with an optional TTL.
@@ -80,12 +80,12 @@ interface CacheInterface
      *   MUST be thrown if $values is neither an array nor a Traversable,
      *   or if any of the $values are not a legal value.
      */
-    public function setMultiple($values, $ttl = null);
+    public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool;
 
     /**
      * Deletes multiple cache items in a single operation.
      *
-     * @param iterable $keys A list of string-based keys to be deleted.
+     * @param iterable<string> $keys A list of string-based keys to be deleted.
      *
      * @return bool True if the items were successfully removed. False if there was an error.
      *
@@ -93,7 +93,7 @@ interface CacheInterface
      *   MUST be thrown if $keys is neither an array nor a Traversable,
      *   or if any of the $keys are not a legal value.
      */
-    public function deleteMultiple($keys);
+    public function deleteMultiple(iterable $keys): bool;
 
     /**
      * Determines whether an item is present in the cache.
@@ -110,5 +110,5 @@ interface CacheInterface
      * @throws \Psr\SimpleCache\InvalidArgumentException
      *   MUST be thrown if the $key string is not a legal value.
      */
-    public function has($key);
+    public function has(string $key): bool;
 }
diff --git a/vendor/pusher/pusher-php-server/CHANGELOG.md b/vendor/pusher/pusher-php-server/CHANGELOG.md
index a58f643..219be46 100644
--- a/vendor/pusher/pusher-php-server/CHANGELOG.md
+++ b/vendor/pusher/pusher-php-server/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Changelog
 
+## 7.2.4
+
+* [Fixed] Timeout option is propagated to guzzle client
+
+## 7.2.3
+
+* [Fixed] Include socket_id in batch trigger if included.
+
 ## 7.2.2
 
 * [FIXED] composer.phar file removed from package
diff --git a/vendor/pusher/pusher-php-server/src/Pusher.php b/vendor/pusher/pusher-php-server/src/Pusher.php
index 157de3e..72b6b8a 100644
--- a/vendor/pusher/pusher-php-server/src/Pusher.php
+++ b/vendor/pusher/pusher-php-server/src/Pusher.php
@@ -19,7 +19,7 @@ class Pusher implements LoggerAwareInterface, PusherInterface
     /**
      * @var string Version
      */
-    public static $VERSION = '7.2.2';
+    public static $VERSION = '7.2.4';
 
     /**
      * @var null|PusherCrypto
@@ -64,12 +64,6 @@ class Pusher implements LoggerAwareInterface, PusherInterface
     {
         $this->check_compatibility();
 
-        if (!is_null($client)) {
-            $this->client = $client;
-        } else {
-            $this->client = new \GuzzleHttp\Client();
-        }
-
         $useTLS = true;
         if (isset($options['useTLS'])) {
             $useTLS = $options['useTLS'] === true;
@@ -119,6 +113,13 @@ class Pusher implements LoggerAwareInterface, PusherInterface
             );
             $this->crypto = new PusherCrypto($parsedKey);
         }
+
+
+        if (!is_null($client)) {
+            $this->client = $client;
+        } else {
+            $this->client = new \GuzzleHttp\Client(['timeout'  => $this->settings['timeout'],]);
+        }
     }
 
     /**
@@ -475,8 +476,8 @@ class Pusher implements LoggerAwareInterface, PusherInterface
      */
     public function sendToUser(string $user_id, string $event, $data, bool $already_encoded = false): object
     {
-      $this->validate_user_id($user_id);
-      return $this->trigger(["#server-to-user-$user_id"], $event, $data, [], $already_encoded);
+        $this->validate_user_id($user_id);
+        return $this->trigger(["#server-to-user-$user_id"], $event, $data, [], $already_encoded);
     }
 
     /**
@@ -492,8 +493,8 @@ class Pusher implements LoggerAwareInterface, PusherInterface
      */
     public function sendToUserAsync(string $user_id, string $event, $data, bool $already_encoded = false): PromiseInterface
     {
-      $this->validate_user_id($user_id);
-      return $this->triggerAsync(["#server-to-user-$user_id"], $event, $data, [], $already_encoded);
+        $this->validate_user_id($user_id);
+        return $this->triggerAsync(["#server-to-user-$user_id"], $event, $data, [], $already_encoded);
     }
 
 
@@ -724,7 +725,8 @@ class Pusher implements LoggerAwareInterface, PusherInterface
             'query' => $signature,
             'http_errors' => false,
             'headers' => $headers,
-            'base_uri' => $this->channels_url_prefix()
+            'base_uri' => $this->channels_url_prefix(),
+            'timeout' => $this->settings['timeout']
         ]);
 
         $status = $response->getStatusCode();
@@ -776,7 +778,8 @@ class Pusher implements LoggerAwareInterface, PusherInterface
                 'body' => $body,
                 'http_errors' => false,
                 'headers' => $headers,
-                'base_uri' => $this->channels_url_prefix()
+                'base_uri' => $this->channels_url_prefix(),
+                'timeout' => $this->settings['timeout']
             ]);
         } catch (ConnectException $e) {
             throw new ApiErrorException($e->getMessage());
@@ -826,7 +829,8 @@ class Pusher implements LoggerAwareInterface, PusherInterface
             'body' => $body,
             'http_errors' => false,
             'headers' => $headers,
-            'base_uri' => $this->channels_url_prefix()
+            'base_uri' => $this->channels_url_prefix(),
+            'timeout' => $this->settings['timeout'],
         ])->then(function ($response) {
             $status = $response->getStatusCode();
 
@@ -914,7 +918,7 @@ class Pusher implements LoggerAwareInterface, PusherInterface
         return $response;
     }
 
-     /**
+    /**
      * Convenience function for presence channel authorization.
      *
      * Equivalent to authorizeChannel($channel, $socket_id, json_encode(['user_id' => $user_id, 'user_info' => $user_info], JSON_THROW_ON_ERROR))
@@ -1061,7 +1065,7 @@ class Pusher implements LoggerAwareInterface, PusherInterface
      */
     private function make_event(array $channels, string $event, $data, array $params = [], ?string $info = null, bool $already_encoded = false): array
     {
-      $has_encrypted_channel = false;
+        $has_encrypted_channel = false;
         foreach ($channels as $chan) {
             if (PusherCrypto::is_encrypted_channel($chan)) {
                 $has_encrypted_channel = true;
@@ -1102,12 +1106,12 @@ class Pusher implements LoggerAwareInterface, PusherInterface
         $post_params['data'] = $data_encoded;
         $channel_values = array_values($channels);
         if (count($channel_values) == 1) {
-          $post_params['channel'] = $channel_values[0];
+            $post_params['channel'] = $channel_values[0];
         } else {
-          $post_params['channels'] = $channel_values;
+            $post_params['channels'] = $channel_values;
         }
         if (!is_null($info)) {
-          $post_params['info'] = $info;
+            $post_params['info'] = $info;
         }
 
         return array_merge($post_params, $params);
@@ -1166,9 +1170,10 @@ class Pusher implements LoggerAwareInterface, PusherInterface
             $this->validate_channel($event['channel']);
             if (isset($event['socket_id'])) {
                 $this->validate_socket_id($event['socket_id']);
+                $batch[$key] = $this->make_event([$event['channel']], $event['name'], $event['data'], ['socket_id' => $event['socket_id']], $event['info'] ?? null, $already_encoded);
+            } else {
+                $batch[$key] = $this->make_event([$event['channel']], $event['name'], $event['data'], [], $event['info'] ?? null, $already_encoded);
             }
-
-            $batch[$key] = $this->make_event([$event['channel']], $event['name'], $event['data'], [], $event['info'] ?? null, $already_encoded);
         }
 
         try {
diff --git a/vendor/sebastian/code-unit-reverse-lookup/.gitignore b/vendor/sebastian/code-unit-reverse-lookup/.gitignore
deleted file mode 100644
index 9e5f1db..0000000
--- a/vendor/sebastian/code-unit-reverse-lookup/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/.idea
-/composer.lock
-/vendor
-
diff --git a/vendor/sebastian/code-unit-reverse-lookup/.php_cs b/vendor/sebastian/code-unit-reverse-lookup/.php_cs
deleted file mode 100644
index b7393bd..0000000
--- a/vendor/sebastian/code-unit-reverse-lookup/.php_cs
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-$finder = Symfony\CS\Finder\DefaultFinder::create()
-    ->files()
-    ->in('src')
-    ->in('tests')
-    ->name('*.php');
-
-return Symfony\CS\Config\Config::create()
-    ->level(\Symfony\CS\FixerInterface::NONE_LEVEL)
-    ->fixers(
-        array(
-            'align_double_arrow',
-            'align_equals',
-            'braces',
-            'concat_with_spaces',
-            'duplicate_semicolon',
-            'elseif',
-            'empty_return',
-            'encoding',
-            'eof_ending',
-            'extra_empty_lines',
-            'function_call_space',
-            'function_declaration',
-            'indentation',
-            'join_function',
-            'line_after_namespace',
-            'linefeed',
-            'list_commas',
-            'lowercase_constants',
-            'lowercase_keywords',
-            'method_argument_space',
-            'multiple_use',
-            'namespace_no_leading_whitespace',
-            'no_blank_lines_after_class_opening',
-            'no_empty_lines_after_phpdocs',
-            'parenthesis',
-            'php_closing_tag',
-            'phpdoc_indent',
-            'phpdoc_no_access',
-            'phpdoc_no_empty_return',
-            'phpdoc_no_package',
-            'phpdoc_params',
-            'phpdoc_scalar',
-            'phpdoc_separation',
-            'phpdoc_to_comment',
-            'phpdoc_trim',
-            'phpdoc_types',
-            'phpdoc_var_without_name',
-            'remove_lines_between_uses',
-            'return',
-            'self_accessor',
-            'short_array_syntax',
-            'short_tag',
-            'single_line_after_imports',
-            'single_quote',
-            'spaces_before_semicolon',
-            'spaces_cast',
-            'ternary_spaces',
-            'trailing_spaces',
-            'trim_array_spaces',
-            'unused_use',
-            'visibility',
-            'whitespacy_lines'
-        )
-    )
-    ->finder($finder);
-
diff --git a/vendor/sebastian/code-unit-reverse-lookup/.travis.yml b/vendor/sebastian/code-unit-reverse-lookup/.travis.yml
deleted file mode 100644
index 9d9c9d9..0000000
--- a/vendor/sebastian/code-unit-reverse-lookup/.travis.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-language: php
-
-php:
-  - 5.6
-  - 7.0
-  - 7.0snapshot
-  - 7.1
-  - 7.1snapshot
-  - master
-
-sudo: false
-
-before_install:
-  - composer self-update
-  - composer clear-cache
-
-install:
-  - travis_retry composer update --no-interaction --no-ansi --no-progress --no-suggest --optimize-autoloader --prefer-stable
-
-script:
-  - ./vendor/bin/phpunit
-
-notifications:
-  email: false
-
diff --git a/vendor/sebastian/code-unit-reverse-lookup/ChangeLog.md b/vendor/sebastian/code-unit-reverse-lookup/ChangeLog.md
index d136bf5..2be04f5 100644
--- a/vendor/sebastian/code-unit-reverse-lookup/ChangeLog.md
+++ b/vendor/sebastian/code-unit-reverse-lookup/ChangeLog.md
@@ -2,6 +2,10 @@
 
 All notable changes to `sebastianbergmann/code-unit-reverse-lookup` are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
 
+## [1.0.3] - 2024-03-01
+
+* No code changes, only updated `.gitattributes` to not export non-essential files.
+
 ## [1.0.2] - 2020-11-30
 
 ### Changed
@@ -12,4 +16,5 @@ All notable changes to `sebastianbergmann/code-unit-reverse-lookup` are document
 
 * Initial release
 
+[1.0.3]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/1.0.2...1.0.3
 [1.0.2]: https://github.com/sebastianbergmann/code-unit-reverse-lookup/compare/1.0.1...1.0.2
diff --git a/vendor/sebastian/code-unit-reverse-lookup/build.xml b/vendor/sebastian/code-unit-reverse-lookup/build.xml
deleted file mode 100644
index 24cf32e..0000000
--- a/vendor/sebastian/code-unit-reverse-lookup/build.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="code-unit-reverse-lookup" default="setup">
-    <target name="setup" depends="clean,composer"/>
-
-    <target name="clean" description="Cleanup build artifacts">
-        <delete dir="${basedir}/vendor"/>
-        <delete file="${basedir}/composer.lock"/>
-    </target>
-
-    <target name="composer" depends="clean" description="Install dependencies with Composer">
-        <exec executable="composer" taskname="composer">
-            <arg value="update"/>
-            <arg value="--no-interaction"/>
-            <arg value="--no-progress"/>
-            <arg value="--no-ansi"/>
-            <arg value="--no-suggest"/>
-            <arg value="--optimize-autoloader"/>
-            <arg value="--prefer-stable"/>
-        </exec>
-    </target>
-</project>
-
diff --git a/vendor/sebastian/code-unit-reverse-lookup/phpunit.xml b/vendor/sebastian/code-unit-reverse-lookup/phpunit.xml
deleted file mode 100644
index 2c0569e..0000000
--- a/vendor/sebastian/code-unit-reverse-lookup/phpunit.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/5.4/phpunit.xsd"
-         bootstrap="vendor/autoload.php"
-         backupGlobals="false"
-         backupStaticAttributes="false"
-         beStrictAboutCoversAnnotation="true"
-         beStrictAboutOutputDuringTests="true"
-         beStrictAboutTestsThatDoNotTestAnything="true"
-         beStrictAboutTodoAnnotatedTests="true"
-         verbose="true">
-    <testsuite>
-        <directory suffix="Test.php">tests</directory>
-    </testsuite>
-
-    <filter>
-        <whitelist processUncoveredFilesFromWhitelist="true">
-            <directory suffix=".php">src</directory>
-        </whitelist>
-    </filter>
-</phpunit>
diff --git a/vendor/sebastian/code-unit-reverse-lookup/tests/WizardTest.php b/vendor/sebastian/code-unit-reverse-lookup/tests/WizardTest.php
deleted file mode 100644
index 711ad28..0000000
--- a/vendor/sebastian/code-unit-reverse-lookup/tests/WizardTest.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-/*
- * This file is part of code-unit-reverse-lookup.
- *
- * (c) Sebastian Bergmann <sebastian@phpunit.de>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace SebastianBergmann\CodeUnitReverseLookup;
-
-use PHPUnit\Framework\TestCase;
-
-/**
- * @covers SebastianBergmann\CodeUnitReverseLookup\Wizard
- */
-class WizardTest extends TestCase
-{
-    /**
-     * @var Wizard
-     */
-    private $wizard;
-
-    protected function setUp(): void
-    {
-        $this->wizard = new Wizard;
-    }
-
-    public function testMethodCanBeLookedUp()
-    {
-        $this->assertEquals(
-            __METHOD__,
-            $this->wizard->lookup(__FILE__, __LINE__)
-        );
-    }
-
-    public function testReturnsFilenameAndLineNumberAsStringWhenNotInCodeUnit()
-    {
-        $this->assertEquals(
-            'file.php:1',
-            $this->wizard->lookup('file.php', 1)
-        );
-    }
-}
diff --git a/vendor/symfony/deprecation-contracts/.gitignore b/vendor/symfony/deprecation-contracts/.gitignore
deleted file mode 100644
index c49a5d8..0000000
--- a/vendor/symfony/deprecation-contracts/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-vendor/
-composer.lock
-phpunit.xml
diff --git a/vendor/symfony/deprecation-contracts/LICENSE b/vendor/symfony/deprecation-contracts/LICENSE
index 406242f..0ed3a24 100644
--- a/vendor/symfony/deprecation-contracts/LICENSE
+++ b/vendor/symfony/deprecation-contracts/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2020-2022 Fabien Potencier
+Copyright (c) 2020-present Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/symfony/deprecation-contracts/README.md b/vendor/symfony/deprecation-contracts/README.md
index 4957933..9814864 100644
--- a/vendor/symfony/deprecation-contracts/README.md
+++ b/vendor/symfony/deprecation-contracts/README.md
@@ -22,5 +22,5 @@ trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use
 This will generate the following message:
 `Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.`
 
-While not necessarily recommended, the deprecation notices can be completely ignored by declaring an empty
+While not recommended, the deprecation notices can be completely ignored by declaring an empty
 `function trigger_deprecation() {}` in your application.
diff --git a/vendor/symfony/deprecation-contracts/composer.json b/vendor/symfony/deprecation-contracts/composer.json
index cc7cc12..ceb6c07 100644
--- a/vendor/symfony/deprecation-contracts/composer.json
+++ b/vendor/symfony/deprecation-contracts/composer.json
@@ -15,7 +15,7 @@
         }
     ],
     "require": {
-        "php": ">=7.1"
+        "php": ">=8.1"
     },
     "autoload": {
         "files": [
@@ -25,7 +25,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-main": "2.5-dev"
+            "dev-main": "3.5-dev"
         },
         "thanks": {
             "name": "symfony/contracts",
diff --git a/vendor/symfony/deprecation-contracts/function.php b/vendor/symfony/deprecation-contracts/function.php
index d437150..2d56512 100644
--- a/vendor/symfony/deprecation-contracts/function.php
+++ b/vendor/symfony/deprecation-contracts/function.php
@@ -20,7 +20,7 @@ if (!function_exists('trigger_deprecation')) {
      *
      * @author Nicolas Grekas <p@tchwork.com>
      */
-    function trigger_deprecation(string $package, string $version, string $message, ...$args): void
+    function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void
     {
         @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED);
     }
diff --git a/vendor/symfony/polyfill-ctype/LICENSE b/vendor/symfony/polyfill-ctype/LICENSE
index 3f853aa..7536cae 100644
--- a/vendor/symfony/polyfill-ctype/LICENSE
+++ b/vendor/symfony/polyfill-ctype/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2018-2019 Fabien Potencier
+Copyright (c) 2018-present Fabien Potencier
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/vendor/symfony/polyfill-ctype/composer.json b/vendor/symfony/polyfill-ctype/composer.json
index 1b3efff..131ca7a 100644
--- a/vendor/symfony/polyfill-ctype/composer.json
+++ b/vendor/symfony/polyfill-ctype/composer.json
@@ -16,7 +16,7 @@
         }
     ],
     "require": {
-        "php": ">=7.1"
+        "php": ">=7.2"
     },
     "provide": {
         "ext-ctype": "*"
@@ -30,9 +30,6 @@
     },
     "minimum-stability": "dev",
     "extra": {
-        "branch-alias": {
-            "dev-main": "1.27-dev"
-        },
         "thanks": {
             "name": "symfony/polyfill",
             "url": "https://github.com/symfony/polyfill"
diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php
deleted file mode 100644
index bce5c4a..0000000
--- a/vendor/symfony/polyfill-mbstring/Mbstring.php
+++ /dev/null
@@ -1,874 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Polyfill\Mbstring;
-
-/**
- * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
- *
- * Implemented:
- * - mb_chr                  - Returns a specific character from its Unicode code point
- * - mb_convert_encoding     - Convert character encoding
- * - mb_convert_variables    - Convert character code in variable(s)
- * - mb_decode_mimeheader    - Decode string in MIME header field
- * - mb_encode_mimeheader    - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
- * - mb_decode_numericentity - Decode HTML numeric string reference to character
- * - mb_encode_numericentity - Encode character to HTML numeric string reference
- * - mb_convert_case         - Perform case folding on a string
- * - mb_detect_encoding      - Detect character encoding
- * - mb_get_info             - Get internal settings of mbstring
- * - mb_http_input           - Detect HTTP input character encoding
- * - mb_http_output          - Set/Get HTTP output character encoding
- * - mb_internal_encoding    - Set/Get internal character encoding
- * - mb_list_encodings       - Returns an array of all supported encodings
- * - mb_ord                  - Returns the Unicode code point of a character
- * - mb_output_handler       - Callback function converts character encoding in output buffer
- * - mb_scrub                - Replaces ill-formed byte sequences with substitute characters
- * - mb_strlen               - Get string length
- * - mb_strpos               - Find position of first occurrence of string in a string
- * - mb_strrpos              - Find position of last occurrence of a string in a string
- * - mb_str_split            - Convert a string to an array
- * - mb_strtolower           - Make a string lowercase
- * - mb_strtoupper           - Make a string uppercase
- * - mb_substitute_character - Set/Get substitution character
- * - mb_substr               - Get part of string
- * - mb_stripos              - Finds position of first occurrence of a string within another, case insensitive
- * - mb_stristr              - Finds first occurrence of a string within another, case insensitive
- * - mb_strrchr              - Finds the last occurrence of a character in a string within another
- * - mb_strrichr             - Finds the last occurrence of a character in a string within another, case insensitive
- * - mb_strripos             - Finds position of last occurrence of a string within another, case insensitive
- * - mb_strstr               - Finds first occurrence of a string within another
- * - mb_strwidth             - Return width of string
- * - mb_substr_count         - Count the number of substring occurrences
- *
- * Not implemented:
- * - mb_convert_kana         - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
- * - mb_ereg_*               - Regular expression with multibyte support
- * - mb_parse_str            - Parse GET/POST/COOKIE data and set global variable
- * - mb_preferred_mime_name  - Get MIME charset string
- * - mb_regex_encoding       - Returns current encoding for multibyte regex as string
- * - mb_regex_set_options    - Set/Get the default options for mbregex functions
- * - mb_send_mail            - Send encoded mail
- * - mb_split                - Split multibyte string using regular expression
- * - mb_strcut               - Get part of string
- * - mb_strimwidth           - Get truncated string with specified width
- *
- * @author Nicolas Grekas <p@tchwork.com>
- *
- * @internal
- */
-final class Mbstring
-{
-    public const MB_CASE_FOLD = \PHP_INT_MAX;
-
-    private const CASE_FOLD = [
-        ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"],
-        ['μ', 's', 'ι',        'σ', 'β',        'θ',        'φ',        'π',        'κ',        'ρ',        'ε',        "\xE1\xB9\xA1", 'ι'],
-    ];
-
-    private static $encodingList = ['ASCII', 'UTF-8'];
-    private static $language = 'neutral';
-    private static $internalEncoding = 'UTF-8';
-
-    public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
-    {
-        if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) {
-            $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
-        } else {
-            $fromEncoding = self::getEncoding($fromEncoding);
-        }
-
-        $toEncoding = self::getEncoding($toEncoding);
-
-        if ('BASE64' === $fromEncoding) {
-            $s = base64_decode($s);
-            $fromEncoding = $toEncoding;
-        }
-
-        if ('BASE64' === $toEncoding) {
-            return base64_encode($s);
-        }
-
-        if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
-            if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
-                $fromEncoding = 'Windows-1252';
-            }
-            if ('UTF-8' !== $fromEncoding) {
-                $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
-            }
-
-            return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s);
-        }
-
-        if ('HTML-ENTITIES' === $fromEncoding) {
-            $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8');
-            $fromEncoding = 'UTF-8';
-        }
-
-        return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
-    }
-
-    public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
-    {
-        $ok = true;
-        array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
-            if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
-                $ok = false;
-            }
-        });
-
-        return $ok ? $fromEncoding : false;
-    }
-
-    public static function mb_decode_mimeheader($s)
-    {
-        return iconv_mime_decode($s, 2, self::$internalEncoding);
-    }
-
-    public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
-    {
-        trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING);
-    }
-
-    public static function mb_decode_numericentity($s, $convmap, $encoding = null)
-    {
-        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
-            trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
-
-            return null;
-        }
-
-        if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
-            return false;
-        }
-
-        if (null !== $encoding && !\is_scalar($encoding)) {
-            trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
-
-            return '';  // Instead of null (cf. mb_encode_numericentity).
-        }
-
-        $s = (string) $s;
-        if ('' === $s) {
-            return '';
-        }
-
-        $encoding = self::getEncoding($encoding);
-
-        if ('UTF-8' === $encoding) {
-            $encoding = null;
-            if (!preg_match('//u', $s)) {
-                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
-            }
-        } else {
-            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
-        }
-
-        $cnt = floor(\count($convmap) / 4) * 4;
-
-        for ($i = 0; $i < $cnt; $i += 4) {
-            // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
-            $convmap[$i] += $convmap[$i + 2];
-            $convmap[$i + 1] += $convmap[$i + 2];
-        }
-
-        $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
-            $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
-            for ($i = 0; $i < $cnt; $i += 4) {
-                if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
-                    return self::mb_chr($c - $convmap[$i + 2]);
-                }
-            }
-
-            return $m[0];
-        }, $s);
-
-        if (null === $encoding) {
-            return $s;
-        }
-
-        return iconv('UTF-8', $encoding.'//IGNORE', $s);
-    }
-
-    public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
-    {
-        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) {
-            trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING);
-
-            return null;
-        }
-
-        if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) {
-            return false;
-        }
-
-        if (null !== $encoding && !\is_scalar($encoding)) {
-            trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING);
-
-            return null;  // Instead of '' (cf. mb_decode_numericentity).
-        }
-
-        if (null !== $is_hex && !\is_scalar($is_hex)) {
-            trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING);
-
-            return null;
-        }
-
-        $s = (string) $s;
-        if ('' === $s) {
-            return '';
-        }
-
-        $encoding = self::getEncoding($encoding);
-
-        if ('UTF-8' === $encoding) {
-            $encoding = null;
-            if (!preg_match('//u', $s)) {
-                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
-            }
-        } else {
-            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
-        }
-
-        static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
-
-        $cnt = floor(\count($convmap) / 4) * 4;
-        $i = 0;
-        $len = \strlen($s);
-        $result = '';
-
-        while ($i < $len) {
-            $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
-            $uchr = substr($s, $i, $ulen);
-            $i += $ulen;
-            $c = self::mb_ord($uchr);
-
-            for ($j = 0; $j < $cnt; $j += 4) {
-                if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
-                    $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
-                    $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
-                    continue 2;
-                }
-            }
-            $result .= $uchr;
-        }
-
-        if (null === $encoding) {
-            return $result;
-        }
-
-        return iconv('UTF-8', $encoding.'//IGNORE', $result);
-    }
-
-    public static function mb_convert_case($s, $mode, $encoding = null)
-    {
-        $s = (string) $s;
-        if ('' === $s) {
-            return '';
-        }
-
-        $encoding = self::getEncoding($encoding);
-
-        if ('UTF-8' === $encoding) {
-            $encoding = null;
-            if (!preg_match('//u', $s)) {
-                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
-            }
-        } else {
-            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
-        }
-
-        if (\MB_CASE_TITLE == $mode) {
-            static $titleRegexp = null;
-            if (null === $titleRegexp) {
-                $titleRegexp = self::getData('titleCaseRegexp');
-            }
-            $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s);
-        } else {
-            if (\MB_CASE_UPPER == $mode) {
-                static $upper = null;
-                if (null === $upper) {
-                    $upper = self::getData('upperCase');
-                }
-                $map = $upper;
-            } else {
-                if (self::MB_CASE_FOLD === $mode) {
-                    $s = str_replace(self::CASE_FOLD[0], self::CASE_FOLD[1], $s);
-                }
-
-                static $lower = null;
-                if (null === $lower) {
-                    $lower = self::getData('lowerCase');
-                }
-                $map = $lower;
-            }
-
-            static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4];
-
-            $i = 0;
-            $len = \strlen($s);
-
-            while ($i < $len) {
-                $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
-                $uchr = substr($s, $i, $ulen);
-                $i += $ulen;
-
-                if (isset($map[$uchr])) {
-                    $uchr = $map[$uchr];
-                    $nlen = \strlen($uchr);
-
-                    if ($nlen == $ulen) {
-                        $nlen = $i;
-                        do {
-                            $s[--$nlen] = $uchr[--$ulen];
-                        } while ($ulen);
-                    } else {
-                        $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
-                        $len += $nlen - $ulen;
-                        $i += $nlen - $ulen;
-                    }
-                }
-            }
-        }
-
-        if (null === $encoding) {
-            return $s;
-        }
-
-        return iconv('UTF-8', $encoding.'//IGNORE', $s);
-    }
-
-    public static function mb_internal_encoding($encoding = null)
-    {
-        if (null === $encoding) {
-            return self::$internalEncoding;
-        }
-
-        $normalizedEncoding = self::getEncoding($encoding);
-
-        if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) {
-            self::$internalEncoding = $normalizedEncoding;
-
-            return true;
-        }
-
-        if (80000 > \PHP_VERSION_ID) {
-            return false;
-        }
-
-        throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding));
-    }
-
-    public static function mb_language($lang = null)
-    {
-        if (null === $lang) {
-            return self::$language;
-        }
-
-        switch ($normalizedLang = strtolower($lang)) {
-            case 'uni':
-            case 'neutral':
-                self::$language = $normalizedLang;
-
-                return true;
-        }
-
-        if (80000 > \PHP_VERSION_ID) {
-            return false;
-        }
-
-        throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang));
-    }
-
-    public static function mb_list_encodings()
-    {
-        return ['UTF-8'];
-    }
-
-    public static function mb_encoding_aliases($encoding)
-    {
-        switch (strtoupper($encoding)) {
-            case 'UTF8':
-            case 'UTF-8':
-                return ['utf8'];
-        }
-
-        return false;
-    }
-
-    public static function mb_check_encoding($var = null, $encoding = null)
-    {
-        if (null === $encoding) {
-            if (null === $var) {
-                return false;
-            }
-            $encoding = self::$internalEncoding;
-        }
-
-        return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var);
-    }
-
-    public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
-    {
-        if (null === $encodingList) {
-            $encodingList = self::$encodingList;
-        } else {
-            if (!\is_array($encodingList)) {
-                $encodingList = array_map('trim', explode(',', $encodingList));
-            }
-            $encodingList = array_map('strtoupper', $encodingList);
-        }
-
-        foreach ($encodingList as $enc) {
-            switch ($enc) {
-                case 'ASCII':
-                    if (!preg_match('/[\x80-\xFF]/', $str)) {
-                        return $enc;
-                    }
-                    break;
-
-                case 'UTF8':
-                case 'UTF-8':
-                    if (preg_match('//u', $str)) {
-                        return 'UTF-8';
-                    }
-                    break;
-
-                default:
-                    if (0 === strncmp($enc, 'ISO-8859-', 9)) {
-                        return $enc;
-                    }
-            }
-        }
-
-        return false;
-    }
-
-    public static function mb_detect_order($encodingList = null)
-    {
-        if (null === $encodingList) {
-            return self::$encodingList;
-        }
-
-        if (!\is_array($encodingList)) {
-            $encodingList = array_map('trim', explode(',', $encodingList));
-        }
-        $encodingList = array_map('strtoupper', $encodingList);
-
-        foreach ($encodingList as $enc) {
-            switch ($enc) {
-                default:
-                    if (strncmp($enc, 'ISO-8859-', 9)) {
-                        return false;
-                    }
-                    // no break
-                case 'ASCII':
-                case 'UTF8':
-                case 'UTF-8':
-            }
-        }
-
-        self::$encodingList = $encodingList;
-
-        return true;
-    }
-
-    public static function mb_strlen($s, $encoding = null)
-    {
-        $encoding = self::getEncoding($encoding);
-        if ('CP850' === $encoding || 'ASCII' === $encoding) {
-            return \strlen($s);
-        }
-
-        return @iconv_strlen($s, $encoding);
-    }
-
-    public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
-    {
-        $encoding = self::getEncoding($encoding);
-        if ('CP850' === $encoding || 'ASCII' === $encoding) {
-            return strpos($haystack, $needle, $offset);
-        }
-
-        $needle = (string) $needle;
-        if ('' === $needle) {
-            if (80000 > \PHP_VERSION_ID) {
-                trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING);
-
-                return false;
-            }
-
-            return 0;
-        }
-
-        return iconv_strpos($haystack, $needle, $offset, $encoding);
-    }
-
-    public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
-    {
-        $encoding = self::getEncoding($encoding);
-        if ('CP850' === $encoding || 'ASCII' === $encoding) {
-            return strrpos($haystack, $needle, $offset);
-        }
-
-        if ($offset != (int) $offset) {
-            $offset = 0;
-        } elseif ($offset = (int) $offset) {
-            if ($offset < 0) {
-                if (0 > $offset += self::mb_strlen($needle)) {
-                    $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
-                }
-                $offset = 0;
-            } else {
-                $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
-            }
-        }
-
-        $pos = '' !== $needle || 80000 > \PHP_VERSION_ID
-            ? iconv_strrpos($haystack, $needle, $encoding)
-            : self::mb_strlen($haystack, $encoding);
-
-        return false !== $pos ? $offset + $pos : false;
-    }
-
-    public static function mb_str_split($string, $split_length = 1, $encoding = null)
-    {
-        if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) {
-            trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING);
-
-            return null;
-        }
-
-        if (1 > $split_length = (int) $split_length) {
-            if (80000 > \PHP_VERSION_ID) {
-                trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING);
-
-                return false;
-            }
-
-            throw new \ValueError('Argument #2 ($length) must be greater than 0');
-        }
-
-        if (null === $encoding) {
-            $encoding = mb_internal_encoding();
-        }
-
-        if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
-            $rx = '/(';
-            while (65535 < $split_length) {
-                $rx .= '.{65535}';
-                $split_length -= 65535;
-            }
-            $rx .= '.{'.$split_length.'})/us';
-
-            return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY);
-        }
-
-        $result = [];
-        $length = mb_strlen($string, $encoding);
-
-        for ($i = 0; $i < $length; $i += $split_length) {
-            $result[] = mb_substr($string, $i, $split_length, $encoding);
-        }
-
-        return $result;
-    }
-
-    public static function mb_strtolower($s, $encoding = null)
-    {
-        return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding);
-    }
-
-    public static function mb_strtoupper($s, $encoding = null)
-    {
-        return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding);
-    }
-
-    public static function mb_substitute_character($c = null)
-    {
-        if (null === $c) {
-            return 'none';
-        }
-        if (0 === strcasecmp($c, 'none')) {
-            return true;
-        }
-        if (80000 > \PHP_VERSION_ID) {
-            return false;
-        }
-        if (\is_int($c) || 'long' === $c || 'entity' === $c) {
-            return false;
-        }
-
-        throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint');
-    }
-
-    public static function mb_substr($s, $start, $length = null, $encoding = null)
-    {
-        $encoding = self::getEncoding($encoding);
-        if ('CP850' === $encoding || 'ASCII' === $encoding) {
-            return (string) substr($s, $start, null === $length ? 2147483647 : $length);
-        }
-
-        if ($start < 0) {
-            $start = iconv_strlen($s, $encoding) + $start;
-            if ($start < 0) {
-                $start = 0;
-            }
-        }
-
-        if (null === $length) {
-            $length = 2147483647;
-        } elseif ($length < 0) {
-            $length = iconv_strlen($s, $encoding) + $length - $start;
-            if ($length < 0) {
-                return '';
-            }
-        }
-
-        return (string) iconv_substr($s, $start, $length, $encoding);
-    }
-
-    public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
-    {
-        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
-        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
-
-        return self::mb_strpos($haystack, $needle, $offset, $encoding);
-    }
-
-    public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
-    {
-        $pos = self::mb_stripos($haystack, $needle, 0, $encoding);
-
-        return self::getSubpart($pos, $part, $haystack, $encoding);
-    }
-
-    public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
-    {
-        $encoding = self::getEncoding($encoding);
-        if ('CP850' === $encoding || 'ASCII' === $encoding) {
-            $pos = strrpos($haystack, $needle);
-        } else {
-            $needle = self::mb_substr($needle, 0, 1, $encoding);
-            $pos = iconv_strrpos($haystack, $needle, $encoding);
-        }
-
-        return self::getSubpart($pos, $part, $haystack, $encoding);
-    }
-
-    public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
-    {
-        $needle = self::mb_substr($needle, 0, 1, $encoding);
-        $pos = self::mb_strripos($haystack, $needle, $encoding);
-
-        return self::getSubpart($pos, $part, $haystack, $encoding);
-    }
-
-    public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
-    {
-        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
-        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
-
-        return self::mb_strrpos($haystack, $needle, $offset, $encoding);
-    }
-
-    public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
-    {
-        $pos = strpos($haystack, $needle);
-        if (false === $pos) {
-            return false;
-        }
-        if ($part) {
-            return substr($haystack, 0, $pos);
-        }
-
-        return substr($haystack, $pos);
-    }
-
-    public static function mb_get_info($type = 'all')
-    {
-        $info = [
-            'internal_encoding' => self::$internalEncoding,
-            'http_output' => 'pass',
-            'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
-            'func_overload' => 0,
-            'func_overload_list' => 'no overload',
-            'mail_charset' => 'UTF-8',
-            'mail_header_encoding' => 'BASE64',
-            'mail_body_encoding' => 'BASE64',
-            'illegal_chars' => 0,
-            'encoding_translation' => 'Off',
-            'language' => self::$language,
-            'detect_order' => self::$encodingList,
-            'substitute_character' => 'none',
-            'strict_detection' => 'Off',
-        ];
-
-        if ('all' === $type) {
-            return $info;
-        }
-        if (isset($info[$type])) {
-            return $info[$type];
-        }
-
-        return false;
-    }
-
-    public static function mb_http_input($type = '')
-    {
-        return false;
-    }
-
-    public static function mb_http_output($encoding = null)
-    {
-        return null !== $encoding ? 'pass' === $encoding : 'pass';
-    }
-
-    public static function mb_strwidth($s, $encoding = null)
-    {
-        $encoding = self::getEncoding($encoding);
-
-        if ('UTF-8' !== $encoding) {
-            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
-        }
-
-        $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
-
-        return ($wide << 1) + iconv_strlen($s, 'UTF-8');
-    }
-
-    public static function mb_substr_count($haystack, $needle, $encoding = null)
-    {
-        return substr_count($haystack, $needle);
-    }
-
-    public static function mb_output_handler($contents, $status)
-    {
-        return $contents;
-    }
-
-    public static function mb_chr($code, $encoding = null)
-    {
-        if (0x80 > $code %= 0x200000) {
-            $s = \chr($code);
-        } elseif (0x800 > $code) {
-            $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
-        } elseif (0x10000 > $code) {
-            $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
-        } else {
-            $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
-        }
-
-        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
-            $s = mb_convert_encoding($s, $encoding, 'UTF-8');
-        }
-
-        return $s;
-    }
-
-    public static function mb_ord($s, $encoding = null)
-    {
-        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
-            $s = mb_convert_encoding($s, 'UTF-8', $encoding);
-        }
-
-        if (1 === \strlen($s)) {
-            return \ord($s);
-        }
-
-        $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
-        if (0xF0 <= $code) {
-            return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
-        }
-        if (0xE0 <= $code) {
-            return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
-        }
-        if (0xC0 <= $code) {
-            return (($code - 0xC0) << 6) + $s[2] - 0x80;
-        }
-
-        return $code;
-    }
-
-    private static function getSubpart($pos, $part, $haystack, $encoding)
-    {
-        if (false === $pos) {
-            return false;
-        }
-        if ($part) {
-            return self::mb_substr($haystack, 0, $pos, $encoding);
-        }
-
-        return self::mb_substr($haystack, $pos, null, $encoding);
-    }
-
-    private static function html_encoding_callback(array $m)
-    {
-        $i = 1;
-        $entities = '';
-        $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8'));
-
-        while (isset($m[$i])) {
-            if (0x80 > $m[$i]) {
-                $entities .= \chr($m[$i++]);
-                continue;
-            }
-            if (0xF0 <= $m[$i]) {
-                $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
-            } elseif (0xE0 <= $m[$i]) {
-                $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
-            } else {
-                $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
-            }
-
-            $entities .= '&#'.$c.';';
-        }
-
-        return $entities;
-    }
-
-    private static function title_case(array $s)
-    {
-        return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8');
-    }
-
-    private static function getData($file)
-    {
-        if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
-            return require $file;
-        }
-
-        return false;
-    }
-
-    private static function getEncoding($encoding)
-    {
-        if (null === $encoding) {
-            return self::$internalEncoding;
-        }
-
-        if ('UTF-8' === $encoding) {
-            return 'UTF-8';
-        }
-
-        $encoding = strtoupper($encoding);
-
-        if ('8BIT' === $encoding || 'BINARY' === $encoding) {
-            return 'CP850';
-        }
-
-        if ('UTF8' === $encoding) {
-            return 'UTF-8';
-        }
-
-        return $encoding;
-    }
-}
diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md
deleted file mode 100644
index 478b40d..0000000
--- a/vendor/symfony/polyfill-mbstring/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-Symfony Polyfill / Mbstring
-===========================
-
-This component provides a partial, native PHP implementation for the
-[Mbstring](https://php.net/mbstring) extension.
-
-More information can be found in the
-[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
-
-License
-=======
-
-This library is released under the [MIT license](LICENSE).
diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
deleted file mode 100644
index fac60b0..0000000
--- a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
+++ /dev/null
@@ -1,1397 +0,0 @@
-<?php
-
-return array (
-  'A' => 'a',
-  'B' => 'b',
-  'C' => 'c',
-  'D' => 'd',
-  'E' => 'e',
-  'F' => 'f',
-  'G' => 'g',
-  'H' => 'h',
-  'I' => 'i',
-  'J' => 'j',
-  'K' => 'k',
-  'L' => 'l',
-  'M' => 'm',
-  'N' => 'n',
-  'O' => 'o',
-  'P' => 'p',
-  'Q' => 'q',
-  'R' => 'r',
-  'S' => 's',
-  'T' => 't',
-  'U' => 'u',
-  'V' => 'v',
-  'W' => 'w',
-  'X' => 'x',
-  'Y' => 'y',
-  'Z' => 'z',
-  'À' => 'à',
-  'Á' => 'á',
-  'Â' => 'â',
-  'Ã' => 'ã',
-  'Ä' => 'ä',
-  'Å' => 'å',
-  'Æ' => 'æ',
-  'Ç' => 'ç',
-  'È' => 'è',
-  'É' => 'é',
-  'Ê' => 'ê',
-  'Ë' => 'ë',
-  'Ì' => 'ì',
-  'Í' => 'í',
-  'Î' => 'î',
-  'Ï' => 'ï',
-  'Ð' => 'ð',
-  'Ñ' => 'ñ',
-  'Ò' => 'ò',
-  'Ó' => 'ó',
-  'Ô' => 'ô',
-  'Õ' => 'õ',
-  'Ö' => 'ö',
-  'Ø' => 'ø',
-  'Ù' => 'ù',
-  'Ú' => 'ú',
-  'Û' => 'û',
-  'Ü' => 'ü',
-  'Ý' => 'ý',
-  'Þ' => 'þ',
-  'Ā' => 'ā',
-  'Ă' => 'ă',
-  'Ą' => 'ą',
-  'Ć' => 'ć',
-  'Ĉ' => 'ĉ',
-  'Ċ' => 'ċ',
-  'Č' => 'č',
-  'Ď' => 'ď',
-  'Đ' => 'đ',
-  'Ē' => 'ē',
-  'Ĕ' => 'ĕ',
-  'Ė' => 'ė',
-  'Ę' => 'ę',
-  'Ě' => 'ě',
-  'Ĝ' => 'ĝ',
-  'Ğ' => 'ğ',
-  'Ġ' => 'ġ',
-  'Ģ' => 'ģ',
-  'Ĥ' => 'ĥ',
-  'Ħ' => 'ħ',
-  'Ĩ' => 'ĩ',
-  'Ī' => 'ī',
-  'Ĭ' => 'ĭ',
-  'Į' => 'į',
-  'İ' => 'i̇',
-  'IJ' => 'ij',
-  'Ĵ' => 'ĵ',
-  'Ķ' => 'ķ',
-  'Ĺ' => 'ĺ',
-  'Ļ' => 'ļ',
-  'Ľ' => 'ľ',
-  'Ŀ' => 'ŀ',
-  'Ł' => 'ł',
-  'Ń' => 'ń',
-  'Ņ' => 'ņ',
-  'Ň' => 'ň',
-  'Ŋ' => 'ŋ',
-  'Ō' => 'ō',
-  'Ŏ' => 'ŏ',
-  'Ő' => 'ő',
-  'Œ' => 'œ',
-  'Ŕ' => 'ŕ',
-  'Ŗ' => 'ŗ',
-  'Ř' => 'ř',
-  'Ś' => 'ś',
-  'Ŝ' => 'ŝ',
-  'Ş' => 'ş',
-  'Š' => 'š',
-  'Ţ' => 'ţ',
-  'Ť' => 'ť',
-  'Ŧ' => 'ŧ',
-  'Ũ' => 'ũ',
-  'Ū' => 'ū',
-  'Ŭ' => 'ŭ',
-  'Ů' => 'ů',
-  'Ű' => 'ű',
-  'Ų' => 'ų',
-  'Ŵ' => 'ŵ',
-  'Ŷ' => 'ŷ',
-  'Ÿ' => 'ÿ',
-  'Ź' => 'ź',
-  'Ż' => 'ż',
-  'Ž' => 'ž',
-  'Ɓ' => 'ɓ',
-  'Ƃ' => 'ƃ',
-  'Ƅ' => 'ƅ',
-  'Ɔ' => 'ɔ',
-  'Ƈ' => 'ƈ',
-  'Ɖ' => 'ɖ',
-  'Ɗ' => 'ɗ',
-  'Ƌ' => 'ƌ',
-  'Ǝ' => 'ǝ',
-  'Ə' => 'ə',
-  'Ɛ' => 'ɛ',
-  'Ƒ' => 'ƒ',
-  'Ɠ' => 'ɠ',
-  'Ɣ' => 'ɣ',
-  'Ɩ' => 'ɩ',
-  'Ɨ' => 'ɨ',
-  'Ƙ' => 'ƙ',
-  'Ɯ' => 'ɯ',
-  'Ɲ' => 'ɲ',
-  'Ɵ' => 'ɵ',
-  'Ơ' => 'ơ',
-  'Ƣ' => 'ƣ',
-  'Ƥ' => 'ƥ',
-  'Ʀ' => 'ʀ',
-  'Ƨ' => 'ƨ',
-  'Ʃ' => 'ʃ',
-  'Ƭ' => 'ƭ',
-  'Ʈ' => 'ʈ',
-  'Ư' => 'ư',
-  'Ʊ' => 'ʊ',
-  'Ʋ' => 'ʋ',
-  'Ƴ' => 'ƴ',
-  'Ƶ' => 'ƶ',
-  'Ʒ' => 'ʒ',
-  'Ƹ' => 'ƹ',
-  'Ƽ' => 'ƽ',
-  'DŽ' => 'dž',
-  'Dž' => 'dž',
-  'LJ' => 'lj',
-  'Lj' => 'lj',
-  'NJ' => 'nj',
-  'Nj' => 'nj',
-  'Ǎ' => 'ǎ',
-  'Ǐ' => 'ǐ',
-  'Ǒ' => 'ǒ',
-  'Ǔ' => 'ǔ',
-  'Ǖ' => 'ǖ',
-  'Ǘ' => 'ǘ',
-  'Ǚ' => 'ǚ',
-  'Ǜ' => 'ǜ',
-  'Ǟ' => 'ǟ',
-  'Ǡ' => 'ǡ',
-  'Ǣ' => 'ǣ',
-  'Ǥ' => 'ǥ',
-  'Ǧ' => 'ǧ',
-  'Ǩ' => 'ǩ',
-  'Ǫ' => 'ǫ',
-  'Ǭ' => 'ǭ',
-  'Ǯ' => 'ǯ',
-  'DZ' => 'dz',
-  'Dz' => 'dz',
-  'Ǵ' => 'ǵ',
-  'Ƕ' => 'ƕ',
-  'Ƿ' => 'ƿ',
-  'Ǹ' => 'ǹ',
-  'Ǻ' => 'ǻ',
-  'Ǽ' => 'ǽ',
-  'Ǿ' => 'ǿ',
-  'Ȁ' => 'ȁ',
-  'Ȃ' => 'ȃ',
-  'Ȅ' => 'ȅ',
-  'Ȇ' => 'ȇ',
-  'Ȉ' => 'ȉ',
-  'Ȋ' => 'ȋ',
-  'Ȍ' => 'ȍ',
-  'Ȏ' => 'ȏ',
-  'Ȑ' => 'ȑ',
-  'Ȓ' => 'ȓ',
-  'Ȕ' => 'ȕ',
-  'Ȗ' => 'ȗ',
-  'Ș' => 'ș',
-  'Ț' => 'ț',
-  'Ȝ' => 'ȝ',
-  'Ȟ' => 'ȟ',
-  'Ƞ' => 'ƞ',
-  'Ȣ' => 'ȣ',
-  'Ȥ' => 'ȥ',
-  'Ȧ' => 'ȧ',
-  'Ȩ' => 'ȩ',
-  'Ȫ' => 'ȫ',
-  'Ȭ' => 'ȭ',
-  'Ȯ' => 'ȯ',
-  'Ȱ' => 'ȱ',
-  'Ȳ' => 'ȳ',
-  'Ⱥ' => 'ⱥ',
-  'Ȼ' => 'ȼ',
-  'Ƚ' => 'ƚ',
-  'Ⱦ' => 'ⱦ',
-  'Ɂ' => 'ɂ',
-  'Ƀ' => 'ƀ',
-  'Ʉ' => 'ʉ',
-  'Ʌ' => 'ʌ',
-  'Ɇ' => 'ɇ',
-  'Ɉ' => 'ɉ',
-  'Ɋ' => 'ɋ',
-  'Ɍ' => 'ɍ',
-  'Ɏ' => 'ɏ',
-  'Ͱ' => 'ͱ',
-  'Ͳ' => 'ͳ',
-  'Ͷ' => 'ͷ',
-  'Ϳ' => 'ϳ',
-  'Ά' => 'ά',
-  'Έ' => 'έ',
-  'Ή' => 'ή',
-  'Ί' => 'ί',
-  'Ό' => 'ό',
-  'Ύ' => 'ύ',
-  'Ώ' => 'ώ',
-  'Α' => 'α',
-  'Β' => 'β',
-  'Γ' => 'γ',
-  'Δ' => 'δ',
-  'Ε' => 'ε',
-  'Ζ' => 'ζ',
-  'Η' => 'η',
-  'Θ' => 'θ',
-  'Ι' => 'ι',
-  'Κ' => 'κ',
-  'Λ' => 'λ',
-  'Μ' => 'μ',
-  'Ν' => 'ν',
-  'Ξ' => 'ξ',
-  'Ο' => 'ο',
-  'Π' => 'π',
-  'Ρ' => 'ρ',
-  'Σ' => 'σ',
-  'Τ' => 'τ',
-  'Υ' => 'υ',
-  'Φ' => 'φ',
-  'Χ' => 'χ',
-  'Ψ' => 'ψ',
-  'Ω' => 'ω',
-  'Ϊ' => 'ϊ',
-  'Ϋ' => 'ϋ',
-  'Ϗ' => 'ϗ',
-  'Ϙ' => 'ϙ',
-  'Ϛ' => 'ϛ',
-  'Ϝ' => 'ϝ',
-  'Ϟ' => 'ϟ',
-  'Ϡ' => 'ϡ',
-  'Ϣ' => 'ϣ',
-  'Ϥ' => 'ϥ',
-  'Ϧ' => 'ϧ',
-  'Ϩ' => 'ϩ',
-  'Ϫ' => 'ϫ',
-  'Ϭ' => 'ϭ',
-  'Ϯ' => 'ϯ',
-  'ϴ' => 'θ',
-  'Ϸ' => 'ϸ',
-  'Ϲ' => 'ϲ',
-  'Ϻ' => 'ϻ',
-  'Ͻ' => 'ͻ',
-  'Ͼ' => 'ͼ',
-  'Ͽ' => 'ͽ',
-  'Ѐ' => 'ѐ',
-  'Ё' => 'ё',
-  'Ђ' => 'ђ',
-  'Ѓ' => 'ѓ',
-  'Є' => 'є',
-  'Ѕ' => 'ѕ',
-  'І' => 'і',
-  'Ї' => 'ї',
-  'Ј' => 'ј',
-  'Љ' => 'љ',
-  'Њ' => 'њ',
-  'Ћ' => 'ћ',
-  'Ќ' => 'ќ',
-  'Ѝ' => 'ѝ',
-  'Ў' => 'ў',
-  'Џ' => 'џ',
-  'А' => 'а',
-  'Б' => 'б',
-  'В' => 'в',
-  'Г' => 'г',
-  'Д' => 'д',
-  'Е' => 'е',
-  'Ж' => 'ж',
-  'З' => 'з',
-  'И' => 'и',
-  'Й' => 'й',
-  'К' => 'к',
-  'Л' => 'л',
-  'М' => 'м',
-  'Н' => 'н',
-  'О' => 'о',
-  'П' => 'п',
-  'Р' => 'р',
-  'С' => 'с',
-  'Т' => 'т',
-  'У' => 'у',
-  'Ф' => 'ф',
-  'Х' => 'х',
-  'Ц' => 'ц',
-  'Ч' => 'ч',
-  'Ш' => 'ш',
-  'Щ' => 'щ',
-  'Ъ' => 'ъ',
-  'Ы' => 'ы',
-  'Ь' => 'ь',
-  'Э' => 'э',
-  'Ю' => 'ю',
-  'Я' => 'я',
-  'Ѡ' => 'ѡ',
-  'Ѣ' => 'ѣ',
-  'Ѥ' => 'ѥ',
-  'Ѧ' => 'ѧ',
-  'Ѩ' => 'ѩ',
-  'Ѫ' => 'ѫ',
-  'Ѭ' => 'ѭ',
-  'Ѯ' => 'ѯ',
-  'Ѱ' => 'ѱ',
-  'Ѳ' => 'ѳ',
-  'Ѵ' => 'ѵ',
-  'Ѷ' => 'ѷ',
-  'Ѹ' => 'ѹ',
-  'Ѻ' => 'ѻ',
-  'Ѽ' => 'ѽ',
-  'Ѿ' => 'ѿ',
-  'Ҁ' => 'ҁ',
-  'Ҋ' => 'ҋ',
-  'Ҍ' => 'ҍ',
-  'Ҏ' => 'ҏ',
-  'Ґ' => 'ґ',
-  'Ғ' => 'ғ',
-  'Ҕ' => 'ҕ',
-  'Җ' => 'җ',
-  'Ҙ' => 'ҙ',
-  'Қ' => 'қ',
-  'Ҝ' => 'ҝ',
-  'Ҟ' => 'ҟ',
-  'Ҡ' => 'ҡ',
-  'Ң' => 'ң',
-  'Ҥ' => 'ҥ',
-  'Ҧ' => 'ҧ',
-  'Ҩ' => 'ҩ',
-  'Ҫ' => 'ҫ',
-  'Ҭ' => 'ҭ',
-  'Ү' => 'ү',
-  'Ұ' => 'ұ',
-  'Ҳ' => 'ҳ',
-  'Ҵ' => 'ҵ',
-  'Ҷ' => 'ҷ',
-  'Ҹ' => 'ҹ',
-  'Һ' => 'һ',
-  'Ҽ' => 'ҽ',
-  'Ҿ' => 'ҿ',
-  'Ӏ' => 'ӏ',
-  'Ӂ' => 'ӂ',
-  'Ӄ' => 'ӄ',
-  'Ӆ' => 'ӆ',
-  'Ӈ' => 'ӈ',
-  'Ӊ' => 'ӊ',
-  'Ӌ' => 'ӌ',
-  'Ӎ' => 'ӎ',
-  'Ӑ' => 'ӑ',
-  'Ӓ' => 'ӓ',
-  'Ӕ' => 'ӕ',
-  'Ӗ' => 'ӗ',
-  'Ә' => 'ә',
-  'Ӛ' => 'ӛ',
-  'Ӝ' => 'ӝ',
-  'Ӟ' => 'ӟ',
-  'Ӡ' => 'ӡ',
-  'Ӣ' => 'ӣ',
-  'Ӥ' => 'ӥ',
-  'Ӧ' => 'ӧ',
-  'Ө' => 'ө',
-  'Ӫ' => 'ӫ',
-  'Ӭ' => 'ӭ',
-  'Ӯ' => 'ӯ',
-  'Ӱ' => 'ӱ',
-  'Ӳ' => 'ӳ',
-  'Ӵ' => 'ӵ',
-  'Ӷ' => 'ӷ',
-  'Ӹ' => 'ӹ',
-  'Ӻ' => 'ӻ',
-  'Ӽ' => 'ӽ',
-  'Ӿ' => 'ӿ',
-  'Ԁ' => 'ԁ',
-  'Ԃ' => 'ԃ',
-  'Ԅ' => 'ԅ',
-  'Ԇ' => 'ԇ',
-  'Ԉ' => 'ԉ',
-  'Ԋ' => 'ԋ',
-  'Ԍ' => 'ԍ',
-  'Ԏ' => 'ԏ',
-  'Ԑ' => 'ԑ',
-  'Ԓ' => 'ԓ',
-  'Ԕ' => 'ԕ',
-  'Ԗ' => 'ԗ',
-  'Ԙ' => 'ԙ',
-  'Ԛ' => 'ԛ',
-  'Ԝ' => 'ԝ',
-  'Ԟ' => 'ԟ',
-  'Ԡ' => 'ԡ',
-  'Ԣ' => 'ԣ',
-  'Ԥ' => 'ԥ',
-  'Ԧ' => 'ԧ',
-  'Ԩ' => 'ԩ',
-  'Ԫ' => 'ԫ',
-  'Ԭ' => 'ԭ',
-  'Ԯ' => 'ԯ',
-  'Ա' => 'ա',
-  'Բ' => 'բ',
-  'Գ' => 'գ',
-  'Դ' => 'դ',
-  'Ե' => 'ե',
-  'Զ' => 'զ',
-  'Է' => 'է',
-  'Ը' => 'ը',
-  'Թ' => 'թ',
-  'Ժ' => 'ժ',
-  'Ի' => 'ի',
-  'Լ' => 'լ',
-  'Խ' => 'խ',
-  'Ծ' => 'ծ',
-  'Կ' => 'կ',
-  'Հ' => 'հ',
-  'Ձ' => 'ձ',
-  'Ղ' => 'ղ',
-  'Ճ' => 'ճ',
-  'Մ' => 'մ',
-  'Յ' => 'յ',
-  'Ն' => 'ն',
-  'Շ' => 'շ',
-  'Ո' => 'ո',
-  'Չ' => 'չ',
-  'Պ' => 'պ',
-  'Ջ' => 'ջ',
-  'Ռ' => 'ռ',
-  'Ս' => 'ս',
-  'Վ' => 'վ',
-  'Տ' => 'տ',
-  'Ր' => 'ր',
-  'Ց' => 'ց',
-  'Ւ' => 'ւ',
-  'Փ' => 'փ',
-  'Ք' => 'ք',
-  'Օ' => 'օ',
-  'Ֆ' => 'ֆ',
-  'Ⴀ' => 'ⴀ',
-  'Ⴁ' => 'ⴁ',
-  'Ⴂ' => 'ⴂ',
-  'Ⴃ' => 'ⴃ',
-  'Ⴄ' => 'ⴄ',
-  'Ⴅ' => 'ⴅ',
-  'Ⴆ' => 'ⴆ',
-  'Ⴇ' => 'ⴇ',
-  'Ⴈ' => 'ⴈ',
-  'Ⴉ' => 'ⴉ',
-  'Ⴊ' => 'ⴊ',
-  'Ⴋ' => 'ⴋ',
-  'Ⴌ' => 'ⴌ',
-  'Ⴍ' => 'ⴍ',
-  'Ⴎ' => 'ⴎ',
-  'Ⴏ' => 'ⴏ',
-  'Ⴐ' => 'ⴐ',
-  'Ⴑ' => 'ⴑ',
-  'Ⴒ' => 'ⴒ',
-  'Ⴓ' => 'ⴓ',
-  'Ⴔ' => 'ⴔ',
-  'Ⴕ' => 'ⴕ',
-  'Ⴖ' => 'ⴖ',
-  'Ⴗ' => 'ⴗ',
-  'Ⴘ' => 'ⴘ',
-  'Ⴙ' => 'ⴙ',
-  'Ⴚ' => 'ⴚ',
-  'Ⴛ' => 'ⴛ',
-  'Ⴜ' => 'ⴜ',
-  'Ⴝ' => 'ⴝ',
-  'Ⴞ' => 'ⴞ',
-  'Ⴟ' => 'ⴟ',
-  'Ⴠ' => 'ⴠ',
-  'Ⴡ' => 'ⴡ',
-  'Ⴢ' => 'ⴢ',
-  'Ⴣ' => 'ⴣ',
-  'Ⴤ' => 'ⴤ',
-  'Ⴥ' => 'ⴥ',
-  'Ⴧ' => 'ⴧ',
-  'Ⴭ' => 'ⴭ',
-  'Ꭰ' => 'ꭰ',
-  'Ꭱ' => 'ꭱ',
-  'Ꭲ' => 'ꭲ',
-  'Ꭳ' => 'ꭳ',
-  'Ꭴ' => 'ꭴ',
-  'Ꭵ' => 'ꭵ',
-  'Ꭶ' => 'ꭶ',
-  'Ꭷ' => 'ꭷ',
-  'Ꭸ' => 'ꭸ',
-  'Ꭹ' => 'ꭹ',
-  'Ꭺ' => 'ꭺ',
-  'Ꭻ' => 'ꭻ',
-  'Ꭼ' => 'ꭼ',
-  'Ꭽ' => 'ꭽ',
-  'Ꭾ' => 'ꭾ',
-  'Ꭿ' => 'ꭿ',
-  'Ꮀ' => 'ꮀ',
-  'Ꮁ' => 'ꮁ',
-  'Ꮂ' => 'ꮂ',
-  'Ꮃ' => 'ꮃ',
-  'Ꮄ' => 'ꮄ',
-  'Ꮅ' => 'ꮅ',
-  'Ꮆ' => 'ꮆ',
-  'Ꮇ' => 'ꮇ',
-  'Ꮈ' => 'ꮈ',
-  'Ꮉ' => 'ꮉ',
-  'Ꮊ' => 'ꮊ',
-  'Ꮋ' => 'ꮋ',
-  'Ꮌ' => 'ꮌ',
-  'Ꮍ' => 'ꮍ',
-  'Ꮎ' => 'ꮎ',
-  'Ꮏ' => 'ꮏ',
-  'Ꮐ' => 'ꮐ',
-  'Ꮑ' => 'ꮑ',
-  'Ꮒ' => 'ꮒ',
-  'Ꮓ' => 'ꮓ',
-  'Ꮔ' => 'ꮔ',
-  'Ꮕ' => 'ꮕ',
-  'Ꮖ' => 'ꮖ',
-  'Ꮗ' => 'ꮗ',
-  'Ꮘ' => 'ꮘ',
-  'Ꮙ' => 'ꮙ',
-  'Ꮚ' => 'ꮚ',
-  'Ꮛ' => 'ꮛ',
-  'Ꮜ' => 'ꮜ',
-  'Ꮝ' => 'ꮝ',
-  'Ꮞ' => 'ꮞ',
-  'Ꮟ' => 'ꮟ',
-  'Ꮠ' => 'ꮠ',
-  'Ꮡ' => 'ꮡ',
-  'Ꮢ' => 'ꮢ',
-  'Ꮣ' => 'ꮣ',
-  'Ꮤ' => 'ꮤ',
-  'Ꮥ' => 'ꮥ',
-  'Ꮦ' => 'ꮦ',
-  'Ꮧ' => 'ꮧ',
-  'Ꮨ' => 'ꮨ',
-  'Ꮩ' => 'ꮩ',
-  'Ꮪ' => 'ꮪ',
-  'Ꮫ' => 'ꮫ',
-  'Ꮬ' => 'ꮬ',
-  'Ꮭ' => 'ꮭ',
-  'Ꮮ' => 'ꮮ',
-  'Ꮯ' => 'ꮯ',
-  'Ꮰ' => 'ꮰ',
-  'Ꮱ' => 'ꮱ',
-  'Ꮲ' => 'ꮲ',
-  'Ꮳ' => 'ꮳ',
-  'Ꮴ' => 'ꮴ',
-  'Ꮵ' => 'ꮵ',
-  'Ꮶ' => 'ꮶ',
-  'Ꮷ' => 'ꮷ',
-  'Ꮸ' => 'ꮸ',
-  'Ꮹ' => 'ꮹ',
-  'Ꮺ' => 'ꮺ',
-  'Ꮻ' => 'ꮻ',
-  'Ꮼ' => 'ꮼ',
-  'Ꮽ' => 'ꮽ',
-  'Ꮾ' => 'ꮾ',
-  'Ꮿ' => 'ꮿ',
-  'Ᏸ' => 'ᏸ',
-  'Ᏹ' => 'ᏹ',
-  'Ᏺ' => 'ᏺ',
-  'Ᏻ' => 'ᏻ',
-  'Ᏼ' => 'ᏼ',
-  'Ᏽ' => 'ᏽ',
-  'Ა' => 'ა',
-  'Ბ' => 'ბ',
-  'Გ' => 'გ',
-  'Დ' => 'დ',
-  'Ე' => 'ე',
-  'Ვ' => 'ვ',
-  'Ზ' => 'ზ',
-  'Თ' => 'თ',
-  'Ი' => 'ი',
-  'Კ' => 'კ',
-  'Ლ' => 'ლ',
-  'Მ' => 'მ',
-  'Ნ' => 'ნ',
-  'Ო' => 'ო',
-  'Პ' => 'პ',
-  'Ჟ' => 'ჟ',
-  'Რ' => 'რ',
-  'Ს' => 'ს',
-  'Ტ' => 'ტ',
-  'Უ' => 'უ',
-  'Ფ' => 'ფ',
-  'Ქ' => 'ქ',
-  'Ღ' => 'ღ',
-  'Ყ' => 'ყ',
-  'Შ' => 'შ',
-  'Ჩ' => 'ჩ',
-  'Ც' => 'ც',
-  'Ძ' => 'ძ',
-  'Წ' => 'წ',
-  'Ჭ' => 'ჭ',
-  'Ხ' => 'ხ',
-  'Ჯ' => 'ჯ',
-  'Ჰ' => 'ჰ',
-  'Ჱ' => 'ჱ',
-  'Ჲ' => 'ჲ',
-  'Ჳ' => 'ჳ',
-  'Ჴ' => 'ჴ',
-  'Ჵ' => 'ჵ',
-  'Ჶ' => 'ჶ',
-  'Ჷ' => 'ჷ',
-  'Ჸ' => 'ჸ',
-  'Ჹ' => 'ჹ',
-  'Ჺ' => 'ჺ',
-  'Ჽ' => 'ჽ',
-  'Ჾ' => 'ჾ',
-  'Ჿ' => 'ჿ',
-  'Ḁ' => 'ḁ',
-  'Ḃ' => 'ḃ',
-  'Ḅ' => 'ḅ',
-  'Ḇ' => 'ḇ',
-  'Ḉ' => 'ḉ',
-  'Ḋ' => 'ḋ',
-  'Ḍ' => 'ḍ',
-  'Ḏ' => 'ḏ',
-  'Ḑ' => 'ḑ',
-  'Ḓ' => 'ḓ',
-  'Ḕ' => 'ḕ',
-  'Ḗ' => 'ḗ',
-  'Ḙ' => 'ḙ',
-  'Ḛ' => 'ḛ',
-  'Ḝ' => 'ḝ',
-  'Ḟ' => 'ḟ',
-  'Ḡ' => 'ḡ',
-  'Ḣ' => 'ḣ',
-  'Ḥ' => 'ḥ',
-  'Ḧ' => 'ḧ',
-  'Ḩ' => 'ḩ',
-  'Ḫ' => 'ḫ',
-  'Ḭ' => 'ḭ',
-  'Ḯ' => 'ḯ',
-  'Ḱ' => 'ḱ',
-  'Ḳ' => 'ḳ',
-  'Ḵ' => 'ḵ',
-  'Ḷ' => 'ḷ',
-  'Ḹ' => 'ḹ',
-  'Ḻ' => 'ḻ',
-  'Ḽ' => 'ḽ',
-  'Ḿ' => 'ḿ',
-  'Ṁ' => 'ṁ',
-  'Ṃ' => 'ṃ',
-  'Ṅ' => 'ṅ',
-  'Ṇ' => 'ṇ',
-  'Ṉ' => 'ṉ',
-  'Ṋ' => 'ṋ',
-  'Ṍ' => 'ṍ',
-  'Ṏ' => 'ṏ',
-  'Ṑ' => 'ṑ',
-  'Ṓ' => 'ṓ',
-  'Ṕ' => 'ṕ',
-  'Ṗ' => 'ṗ',
-  'Ṙ' => 'ṙ',
-  'Ṛ' => 'ṛ',
-  'Ṝ' => 'ṝ',
-  'Ṟ' => 'ṟ',
-  'Ṡ' => 'ṡ',
-  'Ṣ' => 'ṣ',
-  'Ṥ' => 'ṥ',
-  'Ṧ' => 'ṧ',
-  'Ṩ' => 'ṩ',
-  'Ṫ' => 'ṫ',
-  'Ṭ' => 'ṭ',
-  'Ṯ' => 'ṯ',
-  'Ṱ' => 'ṱ',
-  'Ṳ' => 'ṳ',
-  'Ṵ' => 'ṵ',
-  'Ṷ' => 'ṷ',
-  'Ṹ' => 'ṹ',
-  'Ṻ' => 'ṻ',
-  'Ṽ' => 'ṽ',
-  'Ṿ' => 'ṿ',
-  'Ẁ' => 'ẁ',
-  'Ẃ' => 'ẃ',
-  'Ẅ' => 'ẅ',
-  'Ẇ' => 'ẇ',
-  'Ẉ' => 'ẉ',
-  'Ẋ' => 'ẋ',
-  'Ẍ' => 'ẍ',
-  'Ẏ' => 'ẏ',
-  'Ẑ' => 'ẑ',
-  'Ẓ' => 'ẓ',
-  'Ẕ' => 'ẕ',
-  'ẞ' => 'ß',
-  'Ạ' => 'ạ',
-  'Ả' => 'ả',
-  'Ấ' => 'ấ',
-  'Ầ' => 'ầ',
-  'Ẩ' => 'ẩ',
-  'Ẫ' => 'ẫ',
-  'Ậ' => 'ậ',
-  'Ắ' => 'ắ',
-  'Ằ' => 'ằ',
-  'Ẳ' => 'ẳ',
-  'Ẵ' => 'ẵ',
-  'Ặ' => 'ặ',
-  'Ẹ' => 'ẹ',
-  'Ẻ' => 'ẻ',
-  'Ẽ' => 'ẽ',
-  'Ế' => 'ế',
-  'Ề' => 'ề',
-  'Ể' => 'ể',
-  'Ễ' => 'ễ',
-  'Ệ' => 'ệ',
-  'Ỉ' => 'ỉ',
-  'Ị' => 'ị',
-  'Ọ' => 'ọ',
-  'Ỏ' => 'ỏ',
-  'Ố' => 'ố',
-  'Ồ' => 'ồ',
-  'Ổ' => 'ổ',
-  'Ỗ' => 'ỗ',
-  'Ộ' => 'ộ',
-  'Ớ' => 'ớ',
-  'Ờ' => 'ờ',
-  'Ở' => 'ở',
-  'Ỡ' => 'ỡ',
-  'Ợ' => 'ợ',
-  'Ụ' => 'ụ',
-  'Ủ' => 'ủ',
-  'Ứ' => 'ứ',
-  'Ừ' => 'ừ',
-  'Ử' => 'ử',
-  'Ữ' => 'ữ',
-  'Ự' => 'ự',
-  'Ỳ' => 'ỳ',
-  'Ỵ' => 'ỵ',
-  'Ỷ' => 'ỷ',
-  'Ỹ' => 'ỹ',
-  'Ỻ' => 'ỻ',
-  'Ỽ' => 'ỽ',
-  'Ỿ' => 'ỿ',
-  'Ἀ' => 'ἀ',
-  'Ἁ' => 'ἁ',
-  'Ἂ' => 'ἂ',
-  'Ἃ' => 'ἃ',
-  'Ἄ' => 'ἄ',
-  'Ἅ' => 'ἅ',
-  'Ἆ' => 'ἆ',
-  'Ἇ' => 'ἇ',
-  'Ἐ' => 'ἐ',
-  'Ἑ' => 'ἑ',
-  'Ἒ' => 'ἒ',
-  'Ἓ' => 'ἓ',
-  'Ἔ' => 'ἔ',
-  'Ἕ' => 'ἕ',
-  'Ἠ' => 'ἠ',
-  'Ἡ' => 'ἡ',
-  'Ἢ' => 'ἢ',
-  'Ἣ' => 'ἣ',
-  'Ἤ' => 'ἤ',
-  'Ἥ' => 'ἥ',
-  'Ἦ' => 'ἦ',
-  'Ἧ' => 'ἧ',
-  'Ἰ' => 'ἰ',
-  'Ἱ' => 'ἱ',
-  'Ἲ' => 'ἲ',
-  'Ἳ' => 'ἳ',
-  'Ἴ' => 'ἴ',
-  'Ἵ' => 'ἵ',
-  'Ἶ' => 'ἶ',
-  'Ἷ' => 'ἷ',
-  'Ὀ' => 'ὀ',
-  'Ὁ' => 'ὁ',
-  'Ὂ' => 'ὂ',
-  'Ὃ' => 'ὃ',
-  'Ὄ' => 'ὄ',
-  'Ὅ' => 'ὅ',
-  'Ὑ' => 'ὑ',
-  'Ὓ' => 'ὓ',
-  'Ὕ' => 'ὕ',
-  'Ὗ' => 'ὗ',
-  'Ὠ' => 'ὠ',
-  'Ὡ' => 'ὡ',
-  'Ὢ' => 'ὢ',
-  'Ὣ' => 'ὣ',
-  'Ὤ' => 'ὤ',
-  'Ὥ' => 'ὥ',
-  'Ὦ' => 'ὦ',
-  'Ὧ' => 'ὧ',
-  'ᾈ' => 'ᾀ',
-  'ᾉ' => 'ᾁ',
-  'ᾊ' => 'ᾂ',
-  'ᾋ' => 'ᾃ',
-  'ᾌ' => 'ᾄ',
-  'ᾍ' => 'ᾅ',
-  'ᾎ' => 'ᾆ',
-  'ᾏ' => 'ᾇ',
-  'ᾘ' => 'ᾐ',
-  'ᾙ' => 'ᾑ',
-  'ᾚ' => 'ᾒ',
-  'ᾛ' => 'ᾓ',
-  'ᾜ' => 'ᾔ',
-  'ᾝ' => 'ᾕ',
-  'ᾞ' => 'ᾖ',
-  'ᾟ' => 'ᾗ',
-  'ᾨ' => 'ᾠ',
-  'ᾩ' => 'ᾡ',
-  'ᾪ' => 'ᾢ',
-  'ᾫ' => 'ᾣ',
-  'ᾬ' => 'ᾤ',
-  'ᾭ' => 'ᾥ',
-  'ᾮ' => 'ᾦ',
-  'ᾯ' => 'ᾧ',
-  'Ᾰ' => 'ᾰ',
-  'Ᾱ' => 'ᾱ',
-  'Ὰ' => 'ὰ',
-  'Ά' => 'ά',
-  'ᾼ' => 'ᾳ',
-  'Ὲ' => 'ὲ',
-  'Έ' => 'έ',
-  'Ὴ' => 'ὴ',
-  'Ή' => 'ή',
-  'ῌ' => 'ῃ',
-  'Ῐ' => 'ῐ',
-  'Ῑ' => 'ῑ',
-  'Ὶ' => 'ὶ',
-  'Ί' => 'ί',
-  'Ῠ' => 'ῠ',
-  'Ῡ' => 'ῡ',
-  'Ὺ' => 'ὺ',
-  'Ύ' => 'ύ',
-  'Ῥ' => 'ῥ',
-  'Ὸ' => 'ὸ',
-  'Ό' => 'ό',
-  'Ὼ' => 'ὼ',
-  'Ώ' => 'ώ',
-  'ῼ' => 'ῳ',
-  'Ω' => 'ω',
-  'K' => 'k',
-  'Å' => 'å',
-  'Ⅎ' => 'ⅎ',
-  'Ⅰ' => 'ⅰ',
-  'Ⅱ' => 'ⅱ',
-  'Ⅲ' => 'ⅲ',
-  'Ⅳ' => 'ⅳ',
-  'Ⅴ' => 'ⅴ',
-  'Ⅵ' => 'ⅵ',
-  'Ⅶ' => 'ⅶ',
-  'Ⅷ' => 'ⅷ',
-  'Ⅸ' => 'ⅸ',
-  'Ⅹ' => 'ⅹ',
-  'Ⅺ' => 'ⅺ',
-  'Ⅻ' => 'ⅻ',
-  'Ⅼ' => 'ⅼ',
-  'Ⅽ' => 'ⅽ',
-  'Ⅾ' => 'ⅾ',
-  'Ⅿ' => 'ⅿ',
-  'Ↄ' => 'ↄ',
-  'Ⓐ' => 'ⓐ',
-  'Ⓑ' => 'ⓑ',
-  'Ⓒ' => 'ⓒ',
-  'Ⓓ' => 'ⓓ',
-  'Ⓔ' => 'ⓔ',
-  'Ⓕ' => 'ⓕ',
-  'Ⓖ' => 'ⓖ',
-  'Ⓗ' => 'ⓗ',
-  'Ⓘ' => 'ⓘ',
-  'Ⓙ' => 'ⓙ',
-  'Ⓚ' => 'ⓚ',
-  'Ⓛ' => 'ⓛ',
-  'Ⓜ' => 'ⓜ',
-  'Ⓝ' => 'ⓝ',
-  'Ⓞ' => 'ⓞ',
-  'Ⓟ' => 'ⓟ',
-  'Ⓠ' => 'ⓠ',
-  'Ⓡ' => 'ⓡ',
-  'Ⓢ' => 'ⓢ',
-  'Ⓣ' => 'ⓣ',
-  'Ⓤ' => 'ⓤ',
-  'Ⓥ' => 'ⓥ',
-  'Ⓦ' => 'ⓦ',
-  'Ⓧ' => 'ⓧ',
-  'Ⓨ' => 'ⓨ',
-  'Ⓩ' => 'ⓩ',
-  'Ⰰ' => 'ⰰ',
-  'Ⰱ' => 'ⰱ',
-  'Ⰲ' => 'ⰲ',
-  'Ⰳ' => 'ⰳ',
-  'Ⰴ' => 'ⰴ',
-  'Ⰵ' => 'ⰵ',
-  'Ⰶ' => 'ⰶ',
-  'Ⰷ' => 'ⰷ',
-  'Ⰸ' => 'ⰸ',
-  'Ⰹ' => 'ⰹ',
-  'Ⰺ' => 'ⰺ',
-  'Ⰻ' => 'ⰻ',
-  'Ⰼ' => 'ⰼ',
-  'Ⰽ' => 'ⰽ',
-  'Ⰾ' => 'ⰾ',
-  'Ⰿ' => 'ⰿ',
-  'Ⱀ' => 'ⱀ',
-  'Ⱁ' => 'ⱁ',
-  'Ⱂ' => 'ⱂ',
-  'Ⱃ' => 'ⱃ',
-  'Ⱄ' => 'ⱄ',
-  'Ⱅ' => 'ⱅ',
-  'Ⱆ' => 'ⱆ',
-  'Ⱇ' => 'ⱇ',
-  'Ⱈ' => 'ⱈ',
-  'Ⱉ' => 'ⱉ',
-  'Ⱊ' => 'ⱊ',
-  'Ⱋ' => 'ⱋ',
-  'Ⱌ' => 'ⱌ',
-  'Ⱍ' => 'ⱍ',
-  'Ⱎ' => 'ⱎ',
-  'Ⱏ' => 'ⱏ',
-  'Ⱐ' => 'ⱐ',
-  'Ⱑ' => 'ⱑ',
-  'Ⱒ' => 'ⱒ',
-  'Ⱓ' => 'ⱓ',
-  'Ⱔ' => 'ⱔ',
-  'Ⱕ' => 'ⱕ',
-  'Ⱖ' => 'ⱖ',
-  'Ⱗ' => 'ⱗ',
-  'Ⱘ' => 'ⱘ',
-  'Ⱙ' => 'ⱙ',
-  'Ⱚ' => 'ⱚ',
-  'Ⱛ' => 'ⱛ',
-  'Ⱜ' => 'ⱜ',
-  'Ⱝ' => 'ⱝ',
-  'Ⱞ' => 'ⱞ',
-  'Ⱡ' => 'ⱡ',
-  'Ɫ' => 'ɫ',
-  'Ᵽ' => 'ᵽ',
-  'Ɽ' => 'ɽ',
-  'Ⱨ' => 'ⱨ',
-  'Ⱪ' => 'ⱪ',
-  'Ⱬ' => 'ⱬ',
-  'Ɑ' => 'ɑ',
-  'Ɱ' => 'ɱ',
-  'Ɐ' => 'ɐ',
-  'Ɒ' => 'ɒ',
-  'Ⱳ' => 'ⱳ',
-  'Ⱶ' => 'ⱶ',
-  'Ȿ' => 'ȿ',
-  'Ɀ' => 'ɀ',
-  'Ⲁ' => 'ⲁ',
-  'Ⲃ' => 'ⲃ',
-  'Ⲅ' => 'ⲅ',
-  'Ⲇ' => 'ⲇ',
-  'Ⲉ' => 'ⲉ',
-  'Ⲋ' => 'ⲋ',
-  'Ⲍ' => 'ⲍ',
-  'Ⲏ' => 'ⲏ',
-  'Ⲑ' => 'ⲑ',
-  'Ⲓ' => 'ⲓ',
-  'Ⲕ' => 'ⲕ',
-  'Ⲗ' => 'ⲗ',
-  'Ⲙ' => 'ⲙ',
-  'Ⲛ' => 'ⲛ',
-  'Ⲝ' => 'ⲝ',
-  'Ⲟ' => 'ⲟ',
-  'Ⲡ' => 'ⲡ',
-  'Ⲣ' => 'ⲣ',
-  'Ⲥ' => 'ⲥ',
-  'Ⲧ' => 'ⲧ',
-  'Ⲩ' => 'ⲩ',
-  'Ⲫ' => 'ⲫ',
-  'Ⲭ' => 'ⲭ',
-  'Ⲯ' => 'ⲯ',
-  'Ⲱ' => 'ⲱ',
-  'Ⲳ' => 'ⲳ',
-  'Ⲵ' => 'ⲵ',
-  'Ⲷ' => 'ⲷ',
-  'Ⲹ' => 'ⲹ',
-  'Ⲻ' => 'ⲻ',
-  'Ⲽ' => 'ⲽ',
-  'Ⲿ' => 'ⲿ',
-  'Ⳁ' => 'ⳁ',
-  'Ⳃ' => 'ⳃ',
-  'Ⳅ' => 'ⳅ',
-  'Ⳇ' => 'ⳇ',
-  'Ⳉ' => 'ⳉ',
-  'Ⳋ' => 'ⳋ',
-  'Ⳍ' => 'ⳍ',
-  'Ⳏ' => 'ⳏ',
-  'Ⳑ' => 'ⳑ',
-  'Ⳓ' => 'ⳓ',
-  'Ⳕ' => 'ⳕ',
-  'Ⳗ' => 'ⳗ',
-  'Ⳙ' => 'ⳙ',
-  'Ⳛ' => 'ⳛ',
-  'Ⳝ' => 'ⳝ',
-  'Ⳟ' => 'ⳟ',
-  'Ⳡ' => 'ⳡ',
-  'Ⳣ' => 'ⳣ',
-  'Ⳬ' => 'ⳬ',
-  'Ⳮ' => 'ⳮ',
-  'Ⳳ' => 'ⳳ',
-  'Ꙁ' => 'ꙁ',
-  'Ꙃ' => 'ꙃ',
-  'Ꙅ' => 'ꙅ',
-  'Ꙇ' => 'ꙇ',
-  'Ꙉ' => 'ꙉ',
-  'Ꙋ' => 'ꙋ',
-  'Ꙍ' => 'ꙍ',
-  'Ꙏ' => 'ꙏ',
-  'Ꙑ' => 'ꙑ',
-  'Ꙓ' => 'ꙓ',
-  'Ꙕ' => 'ꙕ',
-  'Ꙗ' => 'ꙗ',
-  'Ꙙ' => 'ꙙ',
-  'Ꙛ' => 'ꙛ',
-  'Ꙝ' => 'ꙝ',
-  'Ꙟ' => 'ꙟ',
-  'Ꙡ' => 'ꙡ',
-  'Ꙣ' => 'ꙣ',
-  'Ꙥ' => 'ꙥ',
-  'Ꙧ' => 'ꙧ',
-  'Ꙩ' => 'ꙩ',
-  'Ꙫ' => 'ꙫ',
-  'Ꙭ' => 'ꙭ',
-  'Ꚁ' => 'ꚁ',
-  'Ꚃ' => 'ꚃ',
-  'Ꚅ' => 'ꚅ',
-  'Ꚇ' => 'ꚇ',
-  'Ꚉ' => 'ꚉ',
-  'Ꚋ' => 'ꚋ',
-  'Ꚍ' => 'ꚍ',
-  'Ꚏ' => 'ꚏ',
-  'Ꚑ' => 'ꚑ',
-  'Ꚓ' => 'ꚓ',
-  'Ꚕ' => 'ꚕ',
-  'Ꚗ' => 'ꚗ',
-  'Ꚙ' => 'ꚙ',
-  'Ꚛ' => 'ꚛ',
-  'Ꜣ' => 'ꜣ',
-  'Ꜥ' => 'ꜥ',
-  'Ꜧ' => 'ꜧ',
-  'Ꜩ' => 'ꜩ',
-  'Ꜫ' => 'ꜫ',
-  'Ꜭ' => 'ꜭ',
-  'Ꜯ' => 'ꜯ',
-  'Ꜳ' => 'ꜳ',
-  'Ꜵ' => 'ꜵ',
-  'Ꜷ' => 'ꜷ',
-  'Ꜹ' => 'ꜹ',
-  'Ꜻ' => 'ꜻ',
-  'Ꜽ' => 'ꜽ',
-  'Ꜿ' => 'ꜿ',
-  'Ꝁ' => 'ꝁ',
-  'Ꝃ' => 'ꝃ',
-  'Ꝅ' => 'ꝅ',
-  'Ꝇ' => 'ꝇ',
-  'Ꝉ' => 'ꝉ',
-  'Ꝋ' => 'ꝋ',
-  'Ꝍ' => 'ꝍ',
-  'Ꝏ' => 'ꝏ',
-  'Ꝑ' => 'ꝑ',
-  'Ꝓ' => 'ꝓ',
-  'Ꝕ' => 'ꝕ',
-  'Ꝗ' => 'ꝗ',
-  'Ꝙ' => 'ꝙ',
-  'Ꝛ' => 'ꝛ',
-  'Ꝝ' => 'ꝝ',
-  'Ꝟ' => 'ꝟ',
-  'Ꝡ' => 'ꝡ',
-  'Ꝣ' => 'ꝣ',
-  'Ꝥ' => 'ꝥ',
-  'Ꝧ' => 'ꝧ',
-  'Ꝩ' => 'ꝩ',
-  'Ꝫ' => 'ꝫ',
-  'Ꝭ' => 'ꝭ',
-  'Ꝯ' => 'ꝯ',
-  'Ꝺ' => 'ꝺ',
-  'Ꝼ' => 'ꝼ',
-  'Ᵹ' => 'ᵹ',
-  'Ꝿ' => 'ꝿ',
-  'Ꞁ' => 'ꞁ',
-  'Ꞃ' => 'ꞃ',
-  'Ꞅ' => 'ꞅ',
-  'Ꞇ' => 'ꞇ',
-  'Ꞌ' => 'ꞌ',
-  'Ɥ' => 'ɥ',
-  'Ꞑ' => 'ꞑ',
-  'Ꞓ' => 'ꞓ',
-  'Ꞗ' => 'ꞗ',
-  'Ꞙ' => 'ꞙ',
-  'Ꞛ' => 'ꞛ',
-  'Ꞝ' => 'ꞝ',
-  'Ꞟ' => 'ꞟ',
-  'Ꞡ' => 'ꞡ',
-  'Ꞣ' => 'ꞣ',
-  'Ꞥ' => 'ꞥ',
-  'Ꞧ' => 'ꞧ',
-  'Ꞩ' => 'ꞩ',
-  'Ɦ' => 'ɦ',
-  'Ɜ' => 'ɜ',
-  'Ɡ' => 'ɡ',
-  'Ɬ' => 'ɬ',
-  'Ɪ' => 'ɪ',
-  'Ʞ' => 'ʞ',
-  'Ʇ' => 'ʇ',
-  'Ʝ' => 'ʝ',
-  'Ꭓ' => 'ꭓ',
-  'Ꞵ' => 'ꞵ',
-  'Ꞷ' => 'ꞷ',
-  'Ꞹ' => 'ꞹ',
-  'Ꞻ' => 'ꞻ',
-  'Ꞽ' => 'ꞽ',
-  'Ꞿ' => 'ꞿ',
-  'Ꟃ' => 'ꟃ',
-  'Ꞔ' => 'ꞔ',
-  'Ʂ' => 'ʂ',
-  'Ᶎ' => 'ᶎ',
-  'Ꟈ' => 'ꟈ',
-  'Ꟊ' => 'ꟊ',
-  'Ꟶ' => 'ꟶ',
-  'A' => 'a',
-  'B' => 'b',
-  'C' => 'c',
-  'D' => 'd',
-  'E' => 'e',
-  'F' => 'f',
-  'G' => 'g',
-  'H' => 'h',
-  'I' => 'i',
-  'J' => 'j',
-  'K' => 'k',
-  'L' => 'l',
-  'M' => 'm',
-  'N' => 'n',
-  'O' => 'o',
-  'P' => 'p',
-  'Q' => 'q',
-  'R' => 'r',
-  'S' => 's',
-  'T' => 't',
-  'U' => 'u',
-  'V' => 'v',
-  'W' => 'w',
-  'X' => 'x',
-  'Y' => 'y',
-  'Z' => 'z',
-  '𐐀' => '𐐨',
-  '𐐁' => '𐐩',
-  '𐐂' => '𐐪',
-  '𐐃' => '𐐫',
-  '𐐄' => '𐐬',
-  '𐐅' => '𐐭',
-  '𐐆' => '𐐮',
-  '𐐇' => '𐐯',
-  '𐐈' => '𐐰',
-  '𐐉' => '𐐱',
-  '𐐊' => '𐐲',
-  '𐐋' => '𐐳',
-  '𐐌' => '𐐴',
-  '𐐍' => '𐐵',
-  '𐐎' => '𐐶',
-  '𐐏' => '𐐷',
-  '𐐐' => '𐐸',
-  '𐐑' => '𐐹',
-  '𐐒' => '𐐺',
-  '𐐓' => '𐐻',
-  '𐐔' => '𐐼',
-  '𐐕' => '𐐽',
-  '𐐖' => '𐐾',
-  '𐐗' => '𐐿',
-  '𐐘' => '𐑀',
-  '𐐙' => '𐑁',
-  '𐐚' => '𐑂',
-  '𐐛' => '𐑃',
-  '𐐜' => '𐑄',
-  '𐐝' => '𐑅',
-  '𐐞' => '𐑆',
-  '𐐟' => '𐑇',
-  '𐐠' => '𐑈',
-  '𐐡' => '𐑉',
-  '𐐢' => '𐑊',
-  '𐐣' => '𐑋',
-  '𐐤' => '𐑌',
-  '𐐥' => '𐑍',
-  '𐐦' => '𐑎',
-  '𐐧' => '𐑏',
-  '𐒰' => '𐓘',
-  '𐒱' => '𐓙',
-  '𐒲' => '𐓚',
-  '𐒳' => '𐓛',
-  '𐒴' => '𐓜',
-  '𐒵' => '𐓝',
-  '𐒶' => '𐓞',
-  '𐒷' => '𐓟',
-  '𐒸' => '𐓠',
-  '𐒹' => '𐓡',
-  '𐒺' => '𐓢',
-  '𐒻' => '𐓣',
-  '𐒼' => '𐓤',
-  '𐒽' => '𐓥',
-  '𐒾' => '𐓦',
-  '𐒿' => '𐓧',
-  '𐓀' => '𐓨',
-  '𐓁' => '𐓩',
-  '𐓂' => '𐓪',
-  '𐓃' => '𐓫',
-  '𐓄' => '𐓬',
-  '𐓅' => '𐓭',
-  '𐓆' => '𐓮',
-  '𐓇' => '𐓯',
-  '𐓈' => '𐓰',
-  '𐓉' => '𐓱',
-  '𐓊' => '𐓲',
-  '𐓋' => '𐓳',
-  '𐓌' => '𐓴',
-  '𐓍' => '𐓵',
-  '𐓎' => '𐓶',
-  '𐓏' => '𐓷',
-  '𐓐' => '𐓸',
-  '𐓑' => '𐓹',
-  '𐓒' => '𐓺',
-  '𐓓' => '𐓻',
-  '𐲀' => '𐳀',
-  '𐲁' => '𐳁',
-  '𐲂' => '𐳂',
-  '𐲃' => '𐳃',
-  '𐲄' => '𐳄',
-  '𐲅' => '𐳅',
-  '𐲆' => '𐳆',
-  '𐲇' => '𐳇',
-  '𐲈' => '𐳈',
-  '𐲉' => '𐳉',
-  '𐲊' => '𐳊',
-  '𐲋' => '𐳋',
-  '𐲌' => '𐳌',
-  '𐲍' => '𐳍',
-  '𐲎' => '𐳎',
-  '𐲏' => '𐳏',
-  '𐲐' => '𐳐',
-  '𐲑' => '𐳑',
-  '𐲒' => '𐳒',
-  '𐲓' => '𐳓',
-  '𐲔' => '𐳔',
-  '𐲕' => '𐳕',
-  '𐲖' => '𐳖',
-  '𐲗' => '𐳗',
-  '𐲘' => '𐳘',
-  '𐲙' => '𐳙',
-  '𐲚' => '𐳚',
-  '𐲛' => '𐳛',
-  '𐲜' => '𐳜',
-  '𐲝' => '𐳝',
-  '𐲞' => '𐳞',
-  '𐲟' => '𐳟',
-  '𐲠' => '𐳠',
-  '𐲡' => '𐳡',
-  '𐲢' => '𐳢',
-  '𐲣' => '𐳣',
-  '𐲤' => '𐳤',
-  '𐲥' => '𐳥',
-  '𐲦' => '𐳦',
-  '𐲧' => '𐳧',
-  '𐲨' => '𐳨',
-  '𐲩' => '𐳩',
-  '𐲪' => '𐳪',
-  '𐲫' => '𐳫',
-  '𐲬' => '𐳬',
-  '𐲭' => '𐳭',
-  '𐲮' => '𐳮',
-  '𐲯' => '𐳯',
-  '𐲰' => '𐳰',
-  '𐲱' => '𐳱',
-  '𐲲' => '𐳲',
-  '𑢠' => '𑣀',
-  '𑢡' => '𑣁',
-  '𑢢' => '𑣂',
-  '𑢣' => '𑣃',
-  '𑢤' => '𑣄',
-  '𑢥' => '𑣅',
-  '𑢦' => '𑣆',
-  '𑢧' => '𑣇',
-  '𑢨' => '𑣈',
-  '𑢩' => '𑣉',
-  '𑢪' => '𑣊',
-  '𑢫' => '𑣋',
-  '𑢬' => '𑣌',
-  '𑢭' => '𑣍',
-  '𑢮' => '𑣎',
-  '𑢯' => '𑣏',
-  '𑢰' => '𑣐',
-  '𑢱' => '𑣑',
-  '𑢲' => '𑣒',
-  '𑢳' => '𑣓',
-  '𑢴' => '𑣔',
-  '𑢵' => '𑣕',
-  '𑢶' => '𑣖',
-  '𑢷' => '𑣗',
-  '𑢸' => '𑣘',
-  '𑢹' => '𑣙',
-  '𑢺' => '𑣚',
-  '𑢻' => '𑣛',
-  '𑢼' => '𑣜',
-  '𑢽' => '𑣝',
-  '𑢾' => '𑣞',
-  '𑢿' => '𑣟',
-  '𖹀' => '𖹠',
-  '𖹁' => '𖹡',
-  '𖹂' => '𖹢',
-  '𖹃' => '𖹣',
-  '𖹄' => '𖹤',
-  '𖹅' => '𖹥',
-  '𖹆' => '𖹦',
-  '𖹇' => '𖹧',
-  '𖹈' => '𖹨',
-  '𖹉' => '𖹩',
-  '𖹊' => '𖹪',
-  '𖹋' => '𖹫',
-  '𖹌' => '𖹬',
-  '𖹍' => '𖹭',
-  '𖹎' => '𖹮',
-  '𖹏' => '𖹯',
-  '𖹐' => '𖹰',
-  '𖹑' => '𖹱',
-  '𖹒' => '𖹲',
-  '𖹓' => '𖹳',
-  '𖹔' => '𖹴',
-  '𖹕' => '𖹵',
-  '𖹖' => '𖹶',
-  '𖹗' => '𖹷',
-  '𖹘' => '𖹸',
-  '𖹙' => '𖹹',
-  '𖹚' => '𖹺',
-  '𖹛' => '𖹻',
-  '𖹜' => '𖹼',
-  '𖹝' => '𖹽',
-  '𖹞' => '𖹾',
-  '𖹟' => '𖹿',
-  '𞤀' => '𞤢',
-  '𞤁' => '𞤣',
-  '𞤂' => '𞤤',
-  '𞤃' => '𞤥',
-  '𞤄' => '𞤦',
-  '𞤅' => '𞤧',
-  '𞤆' => '𞤨',
-  '𞤇' => '𞤩',
-  '𞤈' => '𞤪',
-  '𞤉' => '𞤫',
-  '𞤊' => '𞤬',
-  '𞤋' => '𞤭',
-  '𞤌' => '𞤮',
-  '𞤍' => '𞤯',
-  '𞤎' => '𞤰',
-  '𞤏' => '𞤱',
-  '𞤐' => '𞤲',
-  '𞤑' => '𞤳',
-  '𞤒' => '𞤴',
-  '𞤓' => '𞤵',
-  '𞤔' => '𞤶',
-  '𞤕' => '𞤷',
-  '𞤖' => '𞤸',
-  '𞤗' => '𞤹',
-  '𞤘' => '𞤺',
-  '𞤙' => '𞤻',
-  '𞤚' => '𞤼',
-  '𞤛' => '𞤽',
-  '𞤜' => '𞤾',
-  '𞤝' => '𞤿',
-  '𞤞' => '𞥀',
-  '𞤟' => '𞥁',
-  '𞤠' => '𞥂',
-  '𞤡' => '𞥃',
-);
diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
deleted file mode 100644
index 2a8f6e7..0000000
--- a/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
+++ /dev/null
@@ -1,5 +0,0 @@
-<?php
-
-// from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt
-
-return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u';
diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php
deleted file mode 100644
index 56b9cb8..0000000
--- a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php
+++ /dev/null
@@ -1,1489 +0,0 @@
-<?php
-
-return array (
-  'a' => 'A',
-  'b' => 'B',
-  'c' => 'C',
-  'd' => 'D',
-  'e' => 'E',
-  'f' => 'F',
-  'g' => 'G',
-  'h' => 'H',
-  'i' => 'I',
-  'j' => 'J',
-  'k' => 'K',
-  'l' => 'L',
-  'm' => 'M',
-  'n' => 'N',
-  'o' => 'O',
-  'p' => 'P',
-  'q' => 'Q',
-  'r' => 'R',
-  's' => 'S',
-  't' => 'T',
-  'u' => 'U',
-  'v' => 'V',
-  'w' => 'W',
-  'x' => 'X',
-  'y' => 'Y',
-  'z' => 'Z',
-  'µ' => 'Μ',
-  'à' => 'À',
-  'á' => 'Á',
-  'â' => 'Â',
-  'ã' => 'Ã',
-  'ä' => 'Ä',
-  'å' => 'Å',
-  'æ' => 'Æ',
-  'ç' => 'Ç',
-  'è' => 'È',
-  'é' => 'É',
-  'ê' => 'Ê',
-  'ë' => 'Ë',
-  'ì' => 'Ì',
-  'í' => 'Í',
-  'î' => 'Î',
-  'ï' => 'Ï',
-  'ð' => 'Ð',
-  'ñ' => 'Ñ',
-  'ò' => 'Ò',
-  'ó' => 'Ó',
-  'ô' => 'Ô',
-  'õ' => 'Õ',
-  'ö' => 'Ö',
-  'ø' => 'Ø',
-  'ù' => 'Ù',
-  'ú' => 'Ú',
-  'û' => 'Û',
-  'ü' => 'Ü',
-  'ý' => 'Ý',
-  'þ' => 'Þ',
-  'ÿ' => 'Ÿ',
-  'ā' => 'Ā',
-  'ă' => 'Ă',
-  'ą' => 'Ą',
-  'ć' => 'Ć',
-  'ĉ' => 'Ĉ',
-  'ċ' => 'Ċ',
-  'č' => 'Č',
-  'ď' => 'Ď',
-  'đ' => 'Đ',
-  'ē' => 'Ē',
-  'ĕ' => 'Ĕ',
-  'ė' => 'Ė',
-  'ę' => 'Ę',
-  'ě' => 'Ě',
-  'ĝ' => 'Ĝ',
-  'ğ' => 'Ğ',
-  'ġ' => 'Ġ',
-  'ģ' => 'Ģ',
-  'ĥ' => 'Ĥ',
-  'ħ' => 'Ħ',
-  'ĩ' => 'Ĩ',
-  'ī' => 'Ī',
-  'ĭ' => 'Ĭ',
-  'į' => 'Į',
-  'ı' => 'I',
-  'ij' => 'IJ',
-  'ĵ' => 'Ĵ',
-  'ķ' => 'Ķ',
-  'ĺ' => 'Ĺ',
-  'ļ' => 'Ļ',
-  'ľ' => 'Ľ',
-  'ŀ' => 'Ŀ',
-  'ł' => 'Ł',
-  'ń' => 'Ń',
-  'ņ' => 'Ņ',
-  'ň' => 'Ň',
-  'ŋ' => 'Ŋ',
-  'ō' => 'Ō',
-  'ŏ' => 'Ŏ',
-  'ő' => 'Ő',
-  'œ' => 'Œ',
-  'ŕ' => 'Ŕ',
-  'ŗ' => 'Ŗ',
-  'ř' => 'Ř',
-  'ś' => 'Ś',
-  'ŝ' => 'Ŝ',
-  'ş' => 'Ş',
-  'š' => 'Š',
-  'ţ' => 'Ţ',
-  'ť' => 'Ť',
-  'ŧ' => 'Ŧ',
-  'ũ' => 'Ũ',
-  'ū' => 'Ū',
-  'ŭ' => 'Ŭ',
-  'ů' => 'Ů',
-  'ű' => 'Ű',
-  'ų' => 'Ų',
-  'ŵ' => 'Ŵ',
-  'ŷ' => 'Ŷ',
-  'ź' => 'Ź',
-  'ż' => 'Ż',
-  'ž' => 'Ž',
-  'ſ' => 'S',
-  'ƀ' => 'Ƀ',
-  'ƃ' => 'Ƃ',
-  'ƅ' => 'Ƅ',
-  'ƈ' => 'Ƈ',
-  'ƌ' => 'Ƌ',
-  'ƒ' => 'Ƒ',
-  'ƕ' => 'Ƕ',
-  'ƙ' => 'Ƙ',
-  'ƚ' => 'Ƚ',
-  'ƞ' => 'Ƞ',
-  'ơ' => 'Ơ',
-  'ƣ' => 'Ƣ',
-  'ƥ' => 'Ƥ',
-  'ƨ' => 'Ƨ',
-  'ƭ' => 'Ƭ',
-  'ư' => 'Ư',
-  'ƴ' => 'Ƴ',
-  'ƶ' => 'Ƶ',
-  'ƹ' => 'Ƹ',
-  'ƽ' => 'Ƽ',
-  'ƿ' => 'Ƿ',
-  'Dž' => 'DŽ',
-  'dž' => 'DŽ',
-  'Lj' => 'LJ',
-  'lj' => 'LJ',
-  'Nj' => 'NJ',
-  'nj' => 'NJ',
-  'ǎ' => 'Ǎ',
-  'ǐ' => 'Ǐ',
-  'ǒ' => 'Ǒ',
-  'ǔ' => 'Ǔ',
-  'ǖ' => 'Ǖ',
-  'ǘ' => 'Ǘ',
-  'ǚ' => 'Ǚ',
-  'ǜ' => 'Ǜ',
-  'ǝ' => 'Ǝ',
-  'ǟ' => 'Ǟ',
-  'ǡ' => 'Ǡ',
-  'ǣ' => 'Ǣ',
-  'ǥ' => 'Ǥ',
-  'ǧ' => 'Ǧ',
-  'ǩ' => 'Ǩ',
-  'ǫ' => 'Ǫ',
-  'ǭ' => 'Ǭ',
-  'ǯ' => 'Ǯ',
-  'Dz' => 'DZ',
-  'dz' => 'DZ',
-  'ǵ' => 'Ǵ',
-  'ǹ' => 'Ǹ',
-  'ǻ' => 'Ǻ',
-  'ǽ' => 'Ǽ',
-  'ǿ' => 'Ǿ',
-  'ȁ' => 'Ȁ',
-  'ȃ' => 'Ȃ',
-  'ȅ' => 'Ȅ',
-  'ȇ' => 'Ȇ',
-  'ȉ' => 'Ȉ',
-  'ȋ' => 'Ȋ',
-  'ȍ' => 'Ȍ',
-  'ȏ' => 'Ȏ',
-  'ȑ' => 'Ȑ',
-  'ȓ' => 'Ȓ',
-  'ȕ' => 'Ȕ',
-  'ȗ' => 'Ȗ',
-  'ș' => 'Ș',
-  'ț' => 'Ț',
-  'ȝ' => 'Ȝ',
-  'ȟ' => 'Ȟ',
-  'ȣ' => 'Ȣ',
-  'ȥ' => 'Ȥ',
-  'ȧ' => 'Ȧ',
-  'ȩ' => 'Ȩ',
-  'ȫ' => 'Ȫ',
-  'ȭ' => 'Ȭ',
-  'ȯ' => 'Ȯ',
-  'ȱ' => 'Ȱ',
-  'ȳ' => 'Ȳ',
-  'ȼ' => 'Ȼ',
-  'ȿ' => 'Ȿ',
-  'ɀ' => 'Ɀ',
-  'ɂ' => 'Ɂ',
-  'ɇ' => 'Ɇ',
-  'ɉ' => 'Ɉ',
-  'ɋ' => 'Ɋ',
-  'ɍ' => 'Ɍ',
-  'ɏ' => 'Ɏ',
-  'ɐ' => 'Ɐ',
-  'ɑ' => 'Ɑ',
-  'ɒ' => 'Ɒ',
-  'ɓ' => 'Ɓ',
-  'ɔ' => 'Ɔ',
-  'ɖ' => 'Ɖ',
-  'ɗ' => 'Ɗ',
-  'ə' => 'Ə',
-  'ɛ' => 'Ɛ',
-  'ɜ' => 'Ɜ',
-  'ɠ' => 'Ɠ',
-  'ɡ' => 'Ɡ',
-  'ɣ' => 'Ɣ',
-  'ɥ' => 'Ɥ',
-  'ɦ' => 'Ɦ',
-  'ɨ' => 'Ɨ',
-  'ɩ' => 'Ɩ',
-  'ɪ' => 'Ɪ',
-  'ɫ' => 'Ɫ',
-  'ɬ' => 'Ɬ',
-  'ɯ' => 'Ɯ',
-  'ɱ' => 'Ɱ',
-  'ɲ' => 'Ɲ',
-  'ɵ' => 'Ɵ',
-  'ɽ' => 'Ɽ',
-  'ʀ' => 'Ʀ',
-  'ʂ' => 'Ʂ',
-  'ʃ' => 'Ʃ',
-  'ʇ' => 'Ʇ',
-  'ʈ' => 'Ʈ',
-  'ʉ' => 'Ʉ',
-  'ʊ' => 'Ʊ',
-  'ʋ' => 'Ʋ',
-  'ʌ' => 'Ʌ',
-  'ʒ' => 'Ʒ',
-  'ʝ' => 'Ʝ',
-  'ʞ' => 'Ʞ',
-  'ͅ' => 'Ι',
-  'ͱ' => 'Ͱ',
-  'ͳ' => 'Ͳ',
-  'ͷ' => 'Ͷ',
-  'ͻ' => 'Ͻ',
-  'ͼ' => 'Ͼ',
-  'ͽ' => 'Ͽ',
-  'ά' => 'Ά',
-  'έ' => 'Έ',
-  'ή' => 'Ή',
-  'ί' => 'Ί',
-  'α' => 'Α',
-  'β' => 'Β',
-  'γ' => 'Γ',
-  'δ' => 'Δ',
-  'ε' => 'Ε',
-  'ζ' => 'Ζ',
-  'η' => 'Η',
-  'θ' => 'Θ',
-  'ι' => 'Ι',
-  'κ' => 'Κ',
-  'λ' => 'Λ',
-  'μ' => 'Μ',
-  'ν' => 'Ν',
-  'ξ' => 'Ξ',
-  'ο' => 'Ο',
-  'π' => 'Π',
-  'ρ' => 'Ρ',
-  'ς' => 'Σ',
-  'σ' => 'Σ',
-  'τ' => 'Τ',
-  'υ' => 'Υ',
-  'φ' => 'Φ',
-  'χ' => 'Χ',
-  'ψ' => 'Ψ',
-  'ω' => 'Ω',
-  'ϊ' => 'Ϊ',
-  'ϋ' => 'Ϋ',
-  'ό' => 'Ό',
-  'ύ' => 'Ύ',
-  'ώ' => 'Ώ',
-  'ϐ' => 'Β',
-  'ϑ' => 'Θ',
-  'ϕ' => 'Φ',
-  'ϖ' => 'Π',
-  'ϗ' => 'Ϗ',
-  'ϙ' => 'Ϙ',
-  'ϛ' => 'Ϛ',
-  'ϝ' => 'Ϝ',
-  'ϟ' => 'Ϟ',
-  'ϡ' => 'Ϡ',
-  'ϣ' => 'Ϣ',
-  'ϥ' => 'Ϥ',
-  'ϧ' => 'Ϧ',
-  'ϩ' => 'Ϩ',
-  'ϫ' => 'Ϫ',
-  'ϭ' => 'Ϭ',
-  'ϯ' => 'Ϯ',
-  'ϰ' => 'Κ',
-  'ϱ' => 'Ρ',
-  'ϲ' => 'Ϲ',
-  'ϳ' => 'Ϳ',
-  'ϵ' => 'Ε',
-  'ϸ' => 'Ϸ',
-  'ϻ' => 'Ϻ',
-  'а' => 'А',
-  'б' => 'Б',
-  'в' => 'В',
-  'г' => 'Г',
-  'д' => 'Д',
-  'е' => 'Е',
-  'ж' => 'Ж',
-  'з' => 'З',
-  'и' => 'И',
-  'й' => 'Й',
-  'к' => 'К',
-  'л' => 'Л',
-  'м' => 'М',
-  'н' => 'Н',
-  'о' => 'О',
-  'п' => 'П',
-  'р' => 'Р',
-  'с' => 'С',
-  'т' => 'Т',
-  'у' => 'У',
-  'ф' => 'Ф',
-  'х' => 'Х',
-  'ц' => 'Ц',
-  'ч' => 'Ч',
-  'ш' => 'Ш',
-  'щ' => 'Щ',
-  'ъ' => 'Ъ',
-  'ы' => 'Ы',
-  'ь' => 'Ь',
-  'э' => 'Э',
-  'ю' => 'Ю',
-  'я' => 'Я',
-  'ѐ' => 'Ѐ',
-  'ё' => 'Ё',
-  'ђ' => 'Ђ',
-  'ѓ' => 'Ѓ',
-  'є' => 'Є',
-  'ѕ' => 'Ѕ',
-  'і' => 'І',
-  'ї' => 'Ї',
-  'ј' => 'Ј',
-  'љ' => 'Љ',
-  'њ' => 'Њ',
-  'ћ' => 'Ћ',
-  'ќ' => 'Ќ',
-  'ѝ' => 'Ѝ',
-  'ў' => 'Ў',
-  'џ' => 'Џ',
-  'ѡ' => 'Ѡ',
-  'ѣ' => 'Ѣ',
-  'ѥ' => 'Ѥ',
-  'ѧ' => 'Ѧ',
-  'ѩ' => 'Ѩ',
-  'ѫ' => 'Ѫ',
-  'ѭ' => 'Ѭ',
-  'ѯ' => 'Ѯ',
-  'ѱ' => 'Ѱ',
-  'ѳ' => 'Ѳ',
-  'ѵ' => 'Ѵ',
-  'ѷ' => 'Ѷ',
-  'ѹ' => 'Ѹ',
-  'ѻ' => 'Ѻ',
-  'ѽ' => 'Ѽ',
-  'ѿ' => 'Ѿ',
-  'ҁ' => 'Ҁ',
-  'ҋ' => 'Ҋ',
-  'ҍ' => 'Ҍ',
-  'ҏ' => 'Ҏ',
-  'ґ' => 'Ґ',
-  'ғ' => 'Ғ',
-  'ҕ' => 'Ҕ',
-  'җ' => 'Җ',
-  'ҙ' => 'Ҙ',
-  'қ' => 'Қ',
-  'ҝ' => 'Ҝ',
-  'ҟ' => 'Ҟ',
-  'ҡ' => 'Ҡ',
-  'ң' => 'Ң',
-  'ҥ' => 'Ҥ',
-  'ҧ' => 'Ҧ',
-  'ҩ' => 'Ҩ',
-  'ҫ' => 'Ҫ',
-  'ҭ' => 'Ҭ',
-  'ү' => 'Ү',
-  'ұ' => 'Ұ',
-  'ҳ' => 'Ҳ',
-  'ҵ' => 'Ҵ',
-  'ҷ' => 'Ҷ',
-  'ҹ' => 'Ҹ',
-  'һ' => 'Һ',
-  'ҽ' => 'Ҽ',
-  'ҿ' => 'Ҿ',
-  'ӂ' => 'Ӂ',
-  'ӄ' => 'Ӄ',
-  'ӆ' => 'Ӆ',
-  'ӈ' => 'Ӈ',
-  'ӊ' => 'Ӊ',
-  'ӌ' => 'Ӌ',
-  'ӎ' => 'Ӎ',
-  'ӏ' => 'Ӏ',
-  'ӑ' => 'Ӑ',
-  'ӓ' => 'Ӓ',
-  'ӕ' => 'Ӕ',
-  'ӗ' => 'Ӗ',
-  'ә' => 'Ә',
-  'ӛ' => 'Ӛ',
-  'ӝ' => 'Ӝ',
-  'ӟ' => 'Ӟ',
-  'ӡ' => 'Ӡ',
-  'ӣ' => 'Ӣ',
-  'ӥ' => 'Ӥ',
-  'ӧ' => 'Ӧ',
-  'ө' => 'Ө',
-  'ӫ' => 'Ӫ',
-  'ӭ' => 'Ӭ',
-  'ӯ' => 'Ӯ',
-  'ӱ' => 'Ӱ',
-  'ӳ' => 'Ӳ',
-  'ӵ' => 'Ӵ',
-  'ӷ' => 'Ӷ',
-  'ӹ' => 'Ӹ',
-  'ӻ' => 'Ӻ',
-  'ӽ' => 'Ӽ',
-  'ӿ' => 'Ӿ',
-  'ԁ' => 'Ԁ',
-  'ԃ' => 'Ԃ',
-  'ԅ' => 'Ԅ',
-  'ԇ' => 'Ԇ',
-  'ԉ' => 'Ԉ',
-  'ԋ' => 'Ԋ',
-  'ԍ' => 'Ԍ',
-  'ԏ' => 'Ԏ',
-  'ԑ' => 'Ԑ',
-  'ԓ' => 'Ԓ',
-  'ԕ' => 'Ԕ',
-  'ԗ' => 'Ԗ',
-  'ԙ' => 'Ԙ',
-  'ԛ' => 'Ԛ',
-  'ԝ' => 'Ԝ',
-  'ԟ' => 'Ԟ',
-  'ԡ' => 'Ԡ',
-  'ԣ' => 'Ԣ',
-  'ԥ' => 'Ԥ',
-  'ԧ' => 'Ԧ',
-  'ԩ' => 'Ԩ',
-  'ԫ' => 'Ԫ',
-  'ԭ' => 'Ԭ',
-  'ԯ' => 'Ԯ',
-  'ա' => 'Ա',
-  'բ' => 'Բ',
-  'գ' => 'Գ',
-  'դ' => 'Դ',
-  'ե' => 'Ե',
-  'զ' => 'Զ',
-  'է' => 'Է',
-  'ը' => 'Ը',
-  'թ' => 'Թ',
-  'ժ' => 'Ժ',
-  'ի' => 'Ի',
-  'լ' => 'Լ',
-  'խ' => 'Խ',
-  'ծ' => 'Ծ',
-  'կ' => 'Կ',
-  'հ' => 'Հ',
-  'ձ' => 'Ձ',
-  'ղ' => 'Ղ',
-  'ճ' => 'Ճ',
-  'մ' => 'Մ',
-  'յ' => 'Յ',
-  'ն' => 'Ն',
-  'շ' => 'Շ',
-  'ո' => 'Ո',
-  'չ' => 'Չ',
-  'պ' => 'Պ',
-  'ջ' => 'Ջ',
-  'ռ' => 'Ռ',
-  'ս' => 'Ս',
-  'վ' => 'Վ',
-  'տ' => 'Տ',
-  'ր' => 'Ր',
-  'ց' => 'Ց',
-  'ւ' => 'Ւ',
-  'փ' => 'Փ',
-  'ք' => 'Ք',
-  'օ' => 'Օ',
-  'ֆ' => 'Ֆ',
-  'ა' => 'Ა',
-  'ბ' => 'Ბ',
-  'გ' => 'Გ',
-  'დ' => 'Დ',
-  'ე' => 'Ე',
-  'ვ' => 'Ვ',
-  'ზ' => 'Ზ',
-  'თ' => 'Თ',
-  'ი' => 'Ი',
-  'კ' => 'Კ',
-  'ლ' => 'Ლ',
-  'მ' => 'Მ',
-  'ნ' => 'Ნ',
-  'ო' => 'Ო',
-  'პ' => 'Პ',
-  'ჟ' => 'Ჟ',
-  'რ' => 'Რ',
-  'ს' => 'Ს',
-  'ტ' => 'Ტ',
-  'უ' => 'Უ',
-  'ფ' => 'Ფ',
-  'ქ' => 'Ქ',
-  'ღ' => 'Ღ',
-  'ყ' => 'Ყ',
-  'შ' => 'Შ',
-  'ჩ' => 'Ჩ',
-  'ც' => 'Ც',
-  'ძ' => 'Ძ',
-  'წ' => 'Წ',
-  'ჭ' => 'Ჭ',
-  'ხ' => 'Ხ',
-  'ჯ' => 'Ჯ',
-  'ჰ' => 'Ჰ',
-  'ჱ' => 'Ჱ',
-  'ჲ' => 'Ჲ',
-  'ჳ' => 'Ჳ',
-  'ჴ' => 'Ჴ',
-  'ჵ' => 'Ჵ',
-  'ჶ' => 'Ჶ',
-  'ჷ' => 'Ჷ',
-  'ჸ' => 'Ჸ',
-  'ჹ' => 'Ჹ',
-  'ჺ' => 'Ჺ',
-  'ჽ' => 'Ჽ',
-  'ჾ' => 'Ჾ',
-  'ჿ' => 'Ჿ',
-  'ᏸ' => 'Ᏸ',
-  'ᏹ' => 'Ᏹ',
-  'ᏺ' => 'Ᏺ',
-  'ᏻ' => 'Ᏻ',
-  'ᏼ' => 'Ᏼ',
-  'ᏽ' => 'Ᏽ',
-  'ᲀ' => 'В',
-  'ᲁ' => 'Д',
-  'ᲂ' => 'О',
-  'ᲃ' => 'С',
-  'ᲄ' => 'Т',
-  'ᲅ' => 'Т',
-  'ᲆ' => 'Ъ',
-  'ᲇ' => 'Ѣ',
-  'ᲈ' => 'Ꙋ',
-  'ᵹ' => 'Ᵹ',
-  'ᵽ' => 'Ᵽ',
-  'ᶎ' => 'Ᶎ',
-  'ḁ' => 'Ḁ',
-  'ḃ' => 'Ḃ',
-  'ḅ' => 'Ḅ',
-  'ḇ' => 'Ḇ',
-  'ḉ' => 'Ḉ',
-  'ḋ' => 'Ḋ',
-  'ḍ' => 'Ḍ',
-  'ḏ' => 'Ḏ',
-  'ḑ' => 'Ḑ',
-  'ḓ' => 'Ḓ',
-  'ḕ' => 'Ḕ',
-  'ḗ' => 'Ḗ',
-  'ḙ' => 'Ḙ',
-  'ḛ' => 'Ḛ',
-  'ḝ' => 'Ḝ',
-  'ḟ' => 'Ḟ',
-  'ḡ' => 'Ḡ',
-  'ḣ' => 'Ḣ',
-  'ḥ' => 'Ḥ',
-  'ḧ' => 'Ḧ',
-  'ḩ' => 'Ḩ',
-  'ḫ' => 'Ḫ',
-  'ḭ' => 'Ḭ',
-  'ḯ' => 'Ḯ',
-  'ḱ' => 'Ḱ',
-  'ḳ' => 'Ḳ',
-  'ḵ' => 'Ḵ',
-  'ḷ' => 'Ḷ',
-  'ḹ' => 'Ḹ',
-  'ḻ' => 'Ḻ',
-  'ḽ' => 'Ḽ',
-  'ḿ' => 'Ḿ',
-  'ṁ' => 'Ṁ',
-  'ṃ' => 'Ṃ',
-  'ṅ' => 'Ṅ',
-  'ṇ' => 'Ṇ',
-  'ṉ' => 'Ṉ',
-  'ṋ' => 'Ṋ',
-  'ṍ' => 'Ṍ',
-  'ṏ' => 'Ṏ',
-  'ṑ' => 'Ṑ',
-  'ṓ' => 'Ṓ',
-  'ṕ' => 'Ṕ',
-  'ṗ' => 'Ṗ',
-  'ṙ' => 'Ṙ',
-  'ṛ' => 'Ṛ',
-  'ṝ' => 'Ṝ',
-  'ṟ' => 'Ṟ',
-  'ṡ' => 'Ṡ',
-  'ṣ' => 'Ṣ',
-  'ṥ' => 'Ṥ',
-  'ṧ' => 'Ṧ',
-  'ṩ' => 'Ṩ',
-  'ṫ' => 'Ṫ',
-  'ṭ' => 'Ṭ',
-  'ṯ' => 'Ṯ',
-  'ṱ' => 'Ṱ',
-  'ṳ' => 'Ṳ',
-  'ṵ' => 'Ṵ',
-  'ṷ' => 'Ṷ',
-  'ṹ' => 'Ṹ',
-  'ṻ' => 'Ṻ',
-  'ṽ' => 'Ṽ',
-  'ṿ' => 'Ṿ',
-  'ẁ' => 'Ẁ',
-  'ẃ' => 'Ẃ',
-  'ẅ' => 'Ẅ',
-  'ẇ' => 'Ẇ',
-  'ẉ' => 'Ẉ',
-  'ẋ' => 'Ẋ',
-  'ẍ' => 'Ẍ',
-  'ẏ' => 'Ẏ',
-  'ẑ' => 'Ẑ',
-  'ẓ' => 'Ẓ',
-  'ẕ' => 'Ẕ',
-  'ẛ' => 'Ṡ',
-  'ạ' => 'Ạ',
-  'ả' => 'Ả',
-  'ấ' => 'Ấ',
-  'ầ' => 'Ầ',
-  'ẩ' => 'Ẩ',
-  'ẫ' => 'Ẫ',
-  'ậ' => 'Ậ',
-  'ắ' => 'Ắ',
-  'ằ' => 'Ằ',
-  'ẳ' => 'Ẳ',
-  'ẵ' => 'Ẵ',
-  'ặ' => 'Ặ',
-  'ẹ' => 'Ẹ',
-  'ẻ' => 'Ẻ',
-  'ẽ' => 'Ẽ',
-  'ế' => 'Ế',
-  'ề' => 'Ề',
-  'ể' => 'Ể',
-  'ễ' => 'Ễ',
-  'ệ' => 'Ệ',
-  'ỉ' => 'Ỉ',
-  'ị' => 'Ị',
-  'ọ' => 'Ọ',
-  'ỏ' => 'Ỏ',
-  'ố' => 'Ố',
-  'ồ' => 'Ồ',
-  'ổ' => 'Ổ',
-  'ỗ' => 'Ỗ',
-  'ộ' => 'Ộ',
-  'ớ' => 'Ớ',
-  'ờ' => 'Ờ',
-  'ở' => 'Ở',
-  'ỡ' => 'Ỡ',
-  'ợ' => 'Ợ',
-  'ụ' => 'Ụ',
-  'ủ' => 'Ủ',
-  'ứ' => 'Ứ',
-  'ừ' => 'Ừ',
-  'ử' => 'Ử',
-  'ữ' => 'Ữ',
-  'ự' => 'Ự',
-  'ỳ' => 'Ỳ',
-  'ỵ' => 'Ỵ',
-  'ỷ' => 'Ỷ',
-  'ỹ' => 'Ỹ',
-  'ỻ' => 'Ỻ',
-  'ỽ' => 'Ỽ',
-  'ỿ' => 'Ỿ',
-  'ἀ' => 'Ἀ',
-  'ἁ' => 'Ἁ',
-  'ἂ' => 'Ἂ',
-  'ἃ' => 'Ἃ',
-  'ἄ' => 'Ἄ',
-  'ἅ' => 'Ἅ',
-  'ἆ' => 'Ἆ',
-  'ἇ' => 'Ἇ',
-  'ἐ' => 'Ἐ',
-  'ἑ' => 'Ἑ',
-  'ἒ' => 'Ἒ',
-  'ἓ' => 'Ἓ',
-  'ἔ' => 'Ἔ',
-  'ἕ' => 'Ἕ',
-  'ἠ' => 'Ἠ',
-  'ἡ' => 'Ἡ',
-  'ἢ' => 'Ἢ',
-  'ἣ' => 'Ἣ',
-  'ἤ' => 'Ἤ',
-  'ἥ' => 'Ἥ',
-  'ἦ' => 'Ἦ',
-  'ἧ' => 'Ἧ',
-  'ἰ' => 'Ἰ',
-  'ἱ' => 'Ἱ',
-  'ἲ' => 'Ἲ',
-  'ἳ' => 'Ἳ',
-  'ἴ' => 'Ἴ',
-  'ἵ' => 'Ἵ',
-  'ἶ' => 'Ἶ',
-  'ἷ' => 'Ἷ',
-  'ὀ' => 'Ὀ',
-  'ὁ' => 'Ὁ',
-  'ὂ' => 'Ὂ',
-  'ὃ' => 'Ὃ',
-  'ὄ' => 'Ὄ',
-  'ὅ' => 'Ὅ',
-  'ὑ' => 'Ὑ',
-  'ὓ' => 'Ὓ',
-  'ὕ' => 'Ὕ',
-  'ὗ' => 'Ὗ',
-  'ὠ' => 'Ὠ',
-  'ὡ' => 'Ὡ',
-  'ὢ' => 'Ὢ',
-  'ὣ' => 'Ὣ',
-  'ὤ' => 'Ὤ',
-  'ὥ' => 'Ὥ',
-  'ὦ' => 'Ὦ',
-  'ὧ' => 'Ὧ',
-  'ὰ' => 'Ὰ',
-  'ά' => 'Ά',
-  'ὲ' => 'Ὲ',
-  'έ' => 'Έ',
-  'ὴ' => 'Ὴ',
-  'ή' => 'Ή',
-  'ὶ' => 'Ὶ',
-  'ί' => 'Ί',
-  'ὸ' => 'Ὸ',
-  'ό' => 'Ό',
-  'ὺ' => 'Ὺ',
-  'ύ' => 'Ύ',
-  'ὼ' => 'Ὼ',
-  'ώ' => 'Ώ',
-  'ᾀ' => 'ἈΙ',
-  'ᾁ' => 'ἉΙ',
-  'ᾂ' => 'ἊΙ',
-  'ᾃ' => 'ἋΙ',
-  'ᾄ' => 'ἌΙ',
-  'ᾅ' => 'ἍΙ',
-  'ᾆ' => 'ἎΙ',
-  'ᾇ' => 'ἏΙ',
-  'ᾐ' => 'ἨΙ',
-  'ᾑ' => 'ἩΙ',
-  'ᾒ' => 'ἪΙ',
-  'ᾓ' => 'ἫΙ',
-  'ᾔ' => 'ἬΙ',
-  'ᾕ' => 'ἭΙ',
-  'ᾖ' => 'ἮΙ',
-  'ᾗ' => 'ἯΙ',
-  'ᾠ' => 'ὨΙ',
-  'ᾡ' => 'ὩΙ',
-  'ᾢ' => 'ὪΙ',
-  'ᾣ' => 'ὫΙ',
-  'ᾤ' => 'ὬΙ',
-  'ᾥ' => 'ὭΙ',
-  'ᾦ' => 'ὮΙ',
-  'ᾧ' => 'ὯΙ',
-  'ᾰ' => 'Ᾰ',
-  'ᾱ' => 'Ᾱ',
-  'ᾳ' => 'ΑΙ',
-  'ι' => 'Ι',
-  'ῃ' => 'ΗΙ',
-  'ῐ' => 'Ῐ',
-  'ῑ' => 'Ῑ',
-  'ῠ' => 'Ῠ',
-  'ῡ' => 'Ῡ',
-  'ῥ' => 'Ῥ',
-  'ῳ' => 'ΩΙ',
-  'ⅎ' => 'Ⅎ',
-  'ⅰ' => 'Ⅰ',
-  'ⅱ' => 'Ⅱ',
-  'ⅲ' => 'Ⅲ',
-  'ⅳ' => 'Ⅳ',
-  'ⅴ' => 'Ⅴ',
-  'ⅵ' => 'Ⅵ',
-  'ⅶ' => 'Ⅶ',
-  'ⅷ' => 'Ⅷ',
-  'ⅸ' => 'Ⅸ',
-  'ⅹ' => 'Ⅹ',
-  'ⅺ' => 'Ⅺ',
-  'ⅻ' => 'Ⅻ',
-  'ⅼ' => 'Ⅼ',
-  'ⅽ' => 'Ⅽ',
-  'ⅾ' => 'Ⅾ',
-  'ⅿ' => 'Ⅿ',
-  'ↄ' => 'Ↄ',
-  'ⓐ' => 'Ⓐ',
-  'ⓑ' => 'Ⓑ',
-  'ⓒ' => 'Ⓒ',
-  'ⓓ' => 'Ⓓ',
-  'ⓔ' => 'Ⓔ',
-  'ⓕ' => 'Ⓕ',
-  'ⓖ' => 'Ⓖ',
-  'ⓗ' => 'Ⓗ',
-  'ⓘ' => 'Ⓘ',
-  'ⓙ' => 'Ⓙ',
-  'ⓚ' => 'Ⓚ',
-  'ⓛ' => 'Ⓛ',
-  'ⓜ' => 'Ⓜ',
-  'ⓝ' => 'Ⓝ',
-  'ⓞ' => 'Ⓞ',
-  'ⓟ' => 'Ⓟ',
-  'ⓠ' => 'Ⓠ',
-  'ⓡ' => 'Ⓡ',
-  'ⓢ' => 'Ⓢ',
-  'ⓣ' => 'Ⓣ',
-  'ⓤ' => 'Ⓤ',
-  'ⓥ' => 'Ⓥ',
-  'ⓦ' => 'Ⓦ',
-  'ⓧ' => 'Ⓧ',
-  'ⓨ' => 'Ⓨ',
-  'ⓩ' => 'Ⓩ',
-  'ⰰ' => 'Ⰰ',
-  'ⰱ' => 'Ⰱ',
-  'ⰲ' => 'Ⰲ',
-  'ⰳ' => 'Ⰳ',
-  'ⰴ' => 'Ⰴ',
-  'ⰵ' => 'Ⰵ',
-  'ⰶ' => 'Ⰶ',
-  'ⰷ' => 'Ⰷ',
-  'ⰸ' => 'Ⰸ',
-  'ⰹ' => 'Ⰹ',
-  'ⰺ' => 'Ⰺ',
-  'ⰻ' => 'Ⰻ',
-  'ⰼ' => 'Ⰼ',
-  'ⰽ' => 'Ⰽ',
-  'ⰾ' => 'Ⰾ',
-  'ⰿ' => 'Ⰿ',
-  'ⱀ' => 'Ⱀ',
-  'ⱁ' => 'Ⱁ',
-  'ⱂ' => 'Ⱂ',
-  'ⱃ' => 'Ⱃ',
-  'ⱄ' => 'Ⱄ',
-  'ⱅ' => 'Ⱅ',
-  'ⱆ' => 'Ⱆ',
-  'ⱇ' => 'Ⱇ',
-  'ⱈ' => 'Ⱈ',
-  'ⱉ' => 'Ⱉ',
-  'ⱊ' => 'Ⱊ',
-  'ⱋ' => 'Ⱋ',
-  'ⱌ' => 'Ⱌ',
-  'ⱍ' => 'Ⱍ',
-  'ⱎ' => 'Ⱎ',
-  'ⱏ' => 'Ⱏ',
-  'ⱐ' => 'Ⱐ',
-  'ⱑ' => 'Ⱑ',
-  'ⱒ' => 'Ⱒ',
-  'ⱓ' => 'Ⱓ',
-  'ⱔ' => 'Ⱔ',
-  'ⱕ' => 'Ⱕ',
-  'ⱖ' => 'Ⱖ',
-  'ⱗ' => 'Ⱗ',
-  'ⱘ' => 'Ⱘ',
-  'ⱙ' => 'Ⱙ',
-  'ⱚ' => 'Ⱚ',
-  'ⱛ' => 'Ⱛ',
-  'ⱜ' => 'Ⱜ',
-  'ⱝ' => 'Ⱝ',
-  'ⱞ' => 'Ⱞ',
-  'ⱡ' => 'Ⱡ',
-  'ⱥ' => 'Ⱥ',
-  'ⱦ' => 'Ⱦ',
-  'ⱨ' => 'Ⱨ',
-  'ⱪ' => 'Ⱪ',
-  'ⱬ' => 'Ⱬ',
-  'ⱳ' => 'Ⱳ',
-  'ⱶ' => 'Ⱶ',
-  'ⲁ' => 'Ⲁ',
-  'ⲃ' => 'Ⲃ',
-  'ⲅ' => 'Ⲅ',
-  'ⲇ' => 'Ⲇ',
-  'ⲉ' => 'Ⲉ',
-  'ⲋ' => 'Ⲋ',
-  'ⲍ' => 'Ⲍ',
-  'ⲏ' => 'Ⲏ',
-  'ⲑ' => 'Ⲑ',
-  'ⲓ' => 'Ⲓ',
-  'ⲕ' => 'Ⲕ',
-  'ⲗ' => 'Ⲗ',
-  'ⲙ' => 'Ⲙ',
-  'ⲛ' => 'Ⲛ',
-  'ⲝ' => 'Ⲝ',
-  'ⲟ' => 'Ⲟ',
-  'ⲡ' => 'Ⲡ',
-  'ⲣ' => 'Ⲣ',
-  'ⲥ' => 'Ⲥ',
-  'ⲧ' => 'Ⲧ',
-  'ⲩ' => 'Ⲩ',
-  'ⲫ' => 'Ⲫ',
-  'ⲭ' => 'Ⲭ',
-  'ⲯ' => 'Ⲯ',
-  'ⲱ' => 'Ⲱ',
-  'ⲳ' => 'Ⲳ',
-  'ⲵ' => 'Ⲵ',
-  'ⲷ' => 'Ⲷ',
-  'ⲹ' => 'Ⲹ',
-  'ⲻ' => 'Ⲻ',
-  'ⲽ' => 'Ⲽ',
-  'ⲿ' => 'Ⲿ',
-  'ⳁ' => 'Ⳁ',
-  'ⳃ' => 'Ⳃ',
-  'ⳅ' => 'Ⳅ',
-  'ⳇ' => 'Ⳇ',
-  'ⳉ' => 'Ⳉ',
-  'ⳋ' => 'Ⳋ',
-  'ⳍ' => 'Ⳍ',
-  'ⳏ' => 'Ⳏ',
-  'ⳑ' => 'Ⳑ',
-  'ⳓ' => 'Ⳓ',
-  'ⳕ' => 'Ⳕ',
-  'ⳗ' => 'Ⳗ',
-  'ⳙ' => 'Ⳙ',
-  'ⳛ' => 'Ⳛ',
-  'ⳝ' => 'Ⳝ',
-  'ⳟ' => 'Ⳟ',
-  'ⳡ' => 'Ⳡ',
-  'ⳣ' => 'Ⳣ',
-  'ⳬ' => 'Ⳬ',
-  'ⳮ' => 'Ⳮ',
-  'ⳳ' => 'Ⳳ',
-  'ⴀ' => 'Ⴀ',
-  'ⴁ' => 'Ⴁ',
-  'ⴂ' => 'Ⴂ',
-  'ⴃ' => 'Ⴃ',
-  'ⴄ' => 'Ⴄ',
-  'ⴅ' => 'Ⴅ',
-  'ⴆ' => 'Ⴆ',
-  'ⴇ' => 'Ⴇ',
-  'ⴈ' => 'Ⴈ',
-  'ⴉ' => 'Ⴉ',
-  'ⴊ' => 'Ⴊ',
-  'ⴋ' => 'Ⴋ',
-  'ⴌ' => 'Ⴌ',
-  'ⴍ' => 'Ⴍ',
-  'ⴎ' => 'Ⴎ',
-  'ⴏ' => 'Ⴏ',
-  'ⴐ' => 'Ⴐ',
-  'ⴑ' => 'Ⴑ',
-  'ⴒ' => 'Ⴒ',
-  'ⴓ' => 'Ⴓ',
-  'ⴔ' => 'Ⴔ',
-  'ⴕ' => 'Ⴕ',
-  'ⴖ' => 'Ⴖ',
-  'ⴗ' => 'Ⴗ',
-  'ⴘ' => 'Ⴘ',
-  'ⴙ' => 'Ⴙ',
-  'ⴚ' => 'Ⴚ',
-  'ⴛ' => 'Ⴛ',
-  'ⴜ' => 'Ⴜ',
-  'ⴝ' => 'Ⴝ',
-  'ⴞ' => 'Ⴞ',
-  'ⴟ' => 'Ⴟ',
-  'ⴠ' => 'Ⴠ',
-  'ⴡ' => 'Ⴡ',
-  'ⴢ' => 'Ⴢ',
-  'ⴣ' => 'Ⴣ',
-  'ⴤ' => 'Ⴤ',
-  'ⴥ' => 'Ⴥ',
-  'ⴧ' => 'Ⴧ',
-  'ⴭ' => 'Ⴭ',
-  'ꙁ' => 'Ꙁ',
-  'ꙃ' => 'Ꙃ',
-  'ꙅ' => 'Ꙅ',
-  'ꙇ' => 'Ꙇ',
-  'ꙉ' => 'Ꙉ',
-  'ꙋ' => 'Ꙋ',
-  'ꙍ' => 'Ꙍ',
-  'ꙏ' => 'Ꙏ',
-  'ꙑ' => 'Ꙑ',
-  'ꙓ' => 'Ꙓ',
-  'ꙕ' => 'Ꙕ',
-  'ꙗ' => 'Ꙗ',
-  'ꙙ' => 'Ꙙ',
-  'ꙛ' => 'Ꙛ',
-  'ꙝ' => 'Ꙝ',
-  'ꙟ' => 'Ꙟ',
-  'ꙡ' => 'Ꙡ',
-  'ꙣ' => 'Ꙣ',
-  'ꙥ' => 'Ꙥ',
-  'ꙧ' => 'Ꙧ',
-  'ꙩ' => 'Ꙩ',
-  'ꙫ' => 'Ꙫ',
-  'ꙭ' => 'Ꙭ',
-  'ꚁ' => 'Ꚁ',
-  'ꚃ' => 'Ꚃ',
-  'ꚅ' => 'Ꚅ',
-  'ꚇ' => 'Ꚇ',
-  'ꚉ' => 'Ꚉ',
-  'ꚋ' => 'Ꚋ',
-  'ꚍ' => 'Ꚍ',
-  'ꚏ' => 'Ꚏ',
-  'ꚑ' => 'Ꚑ',
-  'ꚓ' => 'Ꚓ',
-  'ꚕ' => 'Ꚕ',
-  'ꚗ' => 'Ꚗ',
-  'ꚙ' => 'Ꚙ',
-  'ꚛ' => 'Ꚛ',
-  'ꜣ' => 'Ꜣ',
-  'ꜥ' => 'Ꜥ',
-  'ꜧ' => 'Ꜧ',
-  'ꜩ' => 'Ꜩ',
-  'ꜫ' => 'Ꜫ',
-  'ꜭ' => 'Ꜭ',
-  'ꜯ' => 'Ꜯ',
-  'ꜳ' => 'Ꜳ',
-  'ꜵ' => 'Ꜵ',
-  'ꜷ' => 'Ꜷ',
-  'ꜹ' => 'Ꜹ',
-  'ꜻ' => 'Ꜻ',
-  'ꜽ' => 'Ꜽ',
-  'ꜿ' => 'Ꜿ',
-  'ꝁ' => 'Ꝁ',
-  'ꝃ' => 'Ꝃ',
-  'ꝅ' => 'Ꝅ',
-  'ꝇ' => 'Ꝇ',
-  'ꝉ' => 'Ꝉ',
-  'ꝋ' => 'Ꝋ',
-  'ꝍ' => 'Ꝍ',
-  'ꝏ' => 'Ꝏ',
-  'ꝑ' => 'Ꝑ',
-  'ꝓ' => 'Ꝓ',
-  'ꝕ' => 'Ꝕ',
-  'ꝗ' => 'Ꝗ',
-  'ꝙ' => 'Ꝙ',
-  'ꝛ' => 'Ꝛ',
-  'ꝝ' => 'Ꝝ',
-  'ꝟ' => 'Ꝟ',
-  'ꝡ' => 'Ꝡ',
-  'ꝣ' => 'Ꝣ',
-  'ꝥ' => 'Ꝥ',
-  'ꝧ' => 'Ꝧ',
-  'ꝩ' => 'Ꝩ',
-  'ꝫ' => 'Ꝫ',
-  'ꝭ' => 'Ꝭ',
-  'ꝯ' => 'Ꝯ',
-  'ꝺ' => 'Ꝺ',
-  'ꝼ' => 'Ꝼ',
-  'ꝿ' => 'Ꝿ',
-  'ꞁ' => 'Ꞁ',
-  'ꞃ' => 'Ꞃ',
-  'ꞅ' => 'Ꞅ',
-  'ꞇ' => 'Ꞇ',
-  'ꞌ' => 'Ꞌ',
-  'ꞑ' => 'Ꞑ',
-  'ꞓ' => 'Ꞓ',
-  'ꞔ' => 'Ꞔ',
-  'ꞗ' => 'Ꞗ',
-  'ꞙ' => 'Ꞙ',
-  'ꞛ' => 'Ꞛ',
-  'ꞝ' => 'Ꞝ',
-  'ꞟ' => 'Ꞟ',
-  'ꞡ' => 'Ꞡ',
-  'ꞣ' => 'Ꞣ',
-  'ꞥ' => 'Ꞥ',
-  'ꞧ' => 'Ꞧ',
-  'ꞩ' => 'Ꞩ',
-  'ꞵ' => 'Ꞵ',
-  'ꞷ' => 'Ꞷ',
-  'ꞹ' => 'Ꞹ',
-  'ꞻ' => 'Ꞻ',
-  'ꞽ' => 'Ꞽ',
-  'ꞿ' => 'Ꞿ',
-  'ꟃ' => 'Ꟃ',
-  'ꟈ' => 'Ꟈ',
-  'ꟊ' => 'Ꟊ',
-  'ꟶ' => 'Ꟶ',
-  'ꭓ' => 'Ꭓ',
-  'ꭰ' => 'Ꭰ',
-  'ꭱ' => 'Ꭱ',
-  'ꭲ' => 'Ꭲ',
-  'ꭳ' => 'Ꭳ',
-  'ꭴ' => 'Ꭴ',
-  'ꭵ' => 'Ꭵ',
-  'ꭶ' => 'Ꭶ',
-  'ꭷ' => 'Ꭷ',
-  'ꭸ' => 'Ꭸ',
-  'ꭹ' => 'Ꭹ',
-  'ꭺ' => 'Ꭺ',
-  'ꭻ' => 'Ꭻ',
-  'ꭼ' => 'Ꭼ',
-  'ꭽ' => 'Ꭽ',
-  'ꭾ' => 'Ꭾ',
-  'ꭿ' => 'Ꭿ',
-  'ꮀ' => 'Ꮀ',
-  'ꮁ' => 'Ꮁ',
-  'ꮂ' => 'Ꮂ',
-  'ꮃ' => 'Ꮃ',
-  'ꮄ' => 'Ꮄ',
-  'ꮅ' => 'Ꮅ',
-  'ꮆ' => 'Ꮆ',
-  'ꮇ' => 'Ꮇ',
-  'ꮈ' => 'Ꮈ',
-  'ꮉ' => 'Ꮉ',
-  'ꮊ' => 'Ꮊ',
-  'ꮋ' => 'Ꮋ',
-  'ꮌ' => 'Ꮌ',
-  'ꮍ' => 'Ꮍ',
-  'ꮎ' => 'Ꮎ',
-  'ꮏ' => 'Ꮏ',
-  'ꮐ' => 'Ꮐ',
-  'ꮑ' => 'Ꮑ',
-  'ꮒ' => 'Ꮒ',
-  'ꮓ' => 'Ꮓ',
-  'ꮔ' => 'Ꮔ',
-  'ꮕ' => 'Ꮕ',
-  'ꮖ' => 'Ꮖ',
-  'ꮗ' => 'Ꮗ',
-  'ꮘ' => 'Ꮘ',
-  'ꮙ' => 'Ꮙ',
-  'ꮚ' => 'Ꮚ',
-  'ꮛ' => 'Ꮛ',
-  'ꮜ' => 'Ꮜ',
-  'ꮝ' => 'Ꮝ',
-  'ꮞ' => 'Ꮞ',
-  'ꮟ' => 'Ꮟ',
-  'ꮠ' => 'Ꮠ',
-  'ꮡ' => 'Ꮡ',
-  'ꮢ' => 'Ꮢ',
-  'ꮣ' => 'Ꮣ',
-  'ꮤ' => 'Ꮤ',
-  'ꮥ' => 'Ꮥ',
-  'ꮦ' => 'Ꮦ',
-  'ꮧ' => 'Ꮧ',
-  'ꮨ' => 'Ꮨ',
-  'ꮩ' => 'Ꮩ',
-  'ꮪ' => 'Ꮪ',
-  'ꮫ' => 'Ꮫ',
-  'ꮬ' => 'Ꮬ',
-  'ꮭ' => 'Ꮭ',
-  'ꮮ' => 'Ꮮ',
-  'ꮯ' => 'Ꮯ',
-  'ꮰ' => 'Ꮰ',
-  'ꮱ' => 'Ꮱ',
-  'ꮲ' => 'Ꮲ',
-  'ꮳ' => 'Ꮳ',
-  'ꮴ' => 'Ꮴ',
-  'ꮵ' => 'Ꮵ',
-  'ꮶ' => 'Ꮶ',
-  'ꮷ' => 'Ꮷ',
-  'ꮸ' => 'Ꮸ',
-  'ꮹ' => 'Ꮹ',
-  'ꮺ' => 'Ꮺ',
-  'ꮻ' => 'Ꮻ',
-  'ꮼ' => 'Ꮼ',
-  'ꮽ' => 'Ꮽ',
-  'ꮾ' => 'Ꮾ',
-  'ꮿ' => 'Ꮿ',
-  'a' => 'A',
-  'b' => 'B',
-  'c' => 'C',
-  'd' => 'D',
-  'e' => 'E',
-  'f' => 'F',
-  'g' => 'G',
-  'h' => 'H',
-  'i' => 'I',
-  'j' => 'J',
-  'k' => 'K',
-  'l' => 'L',
-  'm' => 'M',
-  'n' => 'N',
-  'o' => 'O',
-  'p' => 'P',
-  'q' => 'Q',
-  'r' => 'R',
-  's' => 'S',
-  't' => 'T',
-  'u' => 'U',
-  'v' => 'V',
-  'w' => 'W',
-  'x' => 'X',
-  'y' => 'Y',
-  'z' => 'Z',
-  '𐐨' => '𐐀',
-  '𐐩' => '𐐁',
-  '𐐪' => '𐐂',
-  '𐐫' => '𐐃',
-  '𐐬' => '𐐄',
-  '𐐭' => '𐐅',
-  '𐐮' => '𐐆',
-  '𐐯' => '𐐇',
-  '𐐰' => '𐐈',
-  '𐐱' => '𐐉',
-  '𐐲' => '𐐊',
-  '𐐳' => '𐐋',
-  '𐐴' => '𐐌',
-  '𐐵' => '𐐍',
-  '𐐶' => '𐐎',
-  '𐐷' => '𐐏',
-  '𐐸' => '𐐐',
-  '𐐹' => '𐐑',
-  '𐐺' => '𐐒',
-  '𐐻' => '𐐓',
-  '𐐼' => '𐐔',
-  '𐐽' => '𐐕',
-  '𐐾' => '𐐖',
-  '𐐿' => '𐐗',
-  '𐑀' => '𐐘',
-  '𐑁' => '𐐙',
-  '𐑂' => '𐐚',
-  '𐑃' => '𐐛',
-  '𐑄' => '𐐜',
-  '𐑅' => '𐐝',
-  '𐑆' => '𐐞',
-  '𐑇' => '𐐟',
-  '𐑈' => '𐐠',
-  '𐑉' => '𐐡',
-  '𐑊' => '𐐢',
-  '𐑋' => '𐐣',
-  '𐑌' => '𐐤',
-  '𐑍' => '𐐥',
-  '𐑎' => '𐐦',
-  '𐑏' => '𐐧',
-  '𐓘' => '𐒰',
-  '𐓙' => '𐒱',
-  '𐓚' => '𐒲',
-  '𐓛' => '𐒳',
-  '𐓜' => '𐒴',
-  '𐓝' => '𐒵',
-  '𐓞' => '𐒶',
-  '𐓟' => '𐒷',
-  '𐓠' => '𐒸',
-  '𐓡' => '𐒹',
-  '𐓢' => '𐒺',
-  '𐓣' => '𐒻',
-  '𐓤' => '𐒼',
-  '𐓥' => '𐒽',
-  '𐓦' => '𐒾',
-  '𐓧' => '𐒿',
-  '𐓨' => '𐓀',
-  '𐓩' => '𐓁',
-  '𐓪' => '𐓂',
-  '𐓫' => '𐓃',
-  '𐓬' => '𐓄',
-  '𐓭' => '𐓅',
-  '𐓮' => '𐓆',
-  '𐓯' => '𐓇',
-  '𐓰' => '𐓈',
-  '𐓱' => '𐓉',
-  '𐓲' => '𐓊',
-  '𐓳' => '𐓋',
-  '𐓴' => '𐓌',
-  '𐓵' => '𐓍',
-  '𐓶' => '𐓎',
-  '𐓷' => '𐓏',
-  '𐓸' => '𐓐',
-  '𐓹' => '𐓑',
-  '𐓺' => '𐓒',
-  '𐓻' => '𐓓',
-  '𐳀' => '𐲀',
-  '𐳁' => '𐲁',
-  '𐳂' => '𐲂',
-  '𐳃' => '𐲃',
-  '𐳄' => '𐲄',
-  '𐳅' => '𐲅',
-  '𐳆' => '𐲆',
-  '𐳇' => '𐲇',
-  '𐳈' => '𐲈',
-  '𐳉' => '𐲉',
-  '𐳊' => '𐲊',
-  '𐳋' => '𐲋',
-  '𐳌' => '𐲌',
-  '𐳍' => '𐲍',
-  '𐳎' => '𐲎',
-  '𐳏' => '𐲏',
-  '𐳐' => '𐲐',
-  '𐳑' => '𐲑',
-  '𐳒' => '𐲒',
-  '𐳓' => '𐲓',
-  '𐳔' => '𐲔',
-  '𐳕' => '𐲕',
-  '𐳖' => '𐲖',
-  '𐳗' => '𐲗',
-  '𐳘' => '𐲘',
-  '𐳙' => '𐲙',
-  '𐳚' => '𐲚',
-  '𐳛' => '𐲛',
-  '𐳜' => '𐲜',
-  '𐳝' => '𐲝',
-  '𐳞' => '𐲞',
-  '𐳟' => '𐲟',
-  '𐳠' => '𐲠',
-  '𐳡' => '𐲡',
-  '𐳢' => '𐲢',
-  '𐳣' => '𐲣',
-  '𐳤' => '𐲤',
-  '𐳥' => '𐲥',
-  '𐳦' => '𐲦',
-  '𐳧' => '𐲧',
-  '𐳨' => '𐲨',
-  '𐳩' => '𐲩',
-  '𐳪' => '𐲪',
-  '𐳫' => '𐲫',
-  '𐳬' => '𐲬',
-  '𐳭' => '𐲭',
-  '𐳮' => '𐲮',
-  '𐳯' => '𐲯',
-  '𐳰' => '𐲰',
-  '𐳱' => '𐲱',
-  '𐳲' => '𐲲',
-  '𑣀' => '𑢠',
-  '𑣁' => '𑢡',
-  '𑣂' => '𑢢',
-  '𑣃' => '𑢣',
-  '𑣄' => '𑢤',
-  '𑣅' => '𑢥',
-  '𑣆' => '𑢦',
-  '𑣇' => '𑢧',
-  '𑣈' => '𑢨',
-  '𑣉' => '𑢩',
-  '𑣊' => '𑢪',
-  '𑣋' => '𑢫',
-  '𑣌' => '𑢬',
-  '𑣍' => '𑢭',
-  '𑣎' => '𑢮',
-  '𑣏' => '𑢯',
-  '𑣐' => '𑢰',
-  '𑣑' => '𑢱',
-  '𑣒' => '𑢲',
-  '𑣓' => '𑢳',
-  '𑣔' => '𑢴',
-  '𑣕' => '𑢵',
-  '𑣖' => '𑢶',
-  '𑣗' => '𑢷',
-  '𑣘' => '𑢸',
-  '𑣙' => '𑢹',
-  '𑣚' => '𑢺',
-  '𑣛' => '𑢻',
-  '𑣜' => '𑢼',
-  '𑣝' => '𑢽',
-  '𑣞' => '𑢾',
-  '𑣟' => '𑢿',
-  '𖹠' => '𖹀',
-  '𖹡' => '𖹁',
-  '𖹢' => '𖹂',
-  '𖹣' => '𖹃',
-  '𖹤' => '𖹄',
-  '𖹥' => '𖹅',
-  '𖹦' => '𖹆',
-  '𖹧' => '𖹇',
-  '𖹨' => '𖹈',
-  '𖹩' => '𖹉',
-  '𖹪' => '𖹊',
-  '𖹫' => '𖹋',
-  '𖹬' => '𖹌',
-  '𖹭' => '𖹍',
-  '𖹮' => '𖹎',
-  '𖹯' => '𖹏',
-  '𖹰' => '𖹐',
-  '𖹱' => '𖹑',
-  '𖹲' => '𖹒',
-  '𖹳' => '𖹓',
-  '𖹴' => '𖹔',
-  '𖹵' => '𖹕',
-  '𖹶' => '𖹖',
-  '𖹷' => '𖹗',
-  '𖹸' => '𖹘',
-  '𖹹' => '𖹙',
-  '𖹺' => '𖹚',
-  '𖹻' => '𖹛',
-  '𖹼' => '𖹜',
-  '𖹽' => '𖹝',
-  '𖹾' => '𖹞',
-  '𖹿' => '𖹟',
-  '𞤢' => '𞤀',
-  '𞤣' => '𞤁',
-  '𞤤' => '𞤂',
-  '𞤥' => '𞤃',
-  '𞤦' => '𞤄',
-  '𞤧' => '𞤅',
-  '𞤨' => '𞤆',
-  '𞤩' => '𞤇',
-  '𞤪' => '𞤈',
-  '𞤫' => '𞤉',
-  '𞤬' => '𞤊',
-  '𞤭' => '𞤋',
-  '𞤮' => '𞤌',
-  '𞤯' => '𞤍',
-  '𞤰' => '𞤎',
-  '𞤱' => '𞤏',
-  '𞤲' => '𞤐',
-  '𞤳' => '𞤑',
-  '𞤴' => '𞤒',
-  '𞤵' => '𞤓',
-  '𞤶' => '𞤔',
-  '𞤷' => '𞤕',
-  '𞤸' => '𞤖',
-  '𞤹' => '𞤗',
-  '𞤺' => '𞤘',
-  '𞤻' => '𞤙',
-  '𞤼' => '𞤚',
-  '𞤽' => '𞤛',
-  '𞤾' => '𞤜',
-  '𞤿' => '𞤝',
-  '𞥀' => '𞤞',
-  '𞥁' => '𞤟',
-  '𞥂' => '𞤠',
-  '𞥃' => '𞤡',
-  'ß' => 'SS',
-  'ff' => 'FF',
-  'fi' => 'FI',
-  'fl' => 'FL',
-  'ffi' => 'FFI',
-  'ffl' => 'FFL',
-  'ſt' => 'ST',
-  'st' => 'ST',
-  'և' => 'ԵՒ',
-  'ﬓ' => 'ՄՆ',
-  'ﬔ' => 'ՄԵ',
-  'ﬕ' => 'ՄԻ',
-  'ﬖ' => 'ՎՆ',
-  'ﬗ' => 'ՄԽ',
-  'ʼn' => 'ʼN',
-  'ΐ' => 'Ϊ́',
-  'ΰ' => 'Ϋ́',
-  'ǰ' => 'J̌',
-  'ẖ' => 'H̱',
-  'ẗ' => 'T̈',
-  'ẘ' => 'W̊',
-  'ẙ' => 'Y̊',
-  'ẚ' => 'Aʾ',
-  'ὐ' => 'Υ̓',
-  'ὒ' => 'Υ̓̀',
-  'ὔ' => 'Υ̓́',
-  'ὖ' => 'Υ̓͂',
-  'ᾶ' => 'Α͂',
-  'ῆ' => 'Η͂',
-  'ῒ' => 'Ϊ̀',
-  'ΐ' => 'Ϊ́',
-  'ῖ' => 'Ι͂',
-  'ῗ' => 'Ϊ͂',
-  'ῢ' => 'Ϋ̀',
-  'ΰ' => 'Ϋ́',
-  'ῤ' => 'Ρ̓',
-  'ῦ' => 'Υ͂',
-  'ῧ' => 'Ϋ͂',
-  'ῶ' => 'Ω͂',
-  'ᾈ' => 'ἈΙ',
-  'ᾉ' => 'ἉΙ',
-  'ᾊ' => 'ἊΙ',
-  'ᾋ' => 'ἋΙ',
-  'ᾌ' => 'ἌΙ',
-  'ᾍ' => 'ἍΙ',
-  'ᾎ' => 'ἎΙ',
-  'ᾏ' => 'ἏΙ',
-  'ᾘ' => 'ἨΙ',
-  'ᾙ' => 'ἩΙ',
-  'ᾚ' => 'ἪΙ',
-  'ᾛ' => 'ἫΙ',
-  'ᾜ' => 'ἬΙ',
-  'ᾝ' => 'ἭΙ',
-  'ᾞ' => 'ἮΙ',
-  'ᾟ' => 'ἯΙ',
-  'ᾨ' => 'ὨΙ',
-  'ᾩ' => 'ὩΙ',
-  'ᾪ' => 'ὪΙ',
-  'ᾫ' => 'ὫΙ',
-  'ᾬ' => 'ὬΙ',
-  'ᾭ' => 'ὭΙ',
-  'ᾮ' => 'ὮΙ',
-  'ᾯ' => 'ὯΙ',
-  'ᾼ' => 'ΑΙ',
-  'ῌ' => 'ΗΙ',
-  'ῼ' => 'ΩΙ',
-  'ᾲ' => 'ᾺΙ',
-  'ᾴ' => 'ΆΙ',
-  'ῂ' => 'ῊΙ',
-  'ῄ' => 'ΉΙ',
-  'ῲ' => 'ῺΙ',
-  'ῴ' => 'ΏΙ',
-  'ᾷ' => 'Α͂Ι',
-  'ῇ' => 'Η͂Ι',
-  'ῷ' => 'Ω͂Ι',
-);
diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php
deleted file mode 100644
index 1fedd1f..0000000
--- a/vendor/symfony/polyfill-mbstring/bootstrap.php
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-use Symfony\Polyfill\Mbstring as p;
-
-if (\PHP_VERSION_ID >= 80000) {
-    return require __DIR__.'/bootstrap80.php';
-}
-
-if (!function_exists('mb_convert_encoding')) {
-    function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); }
-}
-if (!function_exists('mb_decode_mimeheader')) {
-    function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); }
-}
-if (!function_exists('mb_encode_mimeheader')) {
-    function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); }
-}
-if (!function_exists('mb_decode_numericentity')) {
-    function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); }
-}
-if (!function_exists('mb_encode_numericentity')) {
-    function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); }
-}
-if (!function_exists('mb_convert_case')) {
-    function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); }
-}
-if (!function_exists('mb_internal_encoding')) {
-    function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); }
-}
-if (!function_exists('mb_language')) {
-    function mb_language($language = null) { return p\Mbstring::mb_language($language); }
-}
-if (!function_exists('mb_list_encodings')) {
-    function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
-}
-if (!function_exists('mb_encoding_aliases')) {
-    function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
-}
-if (!function_exists('mb_check_encoding')) {
-    function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); }
-}
-if (!function_exists('mb_detect_encoding')) {
-    function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); }
-}
-if (!function_exists('mb_detect_order')) {
-    function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); }
-}
-if (!function_exists('mb_parse_str')) {
-    function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; }
-}
-if (!function_exists('mb_strlen')) {
-    function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); }
-}
-if (!function_exists('mb_strpos')) {
-    function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); }
-}
-if (!function_exists('mb_strtolower')) {
-    function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); }
-}
-if (!function_exists('mb_strtoupper')) {
-    function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); }
-}
-if (!function_exists('mb_substitute_character')) {
-    function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); }
-}
-if (!function_exists('mb_substr')) {
-    function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); }
-}
-if (!function_exists('mb_stripos')) {
-    function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); }
-}
-if (!function_exists('mb_stristr')) {
-    function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); }
-}
-if (!function_exists('mb_strrchr')) {
-    function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); }
-}
-if (!function_exists('mb_strrichr')) {
-    function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); }
-}
-if (!function_exists('mb_strripos')) {
-    function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); }
-}
-if (!function_exists('mb_strrpos')) {
-    function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); }
-}
-if (!function_exists('mb_strstr')) {
-    function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); }
-}
-if (!function_exists('mb_get_info')) {
-    function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
-}
-if (!function_exists('mb_http_output')) {
-    function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); }
-}
-if (!function_exists('mb_strwidth')) {
-    function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); }
-}
-if (!function_exists('mb_substr_count')) {
-    function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); }
-}
-if (!function_exists('mb_output_handler')) {
-    function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); }
-}
-if (!function_exists('mb_http_input')) {
-    function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); }
-}
-
-if (!function_exists('mb_convert_variables')) {
-    function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); }
-}
-
-if (!function_exists('mb_ord')) {
-    function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); }
-}
-if (!function_exists('mb_chr')) {
-    function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); }
-}
-if (!function_exists('mb_scrub')) {
-    function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); }
-}
-if (!function_exists('mb_str_split')) {
-    function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); }
-}
-
-if (extension_loaded('mbstring')) {
-    return;
-}
-
-if (!defined('MB_CASE_UPPER')) {
-    define('MB_CASE_UPPER', 0);
-}
-if (!defined('MB_CASE_LOWER')) {
-    define('MB_CASE_LOWER', 1);
-}
-if (!defined('MB_CASE_TITLE')) {
-    define('MB_CASE_TITLE', 2);
-}
diff --git a/vendor/symfony/polyfill-mbstring/bootstrap80.php b/vendor/symfony/polyfill-mbstring/bootstrap80.php
deleted file mode 100644
index 82f5ac4..0000000
--- a/vendor/symfony/polyfill-mbstring/bootstrap80.php
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-use Symfony\Polyfill\Mbstring as p;
-
-if (!function_exists('mb_convert_encoding')) {
-    function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); }
-}
-if (!function_exists('mb_decode_mimeheader')) {
-    function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); }
-}
-if (!function_exists('mb_encode_mimeheader')) {
-    function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); }
-}
-if (!function_exists('mb_decode_numericentity')) {
-    function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); }
-}
-if (!function_exists('mb_encode_numericentity')) {
-    function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); }
-}
-if (!function_exists('mb_convert_case')) {
-    function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); }
-}
-if (!function_exists('mb_internal_encoding')) {
-    function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); }
-}
-if (!function_exists('mb_language')) {
-    function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); }
-}
-if (!function_exists('mb_list_encodings')) {
-    function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); }
-}
-if (!function_exists('mb_encoding_aliases')) {
-    function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); }
-}
-if (!function_exists('mb_check_encoding')) {
-    function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); }
-}
-if (!function_exists('mb_detect_encoding')) {
-    function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); }
-}
-if (!function_exists('mb_detect_order')) {
-    function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); }
-}
-if (!function_exists('mb_parse_str')) {
-    function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; }
-}
-if (!function_exists('mb_strlen')) {
-    function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); }
-}
-if (!function_exists('mb_strpos')) {
-    function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
-}
-if (!function_exists('mb_strtolower')) {
-    function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); }
-}
-if (!function_exists('mb_strtoupper')) {
-    function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); }
-}
-if (!function_exists('mb_substitute_character')) {
-    function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); }
-}
-if (!function_exists('mb_substr')) {
-    function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); }
-}
-if (!function_exists('mb_stripos')) {
-    function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
-}
-if (!function_exists('mb_stristr')) {
-    function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
-}
-if (!function_exists('mb_strrchr')) {
-    function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
-}
-if (!function_exists('mb_strrichr')) {
-    function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
-}
-if (!function_exists('mb_strripos')) {
-    function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
-}
-if (!function_exists('mb_strrpos')) {
-    function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); }
-}
-if (!function_exists('mb_strstr')) {
-    function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); }
-}
-if (!function_exists('mb_get_info')) {
-    function mb_get_info(?string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info((string) $type); }
-}
-if (!function_exists('mb_http_output')) {
-    function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); }
-}
-if (!function_exists('mb_strwidth')) {
-    function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); }
-}
-if (!function_exists('mb_substr_count')) {
-    function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); }
-}
-if (!function_exists('mb_output_handler')) {
-    function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); }
-}
-if (!function_exists('mb_http_input')) {
-    function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); }
-}
-
-if (!function_exists('mb_convert_variables')) {
-    function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); }
-}
-
-if (!function_exists('mb_ord')) {
-    function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); }
-}
-if (!function_exists('mb_chr')) {
-    function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); }
-}
-if (!function_exists('mb_scrub')) {
-    function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); }
-}
-if (!function_exists('mb_str_split')) {
-    function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); }
-}
-
-if (extension_loaded('mbstring')) {
-    return;
-}
-
-if (!defined('MB_CASE_UPPER')) {
-    define('MB_CASE_UPPER', 0);
-}
-if (!defined('MB_CASE_LOWER')) {
-    define('MB_CASE_LOWER', 1);
-}
-if (!defined('MB_CASE_TITLE')) {
-    define('MB_CASE_TITLE', 2);
-}
diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json
deleted file mode 100644
index 4489553..0000000
--- a/vendor/symfony/polyfill-mbstring/composer.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-    "name": "symfony/polyfill-mbstring",
-    "type": "library",
-    "description": "Symfony polyfill for the Mbstring extension",
-    "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"],
-    "homepage": "https://symfony.com",
-    "license": "MIT",
-    "authors": [
-        {
-            "name": "Nicolas Grekas",
-            "email": "p@tchwork.com"
-        },
-        {
-            "name": "Symfony Community",
-            "homepage": "https://symfony.com/contributors"
-        }
-    ],
-    "require": {
-        "php": ">=7.1"
-    },
-    "provide": {
-        "ext-mbstring": "*"
-    },
-    "autoload": {
-        "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" },
-        "files": [ "bootstrap.php" ]
-    },
-    "suggest": {
-        "ext-mbstring": "For best performance"
-    },
-    "minimum-stability": "dev",
-    "extra": {
-        "branch-alias": {
-            "dev-main": "1.27-dev"
-        },
-        "thanks": {
-            "name": "symfony/polyfill",
-            "url": "https://github.com/symfony/polyfill"
-        }
-    }
-}