MongoDbProfilerStorage.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpKernel\Profiler;
  11. class MongoDbProfilerStorage implements ProfilerStorageInterface
  12. {
  13. protected $dsn;
  14. protected $lifetime;
  15. private $mongo;
  16. /**
  17. * Constructor.
  18. *
  19. * @param string $dsn A data source name
  20. * @param string $username Not used
  21. * @param string $password Not used
  22. * @param integer $lifetime The lifetime to use for the purge
  23. */
  24. public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
  25. {
  26. $this->dsn = $dsn;
  27. $this->lifetime = (int) $lifetime;
  28. }
  29. /**
  30. * {@inheritdoc}
  31. */
  32. public function find($ip, $url, $limit, $method, $start = null, $end = null)
  33. {
  34. $cursor = $this->getMongo()->find($this->buildQuery($ip, $url, $method, $start, $end), array('_id', 'parent', 'ip', 'method', 'url', 'time'))->sort(array('time' => -1))->limit($limit);
  35. $tokens = array();
  36. foreach ($cursor as $profile) {
  37. $tokens[] = $this->getData($profile);
  38. }
  39. return $tokens;
  40. }
  41. /**
  42. * {@inheritdoc}
  43. */
  44. public function purge()
  45. {
  46. $this->getMongo()->remove(array());
  47. }
  48. /**
  49. * {@inheritdoc}
  50. */
  51. public function read($token)
  52. {
  53. $profile = $this->getMongo()->findOne(array('_id' => $token, 'data' => array('$exists' => true)));
  54. if (null !== $profile) {
  55. $profile = $this->createProfileFromData($this->getData($profile));
  56. }
  57. return $profile;
  58. }
  59. /**
  60. * {@inheritdoc}
  61. */
  62. public function write(Profile $profile)
  63. {
  64. $this->cleanup();
  65. $record = array(
  66. '_id' => $profile->getToken(),
  67. 'parent' => $profile->getParentToken(),
  68. 'data' => base64_encode(serialize($profile->getCollectors())),
  69. 'ip' => $profile->getIp(),
  70. 'method' => $profile->getMethod(),
  71. 'url' => $profile->getUrl(),
  72. 'time' => $profile->getTime()
  73. );
  74. $result = $this->getMongo()->update(array('_id' => $profile->getToken()), array_filter($record, function ($v) { return !empty($v); }), array('upsert' => true));
  75. return (boolean) (isset($result['ok']) ? $result['ok'] : $result);
  76. }
  77. /**
  78. * Internal convenience method that returns the instance of the MongoDB Collection
  79. *
  80. * @return \MongoCollection
  81. *
  82. * @throws \RuntimeException
  83. */
  84. protected function getMongo()
  85. {
  86. if ($this->mongo === null) {
  87. if (preg_match('#^(mongodb://.*)/(.*)/(.*)$#', $this->dsn, $matches)) {
  88. $server = $matches[1].(!empty($matches[2]) ? '/'.$matches[2] : '');
  89. $database = $matches[2];
  90. $collection = $matches[3];
  91. $mongoClass = (version_compare(phpversion('mongo'), '1.3.0', '<')) ? '\Mongo' : '\MongoClient';
  92. $mongo = new $mongoClass($server);
  93. $this->mongo = $mongo->selectCollection($database, $collection);
  94. } else {
  95. throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use MongoDB with an invalid dsn "%s". The expected format is "mongodb://[user:pass@]host/database/collection"', $this->dsn));
  96. }
  97. }
  98. return $this->mongo;
  99. }
  100. /**
  101. * @param array $data
  102. *
  103. * @return Profile
  104. */
  105. protected function createProfileFromData(array $data)
  106. {
  107. $profile = $this->getProfile($data);
  108. if ($data['parent']) {
  109. $parent = $this->getMongo()->findOne(array('_id' => $data['parent'], 'data' => array('$exists' => true)));
  110. if ($parent) {
  111. $profile->setParent($this->getProfile($this->getData($parent)));
  112. }
  113. }
  114. $profile->setChildren($this->readChildren($data['token']));
  115. return $profile;
  116. }
  117. /**
  118. * @param string $token
  119. *
  120. * @return Profile[] An array of Profile instances
  121. */
  122. protected function readChildren($token)
  123. {
  124. $profiles = array();
  125. $cursor = $this->getMongo()->find(array('parent' => $token, 'data' => array('$exists' => true)));
  126. foreach ($cursor as $d) {
  127. $profiles[] = $this->getProfile($this->getData($d));
  128. }
  129. return $profiles;
  130. }
  131. protected function cleanup()
  132. {
  133. $this->getMongo()->remove(array('time' => array('$lt' => time() - $this->lifetime)));
  134. }
  135. /**
  136. * @param string $ip
  137. * @param string $url
  138. * @param string $method
  139. * @param int $start
  140. * @param int $end
  141. *
  142. * @return array
  143. */
  144. private function buildQuery($ip, $url, $method, $start, $end)
  145. {
  146. $query = array();
  147. if (!empty($ip)) {
  148. $query['ip'] = $ip;
  149. }
  150. if (!empty($url)) {
  151. $query['url'] = $url;
  152. }
  153. if (!empty($method)) {
  154. $query['method'] = $method;
  155. }
  156. if (!empty($start) || !empty($end)) {
  157. $query['time'] = array();
  158. }
  159. if (!empty($start)) {
  160. $query['time']['$gte'] = $start;
  161. }
  162. if (!empty($end)) {
  163. $query['time']['$lte'] = $end;
  164. }
  165. return $query;
  166. }
  167. /**
  168. * @param array $data
  169. *
  170. * @return array
  171. */
  172. private function getData(array $data)
  173. {
  174. return array(
  175. 'token' => $data['_id'],
  176. 'parent' => isset($data['parent']) ? $data['parent'] : null,
  177. 'ip' => isset($data['ip']) ? $data['ip'] : null,
  178. 'method' => isset($data['method']) ? $data['method'] : null,
  179. 'url' => isset($data['url']) ? $data['url'] : null,
  180. 'time' => isset($data['time']) ? $data['time'] : null,
  181. 'data' => isset($data['data']) ? $data['data'] : null,
  182. );
  183. }
  184. /**
  185. * @param array $data
  186. *
  187. * @return Profile
  188. */
  189. private function getProfile(array $data)
  190. {
  191. $profile = new Profile($data['token']);
  192. $profile->setIp($data['ip']);
  193. $profile->setMethod($data['method']);
  194. $profile->setUrl($data['url']);
  195. $profile->setTime($data['time']);
  196. $profile->setCollectors(unserialize(base64_decode($data['data'])));
  197. return $profile;
  198. }
  199. }