PageRenderTime 52ms CodeModel.GetById 7ms RepoModel.GetById 1ms app.codeStats 0ms

/main/Base/AbstractProtoClass.class.php

http://github.com/onPHP/onphp-framework
PHP | 461 lines | 347 code | 80 blank | 34 comment | 55 complexity | ae5df31f829143b2f1d343be76f99d57 MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /***************************************************************************
  3. * Copyright (C) 2006-2008 by Konstantin V. Arkhipov *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU Lesser General Public License as *
  7. * published by the Free Software Foundation; either version 3 of the *
  8. * License, or (at your option) any later version. *
  9. * *
  10. ***************************************************************************/
  11. /**
  12. * @ingroup Helpers
  13. **/
  14. abstract class AbstractProtoClass extends Singleton
  15. {
  16. private $depth = 0;
  17. private $storage = array();
  18. private $skipList = array();
  19. abstract protected function makePropertyList();
  20. /**
  21. * @return AbstractProtoClass
  22. **/
  23. public function beginPrefetch()
  24. {
  25. $this->storage[++$this->depth] = array();
  26. $this->skipList[$this->depth] = array();
  27. return $this;
  28. }
  29. /**
  30. * @return AbstractProtoClass
  31. **/
  32. public function skipObjectPrefetching(Identifiable $object)
  33. {
  34. if ($this->depth) {
  35. if (!isset($this->skipList[$this->depth][$object->getId()]))
  36. $this->skipList[$this->depth][$object->getId()] = 1;
  37. else
  38. ++$this->skipList[$this->depth][$object->getId()];
  39. }
  40. return $this;
  41. }
  42. public function endPrefetch(array $objectList)
  43. {
  44. if (!$this->depth)
  45. throw new WrongStateException('prefetch mode is already off');
  46. foreach ($this->storage[$this->depth] as $setter => $innerList) {
  47. Assert::isEqual(
  48. count($objectList),
  49. count($innerList) + array_sum($this->skipList[$this->depth])
  50. );
  51. $ids = array();
  52. foreach ($innerList as $inner)
  53. if ($inner)
  54. $ids[] = $inner->getId();
  55. // finding first available inner object
  56. foreach ($innerList as $inner)
  57. if ($inner)
  58. break;
  59. if (!$inner)
  60. continue;
  61. // put yet unmapped objects into dao's identityMap
  62. $inner->dao()->getListByIds($ids);
  63. $skippedMap = $this->skipList[$this->depth];
  64. $i = $j = 0;
  65. foreach ($objectList as $object) {
  66. $objectId = $object->getId();
  67. if (isset($skippedMap[$objectId])) {
  68. if ($skippedMap[$objectId] == 1)
  69. unset($skippedMap[$objectId]);
  70. else
  71. --$skippedMap[$objectId];
  72. ++$j;
  73. continue;
  74. }
  75. if ($innerList[$i]) {
  76. try {
  77. // avoid dao "caching" here
  78. // because of possible breakage
  79. // in overriden properties
  80. $object->$setter(
  81. $innerList[$i]->dao()->getById(
  82. $innerList[$i]->getId()
  83. )
  84. );
  85. } catch (ObjectNotFoundException $e) {
  86. throw new WrongStateException(
  87. 'possible corruption found: '.$e->getMessage()
  88. );
  89. }
  90. }
  91. ++$i;
  92. }
  93. Assert::isEqual(
  94. $i,
  95. count($objectList) - $j
  96. );
  97. }
  98. unset($this->skipList[$this->depth], $this->storage[$this->depth--]);
  99. return $objectList;
  100. }
  101. public static function makeOnlyObject($className, $array, $prefix = null)
  102. {
  103. return self::assemblyObject(new $className, $array, $prefix);
  104. }
  105. public static function completeObject(Prototyped $object)
  106. {
  107. return self::fetchEncapsulants($object);
  108. }
  109. final public function getPropertyList()
  110. {
  111. static $lists = array();
  112. $className = get_class($this);
  113. if (!isset($lists[$className])) {
  114. $lists[$className] = $this->makePropertyList();
  115. }
  116. return $lists[$className];
  117. }
  118. final public function getExpandedPropertyList($prefix = null)
  119. {
  120. static $lists = array();
  121. $className = get_class($this);
  122. if (!isset($lists[$className])) {
  123. foreach ($this->makePropertyList() as $property) {
  124. if ($property instanceof InnerMetaProperty) {
  125. $lists[$className] =
  126. array_merge(
  127. $lists[$className],
  128. $property->getProto()->getExpandedPropertyList(
  129. $property->getName().':'
  130. )
  131. );
  132. } else {
  133. $lists[
  134. $className
  135. ][
  136. $prefix.$property->getName()
  137. ]
  138. = $property;
  139. }
  140. }
  141. }
  142. return $lists[$className];
  143. }
  144. /**
  145. * @return LightMetaProperty
  146. * @throws MissingElementException
  147. **/
  148. public function getPropertyByName($name)
  149. {
  150. if ($property = $this->safePropertyGet($name))
  151. return $property;
  152. throw new MissingElementException(
  153. get_class($this) . ": unknown property requested by name '{$name}'"
  154. );
  155. }
  156. public function isPropertyExists($name)
  157. {
  158. return $this->safePropertyGet($name) !== null;
  159. }
  160. /**
  161. * @return Form
  162. **/
  163. public function makeForm($prefix = null)
  164. {
  165. $form = Form::create();
  166. foreach ($this->getPropertyList() as $property) {
  167. $property->fillForm($form, $prefix);
  168. }
  169. return $form;
  170. }
  171. /**
  172. * @return InsertOrUpdateQuery
  173. **/
  174. public function fillQuery(
  175. InsertOrUpdateQuery $query,
  176. Prototyped $object,
  177. Prototyped $old = null
  178. )
  179. {
  180. if ($old) {
  181. if ($object instanceof Identifiable) {
  182. Assert::isNotNull($object->getId());
  183. Assert::isTypelessEqual(
  184. $object->getId(), $old->getId(),
  185. 'cannot merge different objects'
  186. );
  187. }
  188. }
  189. foreach ($this->getPropertyList() as $property) {
  190. $property->fillQuery($query, $object, $old);
  191. }
  192. return $query;
  193. }
  194. public function getMapping()
  195. {
  196. static $mappings = array();
  197. $className = get_class($this);
  198. if (!isset($mappings[$className])) {
  199. $mapping = array();
  200. foreach ($this->getPropertyList() as $property) {
  201. $mapping = $property->fillMapping($mapping);
  202. }
  203. $mappings[$className] = $mapping;
  204. }
  205. return $mappings[$className];
  206. }
  207. public function importPrimitive(
  208. $path,
  209. Form $form,
  210. BasePrimitive $prm,
  211. /* Prototyped */ $object,
  212. $ignoreNull = true
  213. )
  214. {
  215. if (strpos($path, ':') !== false) {
  216. return $this->forwardPrimitive(
  217. $path, $form, $prm, $object, $ignoreNull
  218. );
  219. } else {
  220. $property = $this->getPropertyByName($path);
  221. $getter = $property->getGetter();
  222. if (
  223. !$property->isFormless()
  224. && ($property->getFetchStrategyId() == FetchStrategy::LAZY)
  225. && !$object->{$getter.'Id'}()
  226. ) {
  227. return $object;
  228. }
  229. $value = $object->$getter();
  230. if (!$ignoreNull || ($value !== null)) {
  231. $form->importValue($prm->getName(), $value);
  232. }
  233. }
  234. return $object;
  235. }
  236. public function exportPrimitive(
  237. $path,
  238. BasePrimitive $prm,
  239. /* Prototyped */ $object,
  240. $ignoreNull = true
  241. )
  242. {
  243. if (strpos($path, ':') !== false) {
  244. return $this->forwardPrimitive(
  245. $path, null, $prm, $object, $ignoreNull
  246. );
  247. } else {
  248. $property = $this->getPropertyByName($path);
  249. $setter = $property->getSetter();
  250. $value = $prm->getValue();
  251. if (
  252. !$ignoreNull || ($value !== null)
  253. ) {
  254. if ($property->isIdentifier()) {
  255. $value = $value->getId();
  256. }
  257. $dropper = $property->getDropper();
  258. if (
  259. ($value === null)
  260. && method_exists($object, $dropper)
  261. && (
  262. !$property->getRelationId()
  263. || (
  264. $property->getRelationId()
  265. == MetaRelation::ONE_TO_ONE
  266. )
  267. )
  268. ) {
  269. $object->$dropper();
  270. return $object;
  271. } elseif (
  272. (
  273. $property->getRelationId()
  274. == MetaRelation::ONE_TO_MANY
  275. ) || (
  276. $property->getRelationId()
  277. == MetaRelation::MANY_TO_MANY
  278. )
  279. ) {
  280. if ($value === null)
  281. $value = array();
  282. $getter = $property->getGetter();
  283. $object->$getter()->setList($value);
  284. return $object;
  285. }
  286. $object->$setter($value);
  287. }
  288. }
  289. return $object;
  290. }
  291. private static function fetchEncapsulants(Prototyped $object)
  292. {
  293. $proto = $object->proto();
  294. foreach ($proto->getPropertyList() as $property) {
  295. if (
  296. $property->getRelationId() == MetaRelation::ONE_TO_ONE
  297. && ($property->getFetchStrategyId() != FetchStrategy::LAZY)
  298. ) {
  299. $getter = $property->getGetter();
  300. $setter = $property->getSetter();
  301. if (($inner = $object->$getter()) instanceof DAOConnected) {
  302. if ($proto->depth)
  303. $proto->storage[$proto->depth][$setter][] = $inner;
  304. else
  305. $object->$setter(
  306. $inner->dao()->getById(
  307. $inner->getId()
  308. )
  309. );
  310. } elseif (
  311. $proto->depth
  312. // emulating 'instanceof DAOConnected'
  313. && method_exists($property->getClassName(), 'dao')
  314. )
  315. $proto->storage[$proto->depth][$setter][] = null;
  316. }
  317. }
  318. return $object;
  319. }
  320. private static function assemblyObject(
  321. Prototyped $object, $array, $prefix = null
  322. )
  323. {
  324. if ($object instanceof DAOConnected)
  325. $dao = $object->dao();
  326. else
  327. $dao = null;
  328. $proto = $object->proto();
  329. foreach ($proto->getPropertyList() as $property) {
  330. $setter = $property->getSetter();
  331. if ($property instanceof InnerMetaProperty) {
  332. $object->$setter(
  333. $property->toValue($dao, $array, $prefix)
  334. );
  335. } elseif ($property->isBuildable($array, $prefix)) {
  336. if ($property->getRelationId() == MetaRelation::ONE_TO_ONE) {
  337. if (
  338. $property->getFetchStrategyId()
  339. == FetchStrategy::LAZY
  340. ) {
  341. $columnName = $prefix.$property->getColumnName();
  342. $object->
  343. {$setter.'Id'}($array[$columnName]);
  344. continue;
  345. }
  346. }
  347. $object->$setter($property->toValue($dao, $array, $prefix));
  348. }
  349. }
  350. return $object;
  351. }
  352. private function forwardPrimitive(
  353. $path,
  354. Form $form = null,
  355. BasePrimitive $prm,
  356. /* Prototyped */ $object,
  357. $ignoreNull = true
  358. )
  359. {
  360. list($propertyName, $path) = explode(':', $path, 2);
  361. $property = $this->getPropertyByName($propertyName);
  362. Assert::isTrue($property instanceof InnerMetaProperty);
  363. $getter = $property->getGetter();
  364. if ($form)
  365. return $property->getProto()->importPrimitive(
  366. $path, $form, $prm, $object->$getter(), $ignoreNull
  367. );
  368. else
  369. return $property->getProto()->exportPrimitive(
  370. $path, $prm, $object->$getter(), $ignoreNull
  371. );
  372. }
  373. private function safePropertyGet($name)
  374. {
  375. $list = $this->getPropertyList();
  376. if (isset($list[$name]))
  377. return $list[$name];
  378. return null;
  379. }
  380. }
  381. ?>