PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php

https://github.com/weaverryan/mongodb-odm
PHP | 283 lines | 199 code | 22 blank | 62 comment | 11 complexity | 70b9a9b8db0a87df57ba2589abbc67a3 MD5 | raw file
  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\ODM\MongoDB\Proxy;
  20. use Doctrine\ODM\MongoDB\DocumentManager,
  21. Doctrine\ODM\MongoDB\Mapping\ClassMetadata,
  22. Doctrine\ODM\MongoDB\Mapping\AssociationMapping;
  23. /**
  24. * This factory is used to create proxy objects for documents at runtime.
  25. *
  26. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  27. * @since 1.0
  28. * @author Jonathan H. Wage <jonwage@gmail.com>
  29. * @author Roman Borschel <roman@code-factory.org>
  30. * @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
  31. */
  32. class ProxyFactory
  33. {
  34. /** The DocumentManager this factory is bound to. */
  35. private $dm;
  36. /** Whether to automatically (re)generate proxy classes. */
  37. private $autoGenerate;
  38. /** The namespace that contains all proxy classes. */
  39. private $proxyNamespace;
  40. /** The directory that contains all proxy classes. */
  41. private $proxyDir;
  42. /**
  43. * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
  44. * connected to the given <tt>DocumentManager</tt>.
  45. *
  46. * @param DocumentManager $dm The DocumentManager the new factory works for.
  47. * @param string $proxyDir The directory to use for the proxy classes. It must exist.
  48. * @param string $proxyNs The namespace to use for the proxy classes.
  49. * @param boolean $autoGenerate Whether to automatically generate proxy classes.
  50. */
  51. public function __construct(DocumentManager $dm, $proxyDir, $proxyNs, $autoGenerate = false)
  52. {
  53. if ( ! $proxyDir) {
  54. throw ProxyException::proxyDirectoryRequired();
  55. }
  56. if ( ! $proxyNs) {
  57. throw ProxyException::proxyNamespaceRequired();
  58. }
  59. $this->dm = $dm;
  60. $this->proxyDir = $proxyDir;
  61. $this->autoGenerate = $autoGenerate;
  62. $this->proxyNamespace = $proxyNs;
  63. }
  64. /**
  65. * Gets a reference proxy instance for the document of the given type and identified by
  66. * the given identifier.
  67. *
  68. * @param string $className
  69. * @param mixed $identifier
  70. * @return object
  71. */
  72. public function getProxy($className, $identifier)
  73. {
  74. $proxyClassName = str_replace('\\', '', $className) . 'Proxy';
  75. $fqn = $this->proxyNamespace . '\\' . $proxyClassName;
  76. if ($this->autoGenerate && ! class_exists($fqn, false)) {
  77. $fileName = $this->proxyDir . DIRECTORY_SEPARATOR . $proxyClassName . '.php';
  78. $this->generateProxyClass($this->dm->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate);
  79. require $fileName;
  80. }
  81. if ( ! $this->dm->getMetadataFactory()->hasMetadataFor($fqn)) {
  82. $this->dm->getMetadataFactory()->setMetadataFor($fqn, $this->dm->getClassMetadata($className));
  83. }
  84. return new $fqn($this->dm, $identifier);
  85. }
  86. /**
  87. * Generates proxy classes for all given classes.
  88. *
  89. * @param array $classes The classes (ClassMetadata instances) for which to generate proxies.
  90. * @param string $toDir The target directory of the proxy classes. If not specified, the
  91. * directory configured on the Configuration of the DocumentManager used
  92. * by this factory is used.
  93. */
  94. public function generateProxyClasses(array $classes, $toDir = null)
  95. {
  96. $proxyDir = $toDir ?: $this->proxyDir;
  97. $proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
  98. foreach ($classes as $class) {
  99. $proxyClassName = str_replace('\\', '', $class->name) . 'Proxy';
  100. $proxyFileName = $proxyDir . $proxyClassName . '.php';
  101. $this->generateProxyClass($class, $proxyClassName, $proxyFileName, self::$_proxyClassTemplate);
  102. }
  103. }
  104. /**
  105. * Generates a proxy class file.
  106. *
  107. * @param $class
  108. * @param $originalClassName
  109. * @param $proxyClassName
  110. * @param $file The path of the file to write to.
  111. */
  112. private function generateProxyClass($class, $proxyClassName, $fileName, $file)
  113. {
  114. $methods = $this->generateMethods($class);
  115. $sleepImpl = $this->generateSleep($class);
  116. $placeholders = array(
  117. '<namespace>',
  118. '<proxyClassName>', '<className>',
  119. '<methods>', '<sleepImpl>'
  120. );
  121. if(substr($class->name, 0, 1) == "\\") {
  122. $className = substr($class->name, 1);
  123. } else {
  124. $className = $class->name;
  125. }
  126. $replacements = array(
  127. $this->proxyNamespace,
  128. $proxyClassName, $className,
  129. $methods, $sleepImpl
  130. );
  131. $file = str_replace($placeholders, $replacements, $file);
  132. file_put_contents($fileName, $file);
  133. }
  134. /**
  135. * Generates the methods of a proxy class.
  136. *
  137. * @param ClassMetadata $class
  138. * @return string The code of the generated methods.
  139. */
  140. private function generateMethods(ClassMetadata $class)
  141. {
  142. $methods = '';
  143. foreach ($class->reflClass->getMethods() as $method) {
  144. /* @var $method ReflectionMethod */
  145. if ($method->isConstructor() || strtolower($method->getName()) == "__sleep") {
  146. continue;
  147. }
  148. if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
  149. $methods .= PHP_EOL . ' public function ' . $method->getName() . '(';
  150. $firstParam = true;
  151. $parameterString = $argumentString = '';
  152. foreach ($method->getParameters() as $param) {
  153. if ($firstParam) {
  154. $firstParam = false;
  155. } else {
  156. $parameterString .= ', ';
  157. $argumentString .= ', ';
  158. }
  159. // We need to pick the type hint class too
  160. if (($paramClass = $param->getClass()) !== null) {
  161. $parameterString .= '\\' . $paramClass->getName() . ' ';
  162. } elseif ($param->isArray()) {
  163. $parameterString .= 'array ';
  164. }
  165. if ($param->isPassedByReference()) {
  166. $parameterString .= '&';
  167. }
  168. $parameterString .= '$' . $param->getName();
  169. $argumentString .= '$' . $param->getName();
  170. if ($param->isDefaultValueAvailable()) {
  171. $parameterString .= ' = ' . var_export($param->getDefaultValue(), true);
  172. }
  173. }
  174. $methods .= $parameterString . ')';
  175. $methods .= PHP_EOL . ' {' . PHP_EOL;
  176. $methods .= ' $this->load();' . PHP_EOL;
  177. $methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
  178. $methods .= PHP_EOL . ' }' . PHP_EOL;
  179. }
  180. }
  181. return $methods;
  182. }
  183. /**
  184. * Generates the code for the __sleep method for a proxy class.
  185. *
  186. * @param $class
  187. * @return string
  188. */
  189. private function generateSleep(ClassMetadata $class)
  190. {
  191. $sleepImpl = '';
  192. if ($class->reflClass->hasMethod('__sleep')) {
  193. $sleepImpl .= 'return parent::__sleep();';
  194. } else {
  195. $sleepImpl .= 'return array(';
  196. $first = true;
  197. foreach ($class->getReflectionProperties() as $name => $prop) {
  198. if ($first) {
  199. $first = false;
  200. } else {
  201. $sleepImpl .= ', ';
  202. }
  203. $sleepImpl .= "'" . $name . "'";
  204. }
  205. $sleepImpl .= ');';
  206. }
  207. return $sleepImpl;
  208. }
  209. /** Proxy class code template */
  210. private static $_proxyClassTemplate =
  211. '<?php
  212. namespace <namespace>;
  213. /**
  214. * THIS CLASS WAS GENERATED BY THE DOCTRINE ODM. DO NOT EDIT THIS FILE.
  215. */
  216. class <proxyClassName> extends \<className> implements \Doctrine\ODM\MongoDB\Proxy\Proxy
  217. {
  218. public $__dm;
  219. public $__identifier;
  220. public $__isInitialized__ = false;
  221. public function __construct($dm, $identifier)
  222. {
  223. $this->__dm = $dm;
  224. $this->__identifier = $identifier;
  225. $this->__dm->getClassMetadata(__CLASS__)->setIdentifierValue($this, $this->__identifier);
  226. }
  227. private function load()
  228. {
  229. if ( ! $this->__isInitialized__ && $this->__dm) {
  230. $this->__isInitialized__ = true;
  231. if ($this->__dm->loadByID(get_class($this), $this->__identifier) === null) {
  232. throw \Doctrine\ODM\MongoDB\MongoDBException::documentNotFound(get_class($this), $this->__identifier);
  233. }
  234. unset($this->__dm);
  235. unset($this->__identifier);
  236. }
  237. }
  238. <methods>
  239. public function __sleep()
  240. {
  241. <sleepImpl>
  242. }
  243. }';
  244. }