feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles

- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD)
  * DEV: Clés TEST Pierre (mode test)
  * REC: Clés TEST Client (mode test)
  * PROD: Clés LIVE Client (mode live)
- Ajout de la gestion des bases de données immeubles/bâtiments
  * Configuration buildings_database pour DEV/REC/PROD
  * Service BuildingService pour enrichissement des adresses
- Optimisations pages et améliorations ergonomie
- Mises à jour des dépendances Composer
- Nettoyage des fichiers obsolètes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
pierre
2025-11-09 18:26:27 +01:00
parent 21657a3820
commit 2f5946a184
812 changed files with 142105 additions and 25992 deletions

View File

@@ -50,7 +50,7 @@ class AutoFilter implements Stringable
/**
* Create a new AutoFilter.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|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.
@@ -145,7 +145,7 @@ class AutoFilter implements Stringable
$this->evaluated = false;
if ($this->workSheet !== null) {
$thisrange = $this->range;
$range = (string) preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
$range = (string) preg_replace('/\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
if ($range !== $thisrange) {
$this->setRange($range);
}
@@ -295,7 +295,7 @@ class AutoFilter implements Stringable
$fromColumn = strtoupper($fromColumn);
$toColumn = strtoupper($toColumn);
if (($fromColumn !== null) && (isset($this->columns[$fromColumn])) && ($toColumn !== null)) {
if (isset($this->columns[$fromColumn])) {
$this->columns[$fromColumn]->setParent();
$this->columns[$fromColumn]->setColumnIndex($toColumn);
$this->columns[$toColumn] = $this->columns[$fromColumn];
@@ -317,7 +317,7 @@ class AutoFilter implements Stringable
{
$dataSetValues = $dataSet['filterValues'];
$blanks = $dataSet['blanks'];
if (($cellValue == '') || ($cellValue === null)) {
if (($cellValue === '') || ($cellValue === null)) {
return $blanks;
}
@@ -333,7 +333,7 @@ class AutoFilter implements Stringable
{
$dateSet = $dataSet['filterValues'];
$blanks = $dataSet['blanks'];
if (($cellValue == '') || ($cellValue === null)) {
if (($cellValue === '') || ($cellValue === null)) {
return $blanks;
}
$timeZone = new DateTimeZone('UTC');
@@ -368,28 +368,26 @@ class AutoFilter implements Stringable
/**
* Test if cell value is within a set of values defined by a ruleset.
*
* @param mixed[] $ruleSet
* @param mixed[][] $ruleSet
*/
protected static function filterTestInCustomDataSet(mixed $cellValue, array $ruleSet): bool
{
/** @var array[] $dataSet */
$dataSet = $ruleSet['filterRules'];
$join = $ruleSet['join'];
$customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
if (!$customRuleForBlanks) {
// Blank cells are always ignored, so return a FALSE
if (($cellValue == '') || ($cellValue === null)) {
if (($cellValue === '') || ($cellValue === null)) {
return false;
}
}
$returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND);
foreach ($dataSet as $rule) {
/** @var string $ruleValue */
/** @var string[] $rule */
$ruleValue = $rule['value'];
/** @var string $ruleOperator */
$ruleOperator = $rule['operator'];
/** @var string $cellValueString */
/** @var string */
$cellValueString = $cellValue ?? '';
$retVal = false;
@@ -424,8 +422,8 @@ class AutoFilter implements Stringable
}
} elseif ($ruleValue == '') {
$retVal = match ($ruleOperator) {
Rule::AUTOFILTER_COLUMN_RULE_EQUAL => ($cellValue == '') || ($cellValue === null),
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL => ($cellValue != '') && ($cellValue !== null),
Rule::AUTOFILTER_COLUMN_RULE_EQUAL => ($cellValue === '') || ($cellValue === null),
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL => ($cellValue != ''),
default => true,
};
} else {
@@ -486,7 +484,7 @@ class AutoFilter implements Stringable
protected static function filterTestInPeriodDateSet(mixed $cellValue, array $monthSet): bool
{
// Blank cells are always ignored, so return a FALSE
if (($cellValue == '') || ($cellValue === null)) {
if (($cellValue === '') || ($cellValue === null)) {
return false;
}
@@ -529,6 +527,7 @@ class AutoFilter implements Stringable
Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY => 'dynamicYesterday',
];
/** @return array{DateTime, DateTime} */
private static function dynamicLastMonth(): array
{
$maxval = new DateTime();
@@ -554,6 +553,7 @@ class AutoFilter implements Stringable
return $val;
}
/** @return array{DateTime, DateTime} */
private static function dynamicLastQuarter(): array
{
$maxval = self::firstDayOfQuarter();
@@ -563,6 +563,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicLastWeek(): array
{
$val = new DateTime();
@@ -576,6 +577,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicLastYear(): array
{
$val = new DateTime();
@@ -586,6 +588,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicNextMonth(): array
{
$val = new DateTime();
@@ -600,6 +603,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicNextQuarter(): array
{
$val = self::firstDayOfQuarter();
@@ -610,6 +614,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicNextWeek(): array
{
$val = new DateTime();
@@ -623,6 +628,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicNextYear(): array
{
$val = new DateTime();
@@ -633,6 +639,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicThisMonth(): array
{
$baseDate = new DateTime();
@@ -646,6 +653,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicThisQuarter(): array
{
$val = self::firstDayOfQuarter();
@@ -655,6 +663,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicThisWeek(): array
{
$val = new DateTime();
@@ -668,6 +677,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicThisYear(): array
{
$val = new DateTime();
@@ -678,6 +688,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicToday(): array
{
$val = new DateTime();
@@ -688,6 +699,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicTomorrow(): array
{
$val = new DateTime();
@@ -699,6 +711,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicYearToDate(): array
{
$maxval = new DateTime();
@@ -709,6 +722,7 @@ class AutoFilter implements Stringable
return [$val, $maxval];
}
/** @return array{DateTime, DateTime} */
private static function dynamicYesterday(): array
{
$maxval = new DateTime();
@@ -732,7 +746,7 @@ class AutoFilter implements Stringable
// Val is lowest permitted value.
// Maxval is greater than highest permitted value
$val = $maxval = 0;
if (is_callable($callBack)) {
if (is_callable($callBack)) { //* @phpstan-ignore-line
[$val, $maxval] = $callBack();
}
$val = Date::dateTimeToExcel($val);
@@ -941,6 +955,7 @@ class AutoFilter implements Stringable
if ($periodType == 'M') {
$ruleValues = [$period];
} else {
/** @var int $period */
--$period;
$periodEnd = (1 + $period) * 3;
$periodStart = 1 + $period * 3;
@@ -1071,7 +1086,7 @@ class AutoFilter implements Stringable
// The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
$this->{$key} = [];
foreach ($value as $k => $v) {
$this->{$key}[$k] = clone $v;
$this->{$key}[$k] = clone $v; //* @phpstan-ignore-line
// attach the new cloned Column to this new cloned Autofilter object
$this->{$key}[$k]->setParent($this);
}

View File

@@ -360,7 +360,7 @@ class Column
public function __clone()
{
$vars = get_object_vars($this);
/** @var AutoFilter\Column\Rule[] $value */
/** @var Column\Rule[] $value */
foreach ($vars as $key => $value) {
if ($key === 'parent') {
// Detach from autofilter parent

View File

@@ -330,8 +330,7 @@ class Rule
{
$this->setEvaluatedFalse();
if (
($grouping !== null)
&& (!in_array($grouping, self::DATE_TIME_GROUPS))
(!in_array($grouping, self::DATE_TIME_GROUPS))
&& (!in_array($grouping, self::DYNAMIC_TYPES))
&& (!in_array($grouping, self::TOP_TEN_TYPE))
) {

View File

@@ -15,18 +15,16 @@ class AutoFit
$this->worksheet = $worksheet;
}
/** @return mixed[] */
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);
}
$autoFilterIndentRanges[] = $this->getAutoFilterIndentRange($autoFilter);
}
}

View File

@@ -131,6 +131,12 @@ class BaseDrawing implements IComparable
/** @var null|SimpleXMLElement|string[] */
protected $srcRect = [];
/**
* Percentage multiplied by 100,000, e.g. 40% = 40,000.
* Opacity=x is the same as transparency=100000-x.
*/
protected ?int $opacity = null;
/**
* Create a new BaseDrawing.
*/
@@ -352,9 +358,12 @@ class BaseDrawing implements IComparable
*/
public function setWidthAndHeight(int $width, int $height): self
{
$xratio = $width / ($this->width != 0 ? $this->width : 1);
$yratio = $height / ($this->height != 0 ? $this->height : 1);
if ($this->resizeProportional && !($width == 0 || $height == 0)) {
if ($this->width === 0 || $this->height === 0 || $width === 0 || $height === 0 || !$this->resizeProportional) {
$this->width = $width;
$this->height = $height;
} else {
$xratio = $width / $this->width;
$yratio = $height / $this->height;
if (($xratio * $this->height) < $height) {
$this->height = (int) ceil($xratio * $this->height);
$this->width = $width;
@@ -362,9 +371,6 @@ class BaseDrawing implements IComparable
$this->width = (int) ceil($yratio * $this->width);
$this->height = $height;
}
} else {
$this->width = $width;
$this->height = $height;
}
return $this;
@@ -554,4 +560,16 @@ class BaseDrawing implements IComparable
{
return $this->flipVertical;
}
public function setOpacity(?int $opacity): self
{
$this->opacity = $opacity;
return $this;
}
public function getOpacity(): ?int
{
return $this->opacity;
}
}

View File

@@ -7,6 +7,8 @@ use PhpOffice\PhpSpreadsheet\Helper\Dimension as CssDimension;
class ColumnDimension extends Dimension
{
public const EXCEL_MAX_WIDTH = 255.0;
/**
* Column index.
*/
@@ -89,6 +91,11 @@ class ColumnDimension extends Dimension
: (new CssDimension((string) $this->width))->toUnit($unitOfMeasure);
}
public function getWidthForOutput(bool $restrictMax): float
{
return ($restrictMax && $this->width > self::EXCEL_MAX_WIDTH) ? self::EXCEL_MAX_WIDTH : $this->width;
}
/**
* Set Width.
*

View File

@@ -92,7 +92,7 @@ class Drawing extends BaseDrawing
*
* @return $this
*/
public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null): static
public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null, bool $allowExternal = true): static
{
$this->isUrl = false;
if (preg_match('~^data:image/[a-z]+;base64,~', $path) === 1) {
@@ -103,10 +103,13 @@ class Drawing extends BaseDrawing
$this->path = '';
// Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
if (filter_var($path, FILTER_VALIDATE_URL) || (preg_match('/^([\\w\\s\\x00-\\x1f]+):/u', $path) && !preg_match('/^([\\w]+):/u', $path))) {
if (filter_var($path, FILTER_VALIDATE_URL) || (preg_match('/^([\w\s\x00-\x1f]+):/u', $path) && !preg_match('/^([\w]+):/u', $path))) {
if (!preg_match('/^(http|https|file|ftp|s3):/', $path)) {
throw new PhpSpreadsheetException('Invalid protocol for linked drawing');
}
if (!$allowExternal) {
return $this;
}
// Implicit that it is a URL, rather store info than running check above on value in other places.
$this->isUrl = true;
$ctx = null;
@@ -192,20 +195,6 @@ class Drawing extends BaseDrawing
return $this->isUrl;
}
/**
* Set isURL.
*
* @return $this
*
* @deprecated 3.7.0 not needed, property is set by setPath
*/
public function setIsURL(bool $isUrl): self
{
$this->isUrl = $isUrl;
return $this;
}
/**
* Get hash code.
*

View File

@@ -67,11 +67,29 @@ class HeaderFooter
{
// Header/footer image location
const IMAGE_HEADER_LEFT = 'LH';
const IMAGE_HEADER_LEFT_ODD = 'LH';
const IMAGE_HEADER_LEFT_FIRST = 'LHFIRST';
const IMAGE_HEADER_LEFT_EVEN = 'LHEVEN';
const IMAGE_HEADER_CENTER = 'CH';
const IMAGE_HEADER_CENTER_ODD = 'CH';
const IMAGE_HEADER_CENTER_FIRST = 'CHFIRST';
const IMAGE_HEADER_CENTER_EVEN = 'CHEVEN';
const IMAGE_HEADER_RIGHT = 'RH';
const IMAGE_HEADER_RIGHT_ODD = 'RH';
const IMAGE_HEADER_RIGHT_FIRST = 'RHFIRST';
const IMAGE_HEADER_RIGHT_EVEN = 'RHEVEN';
const IMAGE_FOOTER_LEFT = 'LF';
const IMAGE_FOOTER_LEFT_ODD = 'LF';
const IMAGE_FOOTER_LEFT_FIRST = 'LFFIRST';
const IMAGE_FOOTER_LEFT_EVEN = 'LFEVEN';
const IMAGE_FOOTER_CENTER = 'CF';
const IMAGE_FOOTER_CENTER_ODD = 'CF';
const IMAGE_FOOTER_CENTER_FIRST = 'CFFIRST';
const IMAGE_FOOTER_CENTER_EVEN = 'CFEVEN';
const IMAGE_FOOTER_RIGHT = 'RF';
const IMAGE_FOOTER_RIGHT_ODD = 'RF';
const IMAGE_FOOTER_RIGHT_FIRST = 'RFFIRST';
const IMAGE_FOOTER_RIGHT_EVEN = 'RFEVEN';
/**
* OddHeader.
@@ -377,6 +395,27 @@ class HeaderFooter
return $this;
}
private const IMAGE_SORT_ORDER = [
self::IMAGE_HEADER_LEFT,
self::IMAGE_HEADER_LEFT_FIRST,
self::IMAGE_HEADER_LEFT_EVEN,
self::IMAGE_HEADER_CENTER,
self::IMAGE_HEADER_CENTER_FIRST,
self::IMAGE_HEADER_CENTER_EVEN,
self::IMAGE_HEADER_RIGHT,
self::IMAGE_HEADER_RIGHT_FIRST,
self::IMAGE_HEADER_RIGHT_EVEN,
self::IMAGE_FOOTER_LEFT,
self::IMAGE_FOOTER_LEFT_FIRST,
self::IMAGE_FOOTER_LEFT_EVEN,
self::IMAGE_FOOTER_CENTER,
self::IMAGE_FOOTER_CENTER_FIRST,
self::IMAGE_FOOTER_CENTER_EVEN,
self::IMAGE_FOOTER_RIGHT,
self::IMAGE_FOOTER_RIGHT_FIRST,
self::IMAGE_FOOTER_RIGHT_EVEN,
];
/**
* Get header/footer images.
*
@@ -384,25 +423,12 @@ class HeaderFooter
*/
public function getImages(): array
{
// Sort array
// Sort array - not sure why needed
$images = [];
if (isset($this->headerFooterImages[self::IMAGE_HEADER_LEFT])) {
$images[self::IMAGE_HEADER_LEFT] = $this->headerFooterImages[self::IMAGE_HEADER_LEFT];
}
if (isset($this->headerFooterImages[self::IMAGE_HEADER_CENTER])) {
$images[self::IMAGE_HEADER_CENTER] = $this->headerFooterImages[self::IMAGE_HEADER_CENTER];
}
if (isset($this->headerFooterImages[self::IMAGE_HEADER_RIGHT])) {
$images[self::IMAGE_HEADER_RIGHT] = $this->headerFooterImages[self::IMAGE_HEADER_RIGHT];
}
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_LEFT])) {
$images[self::IMAGE_FOOTER_LEFT] = $this->headerFooterImages[self::IMAGE_FOOTER_LEFT];
}
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_CENTER])) {
$images[self::IMAGE_FOOTER_CENTER] = $this->headerFooterImages[self::IMAGE_FOOTER_CENTER];
}
if (isset($this->headerFooterImages[self::IMAGE_FOOTER_RIGHT])) {
$images[self::IMAGE_FOOTER_RIGHT] = $this->headerFooterImages[self::IMAGE_FOOTER_RIGHT];
foreach (self::IMAGE_SORT_ORDER as $key) {
if (isset($this->headerFooterImages[$key])) {
$images[$key] = $this->headerFooterImages[$key];
}
}
$this->headerFooterImages = $images;

View File

@@ -33,6 +33,8 @@ class MemoryDrawing extends BaseDrawing
/**
* Rendering function.
*
* @var callable-string
*/
private string $renderingFunction;
@@ -62,10 +64,7 @@ class MemoryDrawing extends BaseDrawing
public function __destruct()
{
if ($this->imageResource) {
@imagedestroy($this->imageResource);
$this->imageResource = null;
}
$this->imageResource = null;
$this->worksheet = null;
}
@@ -128,9 +127,6 @@ class MemoryDrawing extends BaseDrawing
public static function fromStream($imageStream): self
{
$streamValue = stream_get_contents($imageStream);
if ($streamValue === false) {
throw new Exception('Unable to read data from stream');
}
return self::fromString($streamValue);
}
@@ -161,6 +157,7 @@ class MemoryDrawing extends BaseDrawing
return $drawing;
}
/** @return callable-string */
private static function identifyRenderingFunction(string $mimeType): string
{
return match ($mimeType) {
@@ -261,6 +258,8 @@ class MemoryDrawing extends BaseDrawing
/**
* Get rendering function.
*
* @return callable-string
*/
public function getRenderingFunction(): string
{
@@ -270,7 +269,7 @@ class MemoryDrawing extends BaseDrawing
/**
* Set rendering function.
*
* @param string $value see self::RENDERING_*
* @param callable-string $value see self::RENDERING_*
*
* @return $this
*/

View File

@@ -208,14 +208,14 @@ class PageSetup
/**
* Columns to repeat at left.
*
* @var array Containing start column and end column, empty array if option unset
* @var array{string, string} Containing start column and end column, empty array if option unset
*/
private array $columnsToRepeatAtLeft = ['', ''];
/**
* Rows to repeat at top.
*
* @var array Containing start row number and end row number, empty array if option unset
* @var int[] Containing start row number and end row number, empty array if option unset
*/
private array $rowsToRepeatAtTop = [0, 0];
@@ -443,7 +443,7 @@ class PageSetup
/**
* Get Columns to repeat at left.
*
* @return array Containing start column and end column, empty array if option unset
* @return array{string, string} Containing start column and end column, empty array if option unset
*/
public function getColumnsToRepeatAtLeft(): array
{
@@ -453,7 +453,7 @@ class PageSetup
/**
* Set Columns to repeat at left.
*
* @param array $columnsToRepeatAtLeft Containing start column and end column, empty array if option unset
* @param array{string, string} $columnsToRepeatAtLeft Containing start column and end column, empty array if option unset
*
* @return $this
*/
@@ -496,7 +496,7 @@ class PageSetup
/**
* Get Rows to repeat at top.
*
* @return array Containing start column and end column, empty array if option unset
* @return int[] Containing start column and end column, empty array if option unset
*/
public function getRowsToRepeatAtTop(): array
{
@@ -506,7 +506,7 @@ class PageSetup
/**
* Set Rows to repeat at top.
*
* @param array $rowsToRepeatAtTop Containing start column and end column, empty array if option unset
* @param int[] $rowsToRepeatAtTop Containing start column and end column, empty array if option unset
*
* @return $this
*/

View File

@@ -2,6 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
class ProtectedRange
{
private string $name = '';
@@ -42,4 +44,16 @@ class ProtectedRange
{
return $this->securityDescriptor;
}
/**
* Split range into coordinate strings.
*
* @return array<array<string>> Array containing one or more arrays containing one or two coordinate strings
* e.g. ['B4','D9'] or [['B4','D9'], ['H2','O11']]
* or ['B4']
*/
public function allRanges(): array
{
return Coordinate::allRanges($this->sqref, false);
}
}

View File

@@ -64,7 +64,7 @@ class Table implements Stringable
/**
* Create a new Table.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|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.
@@ -117,10 +117,10 @@ class Table implements Stringable
) {
throw new PhpSpreadsheetException('The table name can\'t be the same as a cell reference');
}
if (!preg_match('/^[\p{L}_\\\\]/iu', $name)) {
if (!preg_match('/^[\p{L}_\\\]/iu', $name)) {
throw new PhpSpreadsheetException('The table name must begin a name with a letter, an underscore character (_), or a backslash (\)');
}
if (!preg_match('/^[\p{L}_\\\\][\p{L}\p{M}0-9\._]+$/iu', $name)) {
if (!preg_match('/^[\p{L}_\\\][\p{L}\p{M}0-9\._]+$/iu', $name)) {
throw new PhpSpreadsheetException('The table name contains invalid characters');
}
@@ -268,7 +268,7 @@ class Table implements Stringable
/**
* Set Table Cell Range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|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.
@@ -318,7 +318,7 @@ class Table implements Stringable
{
if ($this->workSheet !== null) {
$thisrange = $this->range;
$range = (string) preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
$range = (string) preg_replace('/\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
if ($range !== $thisrange) {
$this->setRange($range);
}
@@ -442,7 +442,7 @@ class Table implements Stringable
{
if ((is_string($columnObjectOrString)) && (!empty($columnObjectOrString))) {
$column = $columnObjectOrString;
} elseif (is_object($columnObjectOrString) && ($columnObjectOrString instanceof Table\Column)) {
} elseif ($columnObjectOrString instanceof Table\Column) {
$column = $columnObjectOrString->getColumnIndex();
} else {
throw new PhpSpreadsheetException('Column is not within the table range.');
@@ -491,7 +491,7 @@ class Table implements Stringable
$fromColumn = strtoupper($fromColumn);
$toColumn = strtoupper($toColumn);
if (($fromColumn !== null) && (isset($this->columns[$fromColumn])) && ($toColumn !== null)) {
if (isset($this->columns[$fromColumn])) {
$this->columns[$fromColumn]->setTable();
$this->columns[$fromColumn]->setColumnIndex($toColumn);
$this->columns[$toColumn] = $this->columns[$fromColumn];
@@ -540,6 +540,19 @@ class Table implements Stringable
return $this;
}
/**
* Get the row number on this table for given coordinates.
*/
public function getRowNumber(string $coordinate): int
{
$range = $this->getRange();
$coords = Coordinate::splitRange($range);
$firstCell = Coordinate::coordinateFromString($coords[0][0]);
$thisCell = Coordinate::coordinateFromString($coordinate);
return (int) $thisCell[1] - (int) $firstCell[1];
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
@@ -558,6 +571,7 @@ class Table implements Stringable
// The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table objects
$this->{$key} = [];
foreach ($value as $k => $v) {
/** @var Table\Column $v */
$this->{$key}[$k] = clone $v;
// attach the new cloned Column to this new cloned Table object
$this->{$key}[$k]->setTable($this);

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Worksheet\Table;
use PhpOffice\PhpSpreadsheet\Style\Style;
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
class TableStyle
@@ -93,6 +94,11 @@ class TableStyle
*/
private bool $showColumnStripes = false;
/**
* TableDxfsStyle.
*/
private ?TableDxfsStyle $tableStyle = null;
/**
* Table.
*/
@@ -198,6 +204,36 @@ class TableStyle
return $this;
}
/**
* Get this Style's Dxfs TableStyle.
*/
public function getTableDxfsStyle(): ?TableDxfsStyle
{
return $this->tableStyle;
}
/**
* Set this Style's Dxfs TableStyle.
*
* @param Style[] $dxfs
*/
public function setTableDxfsStyle(TableDxfsStyle $tableStyle, array $dxfs): self
{
$this->tableStyle = $tableStyle;
if ($this->tableStyle->getHeaderRow() !== null && isset($dxfs[$this->tableStyle->getHeaderRow()])) {
$this->tableStyle->setHeaderRowStyle($dxfs[$this->tableStyle->getHeaderRow()]);
}
if ($this->tableStyle->getFirstRowStripe() !== null && isset($dxfs[$this->tableStyle->getFirstRowStripe()])) {
$this->tableStyle->setFirstRowStripeStyle($dxfs[$this->tableStyle->getFirstRowStripe()]);
}
if ($this->tableStyle->getSecondRowStripe() !== null && isset($dxfs[$this->tableStyle->getSecondRowStripe()])) {
$this->tableStyle->setSecondRowStripeStyle($dxfs[$this->tableStyle->getSecondRowStripe()]);
}
return $this;
}
/**
* Get this Style's Table.
*/

View File

@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Worksheet;
use Composer\Pcre\Preg;
use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
use PhpOffice\PhpSpreadsheet\Cell\CellAddress;
use PhpOffice\PhpSpreadsheet\Cell\CellRange;
@@ -36,7 +37,7 @@ class Validations
/**
* Validate a cell address or cell range.
*
* @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';
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|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.
*/
@@ -45,7 +46,7 @@ class Validations
if (is_string($cellRange) || is_numeric($cellRange)) {
// Convert a single column reference like 'A' to 'A:A',
// a single row reference like '1' to '1:1'
$cellRange = (string) preg_replace('/^([A-Z]+|\d+)$/', '${1}:${1}', (string) $cellRange);
$cellRange = Preg::replace('/^([A-Z]+|\d+)$/', '${1}:${1}', (string) $cellRange);
} elseif (is_object($cellRange) && $cellRange instanceof CellAddress) {
$cellRange = new CellRange($cellRange, $cellRange);
}
@@ -56,10 +57,23 @@ class Validations
private const SETMAXROW = '${1}1:${2}' . AddressRange::MAX_ROW;
private const SETMAXCOL = 'A${1}:' . AddressRange::MAX_COLUMN . '${2}';
/**
* Convert Column ranges like 'A:C' to 'A1:C1048576'
* or Row ranges like '1:3' to 'A1:XFD3'.
*/
public static function convertWholeRowColumn(?string $addressRange): string
{
return Preg::replace(
['/^([A-Z]+):([A-Z]+)$/i', '/^(\d+):(\d+)$/'],
[self::SETMAXROW, self::SETMAXCOL],
$addressRange ?? ''
);
}
/**
* Validate a cell range.
*
* @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';
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|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.
*/
@@ -70,11 +84,7 @@ class Validations
// Convert Column ranges like 'A:C' to 'A1:C1048576'
// or Row ranges like '1:3' to 'A1:XFD3'
$addressRange = (string) preg_replace(
['/^([A-Z]+):([A-Z]+)$/i', '/^(\\d+):(\\d+)$/'],
[self::SETMAXROW, self::SETMAXCOL],
$addressRange ?? ''
);
$addressRange = self::convertWholeRowColumn($addressRange);
return empty($worksheet) ? strtoupper($addressRange) : $worksheet . '!' . strtoupper($addressRange);
}
@@ -105,11 +115,11 @@ class Validations
// Uppercase coordinate
$coordinate = strtoupper($coordinate);
// Eliminate leading equal sign
$testCoordinate = (string) preg_replace('/^=/', '', $coordinate);
$testCoordinate = Preg::replace('/^=/', '', $coordinate);
$defined = $worksheet->getParentOrThrow()->getDefinedName($testCoordinate, $worksheet);
if ($defined !== null) {
if ($defined->getWorksheet() === $worksheet && !$defined->isFormula()) {
$coordinate = (string) preg_replace('/^=/', '', $defined->getValue());
$coordinate = Preg::replace('/^=/', '', $defined->getValue());
}
}