GearmanCacheWrapper.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. /**
  3. * Gearman Bundle for Symfony2
  4. *
  5. * @author Marc Morera <yuhu@mmoreram.com>
  6. * @since 2013
  7. */
  8. namespace Mmoreram\GearmanBundle\Service;
  9. use Symfony\Component\Config\FileLocator;
  10. use Doctrine\Common\Annotations\AnnotationReader;
  11. use Doctrine\Common\Annotations\AnnotationRegistry;
  12. use Symfony\Component\HttpKernel\Kernel;
  13. use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
  14. use Doctrine\Common\Cache\Cache;
  15. use Symfony\Component\Finder\Finder;
  16. use Mmoreram\GearmanBundle\Module\WorkerCollection;
  17. use Mmoreram\GearmanBundle\Module\WorkerDirectoryLoader;
  18. use Mmoreram\GearmanBundle\Module\WorkerClass as Worker;
  19. use Mmoreram\GearmanBundle\Driver\Gearman\Work as WorkAnnotation;
  20. use ReflectionClass;
  21. /**
  22. * Gearman cache loader class
  23. *
  24. * @author Marc Morera <yuhu@mmoreram.com>
  25. */
  26. class GearmanCacheWrapper
  27. {
  28. /**
  29. * Bundles loaded by kernel
  30. *
  31. * @var Array
  32. */
  33. private $kernelBundles;
  34. /**
  35. * Bundles available to perform search
  36. *
  37. * @var Array
  38. */
  39. private $bundles;
  40. /**
  41. * @var Array
  42. *
  43. * paths to search on
  44. */
  45. private $paths = array();
  46. /**
  47. * @var Array
  48. *
  49. * paths to ignore
  50. */
  51. private $excludedPaths = array();
  52. /**
  53. * @var GearmanCache
  54. *
  55. * Gearman Cache
  56. */
  57. private $cache;
  58. /**
  59. * @var string
  60. *
  61. * Gearman cache id
  62. */
  63. private $cacheId;
  64. /**
  65. * @var array
  66. *
  67. * WorkerCollection with all workers and jobs available
  68. */
  69. private $workerCollection;
  70. /**
  71. * @var array
  72. *
  73. * Collection of servers to connect
  74. */
  75. private $servers;
  76. /**
  77. * @var array
  78. *
  79. * Default settings defined by user in config.yml
  80. */
  81. private $defaultSettings;
  82. /**
  83. * Return workerCollection
  84. *
  85. * @return array all available workers
  86. */
  87. public function getWorkers()
  88. {
  89. return $this->workerCollection;
  90. }
  91. /**
  92. * Construct method
  93. *
  94. * @param Kernel $kernel Kernel instance
  95. * @param Cache $cache Cache
  96. * @param string $cacheId Cache id where to save parsing data
  97. * @param array $bundles Bundle array where to parse workers, defined on condiguration
  98. * @param array $servers Server list defined on configuration
  99. * @param array $defaultSettings Default settings defined on configuration
  100. */
  101. public function __construct(Kernel $kernel, Cache $cache, $cacheId, array $bundles, array $servers, array $defaultSettings)
  102. {
  103. $this->kernelBundles = $kernel->getBundles();
  104. $this->bundles = $bundles;
  105. $this->cache = $cache;
  106. $this->cacheId = $cacheId;
  107. $this->servers = $servers;
  108. $this->defaultSettings = $defaultSettings;
  109. }
  110. /**
  111. * loads Gearman cache, only if is not loaded yet
  112. *
  113. * @return GearmanCacheLoader self Object
  114. */
  115. public function load()
  116. {
  117. if ($this->cache->contains($this->cacheId)) {
  118. $this->workerCollection = $this->cache->get($this->cacheId);
  119. } else {
  120. $this->workerCollection = $this->parseNamespaceMap()->toArray();
  121. $this->cache->save($this->cacheId, $this->workerCollection);
  122. }
  123. return $this;
  124. }
  125. /**
  126. * flush all cache
  127. *
  128. * @return GearmanCacheLoader self Object
  129. */
  130. public function flush()
  131. {
  132. $this->cache->delete($this->cacheId);
  133. return $this;
  134. }
  135. /**
  136. * Return Gearman bundle settings, previously loaded by method load()
  137. * If settings are not loaded, a SettingsNotLoadedException Exception is thrown
  138. *
  139. * @return array Bundles that gearman will be able to search annotations
  140. */
  141. public function loadNamespaceMap()
  142. {
  143. /**
  144. * Iteratinc all bundle settings
  145. */
  146. foreach ($this->bundles as $bundleSettings) {
  147. if (!$bundleSettings['active']) {
  148. break;
  149. }
  150. $bundleNamespace = $bundleSettings['name'];
  151. $bundlePath = $this->kernelBundles[$bundleNamespace]->getPath();
  152. if (!empty($bundleSettings['include'])) {
  153. foreach ($bundleSettings['include'] as $include) {
  154. $this->paths[] = rtrim(rtrim($bundlePath, '/') . '/' . $include, '/') . '/';
  155. }
  156. } else {
  157. /**
  158. * If no include is set, include all namespace
  159. */
  160. $this->paths[] = rtrim($bundlePath, '/') . '/';
  161. }
  162. foreach ($bundleSettings['ignore'] as $ignore) {
  163. $this->excludedPaths[] = trim($ignore, '/');
  164. }
  165. }
  166. }
  167. /**
  168. * Perform a parsing inside all namespace map
  169. *
  170. * @return WorkerCollection collection of all info
  171. */
  172. private function parseNamespaceMap()
  173. {
  174. AnnotationRegistry::registerFile(__DIR__ . "/../Driver/Gearman/Work.php");
  175. AnnotationRegistry::registerFile(__DIR__ . "/../Driver/Gearman/Job.php");
  176. /**
  177. * Depending on Symfony2 version
  178. */
  179. if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
  180. $reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader();
  181. $reader->addNamespace('Mmoreram\GearmanBundle\Driver');
  182. } else {
  183. $reader = new AnnotationReader();
  184. $reader->setDefaultAnnotationNamespace('Mmoreram\GearmanBundle\Driver\\');
  185. }
  186. $workerCollection = new WorkerCollection;
  187. $finder = new Finder();
  188. $finder
  189. ->files()
  190. ->followLinks()
  191. ->exclude($this->excludedPaths)
  192. ->in($this->paths);
  193. /**
  194. * Every file found is parsed
  195. */
  196. foreach ($finder as $file) {
  197. /**
  198. * File is accepted to be parsed
  199. */
  200. $classNamespace = $this->getFileClassNamespace($file->getRealpath());
  201. $reflClass = new ReflectionClass($classNamespace);
  202. $classAnnotations = $reader->getClassAnnotations($reflClass);
  203. /**
  204. * Every annotation found is parsed
  205. */
  206. foreach ($classAnnotations as $annot) {
  207. /**
  208. * Annotation is only laoded if is typeof WorkAnnotation
  209. */
  210. if ($annot instanceof WorkAnnotation) {
  211. /**
  212. * Creates new Worker element with all its Job data
  213. */
  214. $worker = new Worker($annot, $reflClass, $reader, $this->servers, $this->defaultSettings);
  215. $workerCollection->add($worker);
  216. }
  217. }
  218. }
  219. return $workerCollection;
  220. }
  221. /**
  222. * Returns the full class name for the first class in the file.
  223. *
  224. * @param string $file A PHP file path
  225. *
  226. * @return string|false Full class name if found, false otherwise
  227. */
  228. protected function getFileClassNamespace($file)
  229. {
  230. $class = false;
  231. $namespace = false;
  232. $tokens = token_get_all(file_get_contents($file));
  233. for ($i = 0, $count = count($tokens); $i < $count; $i++) {
  234. $token = $tokens[$i];
  235. if (!is_array($token)) {
  236. continue;
  237. }
  238. if (true === $class && T_STRING === $token[0]) {
  239. return $namespace.'\\'.$token[1];
  240. }
  241. if (true === $namespace && T_STRING === $token[0]) {
  242. $namespace = '';
  243. do {
  244. $namespace .= $token[1];
  245. $token = $tokens[++$i];
  246. } while ($i < $count && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING)));
  247. }
  248. if (T_CLASS === $token[0]) {
  249. $class = true;
  250. }
  251. if (T_NAMESPACE === $token[0]) {
  252. $namespace = true;
  253. }
  254. }
  255. return false;
  256. }
  257. }