Skip to content

Commit c5a07da

Browse files
authored
Merge branch 'master' into Feature_Images-from-string-or-streams
2 parents c3bab5c + fac0e46 commit c5a07da

File tree

16 files changed

+732
-195
lines changed

16 files changed

+732
-195
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -312,70 +312,6 @@ parameters:
312312
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Address\\:\\:sheetName\\(\\) has no return type specified\\.$#"
313313
count: 1
314314
path: src/PhpSpreadsheet/Calculation/LookupRef/Address.php
315-
-
316-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchFirstValue\\(\\) has no return type specified\\.$#"
317-
count: 1
318-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
319-
-
320-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchFirstValue\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
321-
count: 1
322-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
323-
-
324-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchFirstValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
325-
count: 1
326-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
327-
-
328-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has no return type specified\\.$#"
329-
count: 1
330-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
331-
-
332-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has parameter \\$keySet with no type specified\\.$#"
333-
count: 1
334-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
335-
-
336-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
337-
count: 1
338-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
339-
-
340-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchLargestValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
341-
count: 1
342-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
343-
-
344-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchSmallestValue\\(\\) has no return type specified\\.$#"
345-
count: 1
346-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
347-
-
348-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchSmallestValue\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
349-
count: 1
350-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
351-
-
352-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:matchSmallestValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
353-
count: 1
354-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
355-
-
356-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:prepareLookupArray\\(\\) has no return type specified\\.$#"
357-
count: 1
358-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
359-
-
360-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:prepareLookupArray\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
361-
count: 1
362-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
363-
-
364-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:prepareLookupArray\\(\\) has parameter \\$matchType with no type specified\\.$#"
365-
count: 1
366-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
367-
-
368-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:validateLookupArray\\(\\) has parameter \\$lookupArray with no type specified\\.$#"
369-
count: 1
370-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
371-
-
372-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:validateLookupValue\\(\\) has parameter \\$lookupValue with no type specified\\.$#"
373-
count: 1
374-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
375-
-
376-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\ExcelMatch\\:\\:validateMatchType\\(\\) has parameter \\$matchType with no type specified\\.$#"
377-
count: 1
378-
path: src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php
379315
-
380316
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Lookup\\:\\:verifyResultVector\\(\\) has no return type specified\\.$#"
381317
count: 1

samples/Basic/27_Images_Xlsx.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
use PhpOffice\PhpSpreadsheet\IOFactory;
4+
5+
require __DIR__ . '/../Header.php';
6+
7+
// Read from Xlsx (.xls) template
8+
$helper->log('Load Xlsx template file');
9+
$reader = IOFactory::createReader('Xlsx');
10+
$spreadsheet = $reader->load(__DIR__ . '/../templates/27template.xlsx');
11+
12+
// Save
13+
$helper->write($spreadsheet, __FILE__);

samples/templates/27template.xlsx

261 KB
Binary file not shown.

src/PhpSpreadsheet/Calculation/LookupRef/ExcelMatch.php

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ public static function MATCH($lookupValue, $lookupArray, $matchType = self::MATC
3939
}
4040

4141
$lookupArray = Functions::flattenArray($lookupArray);
42-
$matchType = (int) ($matchType ?? self::MATCHTYPE_LARGEST_VALUE);
4342

4443
try {
4544
// Input validation
4645
self::validateLookupValue($lookupValue);
47-
self::validateMatchType($matchType);
46+
$matchType = self::validateMatchType($matchType);
4847
self::validateLookupArray($lookupArray);
4948

5049
$keySet = array_keys($lookupArray);
@@ -87,36 +86,78 @@ public static function MATCH($lookupValue, $lookupArray, $matchType = self::MATC
8786
return ExcelError::NA();
8887
}
8988

90-
private static function matchFirstValue($lookupArray, $lookupValue)
89+
/**
90+
* @param mixed $lookupValue
91+
*
92+
* @return mixed
93+
*/
94+
private static function matchFirstValue(array $lookupArray, $lookupValue)
9195
{
92-
$wildcardLookup = ((bool) preg_match('/([\?\*])/', $lookupValue));
93-
$wildcard = WildcardMatch::wildcard($lookupValue);
96+
if (is_string($lookupValue)) {
97+
$valueIsString = true;
98+
$wildcard = WildcardMatch::wildcard($lookupValue);
99+
} else {
100+
$valueIsString = false;
101+
$wildcard = '';
102+
}
94103

104+
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
95105
foreach ($lookupArray as $i => $lookupArrayValue) {
96-
$typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) ||
97-
(is_numeric($lookupValue) && is_numeric($lookupArrayValue)));
98-
99106
if (
100-
$typeMatch && is_string($lookupValue) &&
101-
$wildcardLookup && WildcardMatch::compare($lookupArrayValue, $wildcard)
107+
$valueIsString
108+
&& is_string($lookupArrayValue)
102109
) {
103-
// wildcard match
104-
return $i;
105-
} elseif ($lookupArrayValue === $lookupValue) {
106-
// exact match
107-
return $i;
110+
if (WildcardMatch::compare($lookupArrayValue, $wildcard)) {
111+
return $i; // wildcard match
112+
}
113+
} else {
114+
if ($lookupArrayValue === $lookupValue) {
115+
return $i; // exact match
116+
}
117+
if (
118+
$valueIsNumeric
119+
&& (is_float($lookupArrayValue) || is_int($lookupArrayValue))
120+
&& $lookupArrayValue == $lookupValue
121+
) {
122+
return $i; // exact match
123+
}
108124
}
109125
}
110126

