Completed
Pull Request — develop_3.0 (#438)
by Adrien
02:47
created

SheetManager::markWorkbookIdAsUsed()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Box\Spout\Writer\Common\Manager;
4
5
use Box\Spout\Common\Helper\StringHelper;
6
use Box\Spout\Writer\Common\Entity\Sheet;
7
use Box\Spout\Writer\Exception\InvalidSheetNameException;
8
9
/**
10
 * Class SheetManager
11
 * Sheet manager
12
 *
13
 * @package Box\Spout\Writer\Common\Manager
14
 */
15
class SheetManager
16
{
17
    /** Sheet name should not exceed 31 characters */
18
    const MAX_LENGTH_SHEET_NAME = 31;
19
20
    /** @var array Invalid characters that cannot be contained in the sheet name */
21
    private static $INVALID_CHARACTERS_IN_SHEET_NAME = ['\\', '/', '?', '*', ':', '[', ']'];
22
23
    /** @var array Associative array [WORKBOOK_ID] => [[SHEET_INDEX] => [SHEET_NAME]] keeping track of sheets' name to enforce uniqueness per workbook */
24
    private static $SHEETS_NAME_USED = [];
25
26
    /** @var StringHelper */
27
    private $stringHelper;
28
29
    /**
30
     * SheetManager constructor.
31
     *
32
     * @param StringHelper $stringHelper
33
     */
34 107
    public function __construct(StringHelper $stringHelper)
35
    {
36 107
        $this->stringHelper = $stringHelper;
37 107
    }
38
39
    /**
40
     * Throws an exception if the given sheet's name is not valid.
41
     * @see Sheet::setName for validity rules.
42
     *
43
     * @param string $name
44
     * @param Sheet $sheet The sheet whose future name is checked
45
     * @return void
46
     * @throws \Box\Spout\Writer\Exception\InvalidSheetNameException If the sheet's name is invalid.
47
     */
48 107
    public function throwIfNameIsInvalid($name, Sheet $sheet)
49
    {
50 107
        if (!is_string($name)) {
51 2
            $actualType = gettype($name);
52 2
            $errorMessage = "The sheet's name is invalid. It must be a string ($actualType given).";
53 2
            throw new InvalidSheetNameException($errorMessage);
54
        }
55
56 107
        $failedRequirements = [];
57 107
        $nameLength = $this->stringHelper->getStringLength($name);
58
59 107
        if (!$this->isNameUnique($name, $sheet)) {
60 3
            $failedRequirements[] = 'It should be unique';
61
        } else {
62 107
            if ($nameLength === 0) {
63 1
                $failedRequirements[] = 'It should not be blank';
64
            } else {
65 107
                if ($nameLength > self::MAX_LENGTH_SHEET_NAME) {
66 1
                    $failedRequirements[] = 'It should not exceed 31 characters';
67
                }
68
69 107
                if ($this->doesContainInvalidCharacters($name)) {
70 7
                    $failedRequirements[] = 'It should not contain these characters: \\ / ? * : [ or ]';
71
                }
72
73 107
                if ($this->doesStartOrEndWithSingleQuote($name)) {
74 2
                    $failedRequirements[] = 'It should not start or end with a single quote';
75
                }
76
            }
77
        }
78
79 107
        if (count($failedRequirements) !== 0) {
80 14
            $errorMessage = "The sheet's name (\"$name\") is invalid. It did not respect these rules:\n - ";
81 14
            $errorMessage .= implode("\n - ", $failedRequirements);
82 14
            throw new InvalidSheetNameException($errorMessage);
83
        }
84 107
    }
85
86
    /**
87
     * Returns whether the given name contains at least one invalid character.
88
     * @see Sheet::$INVALID_CHARACTERS_IN_SHEET_NAME for the full list.
89
     *
90
     * @param string $name
91
     * @return bool TRUE if the name contains invalid characters, FALSE otherwise.
92
     */
93 107
    private function doesContainInvalidCharacters($name)
94
    {
95 107
        return (str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name);
96
    }
97
98
    /**
99
     * Returns whether the given name starts or ends with a single quote
100
     *
101
     * @param string $name
102
     * @return bool TRUE if the name starts or ends with a single quote, FALSE otherwise.
103
     */
104 107
    private function doesStartOrEndWithSingleQuote($name)
105
    {
106 107
        $startsWithSingleQuote = ($this->stringHelper->getCharFirstOccurrencePosition('\'', $name) === 0);
107 107
        $endsWithSingleQuote = ($this->stringHelper->getCharLastOccurrencePosition('\'', $name) === ($this->stringHelper->getStringLength($name) - 1));
108
109 107
        return ($startsWithSingleQuote || $endsWithSingleQuote);
110
    }
111
112
    /**
113
     * Returns whether the given name is unique.
114
     *
115
     * @param string $name
116
     * @param Sheet $sheet The sheet whose future name is checked
117
     * @return bool TRUE if the name is unique, FALSE otherwise.
118
     */
119 107
    private function isNameUnique($name, Sheet $sheet)
120
    {
121 107
        foreach (self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()] as $sheetIndex => $sheetName) {
122 37
            if ($sheetIndex !== $sheet->getIndex() && $sheetName === $name) {
123 3
                return false;
124
            }
125
        }
126
127 107
        return true;
128
    }
129
130
    /**
131
     * @param int $workbookId Workbook ID associated to a Sheet
132
     * @return void
133
     */
134 107
    public function markWorkbookIdAsUsed($workbookId)
135
    {
136 107
        if (!isset(self::$SHEETS_NAME_USED[$workbookId])) {
137 91
            self::$SHEETS_NAME_USED[$workbookId] = [];
138
        }
139 107
    }
140
141
    /**
142
     * @param Sheet $sheet
143
     * @return void
144
     */
145 107
    public function markSheetNameAsUsed(Sheet $sheet)
146
    {
147 107
        self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()][$sheet->getIndex()] = $sheet->getName();
148 107
    }
149
}
150