AbstractAsset.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. namespace Doctrine\DBAL\Schema;
  3. use Doctrine\DBAL\Platforms\AbstractPlatform;
  4. use Doctrine\Deprecations\Deprecation;
  5. use function array_map;
  6. use function crc32;
  7. use function dechex;
  8. use function explode;
  9. use function implode;
  10. use function str_replace;
  11. use function strpos;
  12. use function strtolower;
  13. use function strtoupper;
  14. use function substr;
  15. /**
  16. * The abstract asset allows to reset the name of all assets without publishing this to the public userland.
  17. *
  18. * This encapsulation hack is necessary to keep a consistent state of the database schema. Say we have a list of tables
  19. * array($tableName => Table($tableName)); if you want to rename the table, you have to make sure
  20. */
  21. abstract class AbstractAsset
  22. {
  23. /** @var string */
  24. protected $_name = '';
  25. /**
  26. * Namespace of the asset. If none isset the default namespace is assumed.
  27. *
  28. * @var string|null
  29. */
  30. protected $_namespace;
  31. /** @var bool */
  32. protected $_quoted = false;
  33. /**
  34. * Sets the name of this asset.
  35. *
  36. * @param string $name
  37. *
  38. * @return void
  39. */
  40. protected function _setName($name)
  41. {
  42. if ($this->isIdentifierQuoted($name)) {
  43. $this->_quoted = true;
  44. $name = $this->trimQuotes($name);
  45. }
  46. if (strpos($name, '.') !== false) {
  47. $parts = explode('.', $name);
  48. $this->_namespace = $parts[0];
  49. $name = $parts[1];
  50. }
  51. $this->_name = $name;
  52. }
  53. /**
  54. * Is this asset in the default namespace?
  55. *
  56. * @param string $defaultNamespaceName
  57. *
  58. * @return bool
  59. */
  60. public function isInDefaultNamespace($defaultNamespaceName)
  61. {
  62. return $this->_namespace === $defaultNamespaceName || $this->_namespace === null;
  63. }
  64. /**
  65. * Gets the namespace name of this asset.
  66. *
  67. * If NULL is returned this means the default namespace is used.
  68. *
  69. * @return string|null
  70. */
  71. public function getNamespaceName()
  72. {
  73. return $this->_namespace;
  74. }
  75. /**
  76. * The shortest name is stripped of the default namespace. All other
  77. * namespaced elements are returned as full-qualified names.
  78. *
  79. * @param string|null $defaultNamespaceName
  80. *
  81. * @return string
  82. */
  83. public function getShortestName($defaultNamespaceName)
  84. {
  85. $shortestName = $this->getName();
  86. if ($this->_namespace === $defaultNamespaceName) {
  87. $shortestName = $this->_name;
  88. }
  89. return strtolower($shortestName);
  90. }
  91. /**
  92. * The normalized name is full-qualified and lower-cased. Lower-casing is
  93. * actually wrong, but we have to do it to keep our sanity. If you are
  94. * using database objects that only differentiate in the casing (FOO vs
  95. * Foo) then you will NOT be able to use Doctrine Schema abstraction.
  96. *
  97. * Every non-namespaced element is prefixed with the default namespace
  98. * name which is passed as argument to this method.
  99. *
  100. * @deprecated Use {@see getNamespaceName()} and {@see getName()} instead.
  101. *
  102. * @param string $defaultNamespaceName
  103. *
  104. * @return string
  105. */
  106. public function getFullQualifiedName($defaultNamespaceName)
  107. {
  108. Deprecation::triggerIfCalledFromOutside(
  109. 'doctrine/dbal',
  110. 'https://github.com/doctrine/dbal/pull/4814',
  111. 'AbstractAsset::getFullQualifiedName() is deprecated.'
  112. . ' Use AbstractAsset::getNamespaceName() and ::getName() instead.'
  113. );
  114. $name = $this->getName();
  115. if ($this->_namespace === null) {
  116. $name = $defaultNamespaceName . '.' . $name;
  117. }
  118. return strtolower($name);
  119. }
  120. /**
  121. * Checks if this asset's name is quoted.
  122. *
  123. * @return bool
  124. */
  125. public function isQuoted()
  126. {
  127. return $this->_quoted;
  128. }
  129. /**
  130. * Checks if this identifier is quoted.
  131. *
  132. * @param string $identifier
  133. *
  134. * @return bool
  135. */
  136. protected function isIdentifierQuoted($identifier)
  137. {
  138. return isset($identifier[0]) && ($identifier[0] === '`' || $identifier[0] === '"' || $identifier[0] === '[');
  139. }
  140. /**
  141. * Trim quotes from the identifier.
  142. *
  143. * @param string $identifier
  144. *
  145. * @return string
  146. */
  147. protected function trimQuotes($identifier)
  148. {
  149. return str_replace(['`', '"', '[', ']'], '', $identifier);
  150. }
  151. /**
  152. * Returns the name of this schema asset.
  153. *
  154. * @return string
  155. */
  156. public function getName()
  157. {
  158. if ($this->_namespace !== null) {
  159. return $this->_namespace . '.' . $this->_name;
  160. }
  161. return $this->_name;
  162. }
  163. /**
  164. * Gets the quoted representation of this asset but only if it was defined with one. Otherwise
  165. * return the plain unquoted value as inserted.
  166. *
  167. * @return string
  168. */
  169. public function getQuotedName(AbstractPlatform $platform)
  170. {
  171. $keywords = $platform->getReservedKeywordsList();
  172. $parts = explode('.', $this->getName());
  173. foreach ($parts as $k => $v) {
  174. $parts[$k] = $this->_quoted || $keywords->isKeyword($v) ? $platform->quoteIdentifier($v) : $v;
  175. }
  176. return implode('.', $parts);
  177. }
  178. /**
  179. * Generates an identifier from a list of column names obeying a certain string length.
  180. *
  181. * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars,
  182. * however building idents automatically for foreign keys, composite keys or such can easily create
  183. * very long names.
  184. *
  185. * @param string[] $columnNames
  186. * @param string $prefix
  187. * @param int $maxSize
  188. *
  189. * @return string
  190. */
  191. protected function _generateIdentifierName($columnNames, $prefix = '', $maxSize = 30)
  192. {
  193. $hash = implode('', array_map(static function ($column): string {
  194. return dechex(crc32($column));
  195. }, $columnNames));
  196. return strtoupper(substr($prefix . '_' . $hash, 0, $maxSize));
  197. }
  198. }