111127
return null;
112128
}
113129

114-
private static function matchLargestValue($lookupArray, $lookupValue, $keySet)
130+
/**
131+
* @param mixed $lookupValue
132+
*
133+
* @return mixed
134+
*/
135+
private static function matchLargestValue(array $lookupArray, $lookupValue, array $keySet)
115136
{
137+
if (is_string($lookupValue)) {
138+
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
139+
$wildcard = WildcardMatch::wildcard($lookupValue);
140+
foreach (array_reverse($lookupArray) as $i => $lookupArrayValue) {
141+
if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
142+
return $i;
143+
}
144+
}
145+
} else {
146+
foreach ($lookupArray as $i => $lookupArrayValue) {
147+
if ($lookupArrayValue === $lookupValue) {
148+
return $keySet[$i];
149+
}
150+
}
151+
}
152+
}
153+
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
116154
foreach ($lookupArray as $i => $lookupArrayValue) {
117-
$typeMatch = ((gettype($lookupValue) === gettype($lookupArrayValue)) ||
118-
(is_numeric($lookupValue) && is_numeric($lookupArrayValue)));
119-
155+
if ($valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue))) {
156+
if ($lookupArrayValue <= $lookupValue) {
157+
return array_search($i, $keySet);
158+
}
159+
}
160+
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
120161
if ($typeMatch && ($lookupArrayValue <= $lookupValue)) {
121162
return array_search($i, $keySet);
122163
}
@@ -125,21 +166,42 @@ private static function matchLargestValue($lookupArray, $lookupValue, $keySet)
125166
return null;
126167
}
127168

128-
private static function matchSmallestValue($lookupArray, $lookupValue)
169+
/**
170+
* @param mixed $lookupValue
171+
*
172+
* @return mixed
173+
*/
174+
private static function matchSmallestValue(array $lookupArray, $lookupValue)
129175
{
130176
$valueKey = null;
177+
if (is_string($lookupValue)) {
178+
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
179+
$wildcard = WildcardMatch::wildcard($lookupValue);
180+
foreach ($lookupArray as $i => $lookupArrayValue) {
181+
if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
182+
return $i;
183+
}
184+
}
185+
}
186+
}
131187

188+
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
132189
// The basic algorithm is:
133190
// Iterate and keep the highest match until the next element is smaller than the searched value.
134191
// Return immediately if perfect match is found
135192
foreach ($lookupArray as $i => $lookupArrayValue) {
136193
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
194+
$bothNumeric = $valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue));
137195

138196
if ($lookupArrayValue === $lookupValue) {
139197
// Another "special" case. If a perfect match is found,
140198
// the algorithm gives up immediately
141199
return $i;
142-
} elseif ($typeMatch && $lookupArrayValue >= $lookupValue) {
200+
}
201+
if ($bothNumeric && $lookupValue == $lookupArrayValue) {
202+
return $i; // exact match, as above
203+
}
204+
if (($typeMatch || $bothNumeric) && $lookupArrayValue >= $lookupValue) {
143205
$valueKey = $i;
144206
} elseif ($typeMatch && $lookupArrayValue < $lookupValue) {
145207
//Excel algorithm gives up immediately if the first element is smaller than the searched value
@@ -150,6 +212,9 @@ private static function matchSmallestValue($lookupArray, $lookupValue)
150212
return $valueKey;
151213
}
152214

215+
/**
216+
* @param mixed $lookupValue
217+
*/
153218
private static function validateLookupValue($lookupValue): void
154219
{
155220
// Lookup_value type has to be number, text, or logical values
@@ -158,18 +223,29 @@ private static function validateLookupValue($lookupValue): void
158223
}
159224
}
160225

161-
private static function validateMatchType($matchType): void
226+
/**
227+
* @param mixed $matchType
228+
*/
229+
private static function validateMatchType($matchType): int
162230
{
163231
// Match_type is 0, 1 or -1
164-
if (
165-
($matchType !== self::MATCHTYPE_FIRST_VALUE) &&
166-
($matchType !== self::MATCHTYPE_LARGEST_VALUE) && ($matchType !== self::MATCHTYPE_SMALLEST_VALUE)
167-
) {
168-
throw new Exception(ExcelError::NA());
232+
// However Excel accepts other numeric values,
233+
// including numeric strings and floats.
234+
// It seems to just be interested in the sign.
235+
if (!is_numeric($matchType)) {
236+
throw new Exception(ExcelError::Value());
237+
}
238+
if ($matchType > 0) {
239+
return self::MATCHTYPE_LARGEST_VALUE;
169240
}
241+
if ($matchType < 0) {
242+
return self::MATCHTYPE_SMALLEST_VALUE;
243+
}
244+
245+
return self::MATCHTYPE_FIRST_VALUE;
170246
}
171247

172-
private static function validateLookupArray($lookupArray): void
248+
private static function validateLookupArray(array $lookupArray): void
173249
{
174250
// Lookup_array should not be empty
175251
$lookupArraySize = count($lookupArray);
@@ -178,7 +254,10 @@ private static function validateLookupArray($lookupArray): void
178254
}
179255
}
180256

181-
private static function prepareLookupArray($lookupArray, $matchType)
257+
/**
258+
* @param mixed $matchType
259+
*/
260+
private static function prepareLookupArray(array $lookupArray, $matchType): array
182261
{
183262
// Lookup_array should contain only number, text, or logical values, or empty (null) cells
184263
foreach ($lookupArray as $i => $value) {

0 commit comments

Comments
 (0)