DefinedName.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet;
  3. use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
  4. abstract class DefinedName
  5. {
  6. protected const REGEXP_IDENTIFY_FORMULA = '[^_\p{N}\p{L}:, \$\'!]';
  7. /**
  8. * Name.
  9. *
  10. * @var string
  11. */
  12. protected $name;
  13. /**
  14. * Worksheet on which the defined name can be resolved.
  15. *
  16. * @var ?Worksheet
  17. */
  18. protected $worksheet;
  19. /**
  20. * Value of the named object.
  21. *
  22. * @var string
  23. */
  24. protected $value;
  25. /**
  26. * Is the defined named local? (i.e. can only be used on $this->worksheet).
  27. *
  28. * @var bool
  29. */
  30. protected $localOnly;
  31. /**
  32. * Scope.
  33. *
  34. * @var ?Worksheet
  35. */
  36. protected $scope;
  37. /**
  38. * Whether this is a named range or a named formula.
  39. *
  40. * @var bool
  41. */
  42. protected $isFormula;
  43. /**
  44. * Create a new Defined Name.
  45. */
  46. public function __construct(
  47. string $name,
  48. ?Worksheet $worksheet = null,
  49. ?string $value = null,
  50. bool $localOnly = false,
  51. ?Worksheet $scope = null
  52. ) {
  53. if ($worksheet === null) {
  54. $worksheet = $scope;
  55. }
  56. // Set local members
  57. $this->name = $name;
  58. $this->worksheet = $worksheet;
  59. $this->value = (string) $value;
  60. $this->localOnly = $localOnly;
  61. // If local only, then the scope will be set to worksheet unless a scope is explicitly set
  62. $this->scope = ($localOnly === true) ? (($scope === null) ? $worksheet : $scope) : null;
  63. // If the range string contains characters that aren't associated with the range definition (A-Z,1-9
  64. // for cell references, and $, or the range operators (colon comma or space), quotes and ! for
  65. // worksheet names
  66. // then this is treated as a named formula, and not a named range
  67. $this->isFormula = self::testIfFormula($this->value);
  68. }
  69. /**
  70. * Create a new defined name, either a range or a formula.
  71. */
  72. public static function createInstance(
  73. string $name,
  74. ?Worksheet $worksheet = null,
  75. ?string $value = null,
  76. bool $localOnly = false,
  77. ?Worksheet $scope = null
  78. ): self {
  79. $value = (string) $value;
  80. $isFormula = self::testIfFormula($value);
  81. if ($isFormula) {
  82. return new NamedFormula($name, $worksheet, $value, $localOnly, $scope);
  83. }
  84. return new NamedRange($name, $worksheet, $value, $localOnly, $scope);
  85. }
  86. public static function testIfFormula(string $value): bool
  87. {
  88. if (substr($value, 0, 1) === '=') {
  89. $value = substr($value, 1);
  90. }
  91. if (is_numeric($value)) {
  92. return true;
  93. }
  94. $segMatcher = false;
  95. foreach (explode("'", $value) as $subVal) {
  96. // Only test in alternate array entries (the non-quoted blocks)
  97. $segMatcher = $segMatcher === false;
  98. if (
  99. $segMatcher &&
  100. (preg_match('/' . self::REGEXP_IDENTIFY_FORMULA . '/miu', $subVal))
  101. ) {
  102. return true;
  103. }
  104. }
  105. return false;
  106. }
  107. /**
  108. * Get name.
  109. */
  110. public function getName(): string
  111. {
  112. return $this->name;
  113. }
  114. /**
  115. * Set name.
  116. */
  117. public function setName(string $name): self
  118. {
  119. if (!empty($name)) {
  120. // Old title
  121. $oldTitle = $this->name;
  122. // Re-attach
  123. if ($this->worksheet !== null) {
  124. $this->worksheet->getParentOrThrow()->removeNamedRange($this->name, $this->worksheet);
  125. }
  126. $this->name = $name;
  127. if ($this->worksheet !== null) {
  128. $this->worksheet->getParentOrThrow()->addDefinedName($this);
  129. }
  130. if ($this->worksheet !== null) {
  131. // New title
  132. $newTitle = $this->name;
  133. ReferenceHelper::getInstance()->updateNamedFormulae($this->worksheet->getParentOrThrow(), $oldTitle, $newTitle);
  134. }
  135. }
  136. return $this;
  137. }
  138. /**
  139. * Get worksheet.
  140. */
  141. public function getWorksheet(): ?Worksheet
  142. {
  143. return $this->worksheet;
  144. }
  145. /**
  146. * Set worksheet.
  147. */
  148. public function setWorksheet(?Worksheet $worksheet): self
  149. {
  150. $this->worksheet = $worksheet;
  151. return $this;
  152. }
  153. /**
  154. * Get range or formula value.
  155. */
  156. public function getValue(): string
  157. {
  158. return $this->value;
  159. }
  160. /**
  161. * Set range or formula value.
  162. */
  163. public function setValue(string $value): self
  164. {
  165. $this->value = $value;
  166. return $this;
  167. }
  168. /**
  169. * Get localOnly.
  170. */
  171. public function getLocalOnly(): bool
  172. {
  173. return $this->localOnly;
  174. }
  175. /**
  176. * Set localOnly.
  177. */
  178. public function setLocalOnly(bool $localScope): self
  179. {
  180. $this->localOnly = $localScope;
  181. $this->scope = $localScope ? $this->worksheet : null;
  182. return $this;
  183. }
  184. /**
  185. * Get scope.
  186. */
  187. public function getScope(): ?Worksheet
  188. {
  189. return $this->scope;
  190. }
  191. /**
  192. * Set scope.
  193. */
  194. public function setScope(?Worksheet $worksheet): self
  195. {
  196. $this->scope = $worksheet;
  197. $this->localOnly = $worksheet !== null;
  198. return $this;
  199. }
  200. /**
  201. * Identify whether this is a named range or a named formula.
  202. */
  203. public function isFormula(): bool
  204. {
  205. return $this->isFormula;
  206. }
  207. /**
  208. * Resolve a named range to a regular cell range or formula.
  209. */
  210. public static function resolveName(string $definedName, Worksheet $worksheet, string $sheetName = ''): ?self
  211. {
  212. if ($sheetName === '') {
  213. $worksheet2 = $worksheet;
  214. } else {
  215. $worksheet2 = $worksheet->getParentOrThrow()->getSheetByName($sheetName);
  216. if ($worksheet2 === null) {
  217. return null;
  218. }
  219. }
  220. return $worksheet->getParentOrThrow()->getDefinedName($definedName, $worksheet2);
  221. }
  222. /**
  223. * Implement PHP __clone to create a deep clone, not just a shallow copy.
  224. */
  225. public function __clone()
  226. {
  227. $vars = get_object_vars($this);
  228. foreach ($vars as $key => $value) {
  229. if (is_object($value)) {
  230. $this->$key = clone $value;
  231. } else {
  232. $this->$key = $value;
  233. }
  234. }
  235. }
  236. }