ClassMetadata.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ORM\Mapping;
  20. use ReflectionClass, ReflectionProperty;
  21. /**
  22. * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
  23. * of an entity and it's associations.
  24. *
  25. * Once populated, ClassMetadata instances are usually cached in a serialized form.
  26. *
  27. * <b>IMPORTANT NOTE:</b>
  28. *
  29. * The fields of this class are only public for 2 reasons:
  30. * 1) To allow fast READ access.
  31. * 2) To drastically reduce the size of a serialized instance (private/protected members
  32. * get the whole class name, namespace inclusive, prepended to every property in
  33. * the serialized representation).
  34. *
  35. * @author Roman Borschel <roman@code-factory.org>
  36. * @author Jonathan H. Wage <jonwage@gmail.com>
  37. * @since 2.0
  38. */
  39. class ClassMetadata extends ClassMetadataInfo
  40. {
  41. /**
  42. * The ReflectionProperty instances of the mapped class.
  43. *
  44. * @var array
  45. */
  46. public $reflFields = array();
  47. /**
  48. * The prototype from which new instances of the mapped class are created.
  49. *
  50. * @var object
  51. */
  52. private $_prototype;
  53. /**
  54. * Initializes a new ClassMetadata instance that will hold the object-relational mapping
  55. * metadata of the class with the given name.
  56. *
  57. * @param string $entityName The name of the entity class the new instance is used for.
  58. */
  59. public function __construct($entityName)
  60. {
  61. $this->reflClass = new ReflectionClass($entityName);
  62. $this->namespace = $this->reflClass->getNamespaceName();
  63. $this->table['name'] = $this->reflClass->getShortName();
  64. parent::__construct($this->reflClass->getName()); // do not use $entityName, possible case-problems
  65. }
  66. /**
  67. * Gets the ReflectionPropertys of the mapped class.
  68. *
  69. * @return array An array of ReflectionProperty instances.
  70. */
  71. public function getReflectionProperties()
  72. {
  73. return $this->reflFields;
  74. }
  75. /**
  76. * Gets a ReflectionProperty for a specific field of the mapped class.
  77. *
  78. * @param string $name
  79. * @return ReflectionProperty
  80. */
  81. public function getReflectionProperty($name)
  82. {
  83. return $this->reflFields[$name];
  84. }
  85. /**
  86. * Gets the ReflectionProperty for the single identifier field.
  87. *
  88. * @return ReflectionProperty
  89. * @throws BadMethodCallException If the class has a composite identifier.
  90. */
  91. public function getSingleIdReflectionProperty()
  92. {
  93. if ($this->isIdentifierComposite) {
  94. throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
  95. }
  96. return $this->reflFields[$this->identifier[0]];
  97. }
  98. /**
  99. * Validates & completes the given field mapping.
  100. *
  101. * @param array $mapping The field mapping to validated & complete.
  102. * @return array The validated and completed field mapping.
  103. *
  104. * @throws MappingException
  105. */
  106. protected function _validateAndCompleteFieldMapping(array &$mapping)
  107. {
  108. parent::_validateAndCompleteFieldMapping($mapping);
  109. // Store ReflectionProperty of mapped field
  110. $refProp = $this->reflClass->getProperty($mapping['fieldName']);
  111. $refProp->setAccessible(true);
  112. $this->reflFields[$mapping['fieldName']] = $refProp;
  113. }
  114. /**
  115. * Extracts the identifier values of an entity of this class.
  116. *
  117. * For composite identifiers, the identifier values are returned as an array
  118. * with the same order as the field order in {@link identifier}.
  119. *
  120. * @param object $entity
  121. * @return array
  122. */
  123. public function getIdentifierValues($entity)
  124. {
  125. if ($this->isIdentifierComposite) {
  126. $id = array();
  127. foreach ($this->identifier as $idField) {
  128. $value = $this->reflFields[$idField]->getValue($entity);
  129. if ($value !== null) {
  130. $id[$idField] = $value;
  131. }
  132. }
  133. return $id;
  134. } else {
  135. $value = $this->reflFields[$this->identifier[0]]->getValue($entity);
  136. if ($value !== null) {
  137. return array($this->identifier[0] => $value);
  138. }
  139. return array();
  140. }
  141. }
  142. /**
  143. * Populates the entity identifier of an entity.
  144. *
  145. * @param object $entity
  146. * @param mixed $id
  147. * @todo Rename to assignIdentifier()
  148. */
  149. public function setIdentifierValues($entity, array $id)
  150. {
  151. foreach ($id as $idField => $idValue) {
  152. $this->reflFields[$idField]->setValue($entity, $idValue);
  153. }
  154. }
  155. /**
  156. * Sets the specified field to the specified value on the given entity.
  157. *
  158. * @param object $entity
  159. * @param string $field
  160. * @param mixed $value
  161. */
  162. public function setFieldValue($entity, $field, $value)
  163. {
  164. $this->reflFields[$field]->setValue($entity, $value);
  165. }
  166. /**
  167. * Gets the specified field's value off the given entity.
  168. *
  169. * @param object $entity
  170. * @param string $field
  171. */
  172. public function getFieldValue($entity, $field)
  173. {
  174. return $this->reflFields[$field]->getValue($entity);
  175. }
  176. /**
  177. * Stores the association mapping.
  178. *
  179. * @param AssociationMapping $assocMapping
  180. */
  181. protected function _storeAssociationMapping(array $assocMapping)
  182. {
  183. parent::_storeAssociationMapping($assocMapping);
  184. // Store ReflectionProperty of mapped field
  185. $sourceFieldName = $assocMapping['fieldName'];
  186. $refProp = $this->reflClass->getProperty($sourceFieldName);
  187. $refProp->setAccessible(true);
  188. $this->reflFields[$sourceFieldName] = $refProp;
  189. }
  190. /**
  191. * Creates a string representation of this instance.
  192. *
  193. * @return string The string representation of this instance.
  194. * @todo Construct meaningful string representation.
  195. */
  196. public function __toString()
  197. {
  198. return __CLASS__ . '@' . spl_object_hash($this);
  199. }
  200. /**
  201. * Determines which fields get serialized.
  202. *
  203. * It is only serialized what is necessary for best unserialization performance.
  204. * That means any metadata properties that are not set or empty or simply have
  205. * their default value are NOT serialized.
  206. *
  207. * Parts that are also NOT serialized because they can not be properly unserialized:
  208. * - reflClass (ReflectionClass)
  209. * - reflFields (ReflectionProperty array)
  210. *
  211. * @return array The names of all the fields that should be serialized.
  212. */
  213. public function __sleep()
  214. {
  215. // This metadata is always serialized/cached.
  216. $serialized = array(
  217. 'associationMappings',
  218. 'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
  219. 'fieldMappings',
  220. 'fieldNames',
  221. 'identifier',
  222. 'isIdentifierComposite', // TODO: REMOVE
  223. 'name',
  224. 'namespace', // TODO: REMOVE
  225. 'table',
  226. 'rootEntityName',
  227. 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
  228. );
  229. // The rest of the metadata is only serialized if necessary.
  230. if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
  231. $serialized[] = 'changeTrackingPolicy';
  232. }
  233. if ($this->customRepositoryClassName) {
  234. $serialized[] = 'customRepositoryClassName';
  235. }
  236. if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
  237. $serialized[] = 'inheritanceType';
  238. $serialized[] = 'discriminatorColumn';
  239. $serialized[] = 'discriminatorValue';
  240. $serialized[] = 'discriminatorMap';
  241. $serialized[] = 'parentClasses';
  242. $serialized[] = 'subClasses';
  243. }
  244. if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
  245. $serialized[] = 'generatorType';
  246. if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
  247. $serialized[] = 'sequenceGeneratorDefinition';
  248. }
  249. }
  250. if ($this->isMappedSuperclass) {
  251. $serialized[] = 'isMappedSuperclass';
  252. }
  253. if ($this->containsForeignIdentifier) {
  254. $serialized[] = 'containsForeignIdentifier';
  255. }
  256. if ($this->isVersioned) {
  257. $serialized[] = 'isVersioned';
  258. $serialized[] = 'versionField';
  259. }
  260. if ($this->lifecycleCallbacks) {
  261. $serialized[] = 'lifecycleCallbacks';
  262. }
  263. if ($this->namedQueries) {
  264. $serialized[] = 'namedQueries';
  265. }
  266. if ($this->isReadOnly) {
  267. $serialized[] = 'isReadOnly';
  268. }
  269. return $serialized;
  270. }
  271. /**
  272. * Restores some state that can not be serialized/unserialized.
  273. *
  274. * @return void
  275. */
  276. public function __wakeup()
  277. {
  278. // Restore ReflectionClass and properties
  279. $this->reflClass = new ReflectionClass($this->name);
  280. foreach ($this->fieldMappings as $field => $mapping) {
  281. if (isset($mapping['declared'])) {
  282. $reflField = new ReflectionProperty($mapping['declared'], $field);
  283. } else {
  284. $reflField = $this->reflClass->getProperty($field);
  285. }
  286. $reflField->setAccessible(true);
  287. $this->reflFields[$field] = $reflField;
  288. }
  289. foreach ($this->associationMappings as $field => $mapping) {
  290. if (isset($mapping['declared'])) {
  291. $reflField = new ReflectionProperty($mapping['declared'], $field);
  292. } else {
  293. $reflField = $this->reflClass->getProperty($field);
  294. }
  295. $reflField->setAccessible(true);
  296. $this->reflFields[$field] = $reflField;
  297. }
  298. }
  299. /**
  300. * Creates a new instance of the mapped class, without invoking the constructor.
  301. *
  302. * @return object
  303. */
  304. public function newInstance()
  305. {
  306. if ($this->_prototype === null) {
  307. $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
  308. }
  309. return clone $this->_prototype;
  310. }
  311. }