DefinedName.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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. if (
  98. ($segMatcher = !$segMatcher) &&
  99. (preg_match('/' . self::REGEXP_IDENTIFY_FORMULA . '/miu', $subVal))
  100. ) {
  101. return true;
  102. }
  103. }
  104. return false;
  105. }
  106. /**
  107. * Get name.
  108. */
  109. public function getName(): string
  110. {
  111. return $this->name;
  112. }
  113. /**
  114. * Set name.
  115. */
  116. public function setName(string $name): self
  117. {
  118. if (!empty($name)) {
  119. // Old title
  120. $oldTitle = $this->name;
  121. // Re-attach
  122. if ($this->worksheet !== null) {
  123. $this->worksheet->getParent()->removeNamedRange($this->name, $this->worksheet);
  124. }
  125. $this->name = $name;
  126. if ($this->worksheet !== null) {
  127. $this->worksheet->getParent()->addNamedRange($this);
  128. }
  129. // New title
  130. $newTitle = $this->name;
  131. ReferenceHelper::getInstance()->updateNamedFormulas($this->worksheet->getParent(), $oldTitle, $newTitle);
  132. }
  133. return $this;
  134. }
  135. /**
  136. * Get worksheet.
  137. */
  138. public function getWorksheet(): ?Worksheet
  139. {
  140. return $this->worksheet;
  141. }
  142. /**
  143. * Set worksheet.
  144. */
  145. public function setWorksheet(?Worksheet $worksheet): self
  146. {
  147. $this->worksheet = $worksheet;
  148. return $this;
  149. }
  150. /**
  151. * Get range or formula value.
  152. */
  153. public function getValue(): string
  154. {
  155. return $this->value;
  156. }
  157. /**
  158. * Set range or formula value.
  159. */
  160. public function setValue(string $value): self
  161. {
  162. $this->value = $value;
  163. return $this;
  164. }
  165. /**
  166. * Get localOnly.
  167. */
  168. public function getLocalOnly(): bool
  169. {
  170. return $this->localOnly;
  171. }
  172. /**
  173. * Set localOnly.
  174. */
  175. public function setLocalOnly(bool $localScope): self
  176. {
  177. $this->localOnly = $localScope;
  178. $this->scope = $localScope ? $this->worksheet : null;
  179. return $this;
  180. }
  181. /**
  182. * Get scope.
  183. */
  184. public function getScope(): ?Worksheet
  185. {
  186. return $this->scope;
  187. }
  188. /**
  189. * Set scope.
  190. */
  191. public function setScope(?Worksheet $worksheet): self
  192. {
  193. $this->scope = $worksheet;
  194. $this->localOnly = $worksheet !== null;
  195. return $this;
  196. }
  197. /**
  198. * Identify whether this is a named range or a named formula.
  199. */
  200. public function isFormula(): bool
  201. {
  202. return $this->isFormula;
  203. }
  204. /**
  205. * Resolve a named range to a regular cell range or formula.
  206. */
  207. public static function resolveName(string $definedName, Worksheet $worksheet, string $sheetName = ''): ?self
  208. {
  209. if ($sheetName === '') {
  210. $worksheet2 = $worksheet;
  211. } else {
  212. $worksheet2 = $worksheet->getParent()->getSheetByName($sheetName);
  213. if ($worksheet2 === null) {
  214. return null;
  215. }
  216. }
  217. return $worksheet->getParent()->getDefinedName($definedName, $worksheet2);
  218. }
  219. /**
  220. * Implement PHP __clone to create a deep clone, not just a shallow copy.
  221. */
  222. public function __clone()
  223. {
  224. $vars = get_object_vars($this);
  225. foreach ($vars as $key => $value) {
  226. if (is_object($value)) {
  227. $this->$key = clone $value;
  228. } else {
  229. $this->$key = $value;
  230. }
  231. }
  232. }
  233. }