GearmanCacheWrapper.php 7.9 KB

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