MongoDbProfilerStorage.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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. @trigger_error('The '.__NAMESPACE__.'\MongoDbProfilerStorage class is deprecated since Symfony 2.8 and will be removed in 3.0. Use FileProfilerStorage instead.', E_USER_DEPRECATED);
  12. /**
  13. * @deprecated Deprecated since Symfony 2.8, to be removed in Symfony 3.0.
  14. * Use {@link FileProfilerStorage} instead.
  15. */
  16. class MongoDbProfilerStorage implements ProfilerStorageInterface
  17. {
  18. protected $dsn;
  19. protected $lifetime;
  20. private $mongo;
  21. /**
  22. * @param string $dsn A data source name
  23. * @param string $username Not used
  24. * @param string $password Not used
  25. * @param int $lifetime The lifetime to use for the purge
  26. */
  27. public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
  28. {
  29. $this->dsn = $dsn;
  30. $this->lifetime = (int) $lifetime;
  31. }
  32. /**
  33. * {@inheritdoc}
  34. */
  35. public function find($ip, $url, $limit, $method, $start = null, $end = null)
  36. {
  37. $cursor = $this->getMongo()->find($this->buildQuery($ip, $url, $method, $start, $end), array('_id', 'parent', 'ip', 'method', 'url', 'time', 'status_code'))->sort(array('time' => -1))->limit($limit);
  38. $tokens = array();
  39. foreach ($cursor as $profile) {
  40. $tokens[] = $this->getData($profile);
  41. }
  42. return $tokens;
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function purge()
  48. {
  49. $this->getMongo()->remove(array());
  50. }
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public function read($token)
  55. {
  56. $profile = $this->getMongo()->findOne(array('_id' => $token, 'data' => array('$exists' => true)));
  57. if (null !== $profile) {
  58. $profile = $this->createProfileFromData($this->getData($profile));
  59. }
  60. return $profile;
  61. }
  62. /**
  63. * {@inheritdoc}
  64. */
  65. public function write(Profile $profile)
  66. {
  67. $this->cleanup();
  68. $record = array(
  69. '_id' => $profile->getToken(),
  70. 'parent' => $profile->getParentToken(),
  71. 'data' => base64_encode(serialize($profile->getCollectors())),
  72. 'ip' => $profile->getIp(),
  73. 'method' => $profile->getMethod(),
  74. 'url' => $profile->getUrl(),
  75. 'time' => $profile->getTime(),
  76. 'status_code' => $profile->getStatusCode(),
  77. );
  78. $result = $this->getMongo()->update(array('_id' => $profile->getToken()), array_filter($record, function ($v) { return !empty($v); }), array('upsert' => true));
  79. return (bool) (isset($result['ok']) ? $result['ok'] : $result);
  80. }
  81. /**
  82. * Internal convenience method that returns the instance of the MongoDB Collection.
  83. *
  84. * @return \MongoCollection
  85. *
  86. * @throws \RuntimeException
  87. */
  88. protected function getMongo()
  89. {
  90. if (null !== $this->mongo) {
  91. return $this->mongo;
  92. }
  93. if (!$parsedDsn = $this->parseDsn($this->dsn)) {
  94. 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));
  95. }
  96. list($server, $database, $collection) = $parsedDsn;
  97. $mongoClass = version_compare(phpversion('mongo'), '1.3.0', '<') ? '\Mongo' : '\MongoClient';
  98. $mongo = new $mongoClass($server);
  99. return $this->mongo = $mongo->selectCollection($database, $collection);
  100. }
  101. /**
  102. * @return Profile
  103. */
  104. protected function createProfileFromData(array $data)
  105. {
  106. $profile = $this->getProfile($data);
  107. if ($data['parent']) {
  108. $parent = $this->getMongo()->findOne(array('_id' => $data['parent'], 'data' => array('$exists' => true)));
  109. if ($parent) {
  110. $profile->setParent($this->getProfile($this->getData($parent)));
  111. }
  112. }
  113. $profile->setChildren($this->readChildren($data['token']));
  114. return $profile;
  115. }
  116. /**
  117. * @param string $token
  118. *
  119. * @return Profile[] An array of Profile instances
  120. */
  121. protected function readChildren($token)
  122. {
  123. $profiles = array();
  124. $cursor = $this->getMongo()->find(array('parent' => $token, 'data' => array('$exists' => true)));
  125. foreach ($cursor as $d) {
  126. $profiles[] = $this->getProfile($this->getData($d));
  127. }
  128. return $profiles;
  129. }
  130. protected function cleanup()
  131. {
  132. $this->getMongo()->remove(array('time' => array('$lt' => time() - $this->lifetime)));
  133. }
  134. /**
  135. * @param string $ip
  136. * @param string $url
  137. * @param string $method
  138. * @param int $start
  139. * @param int $end
  140. *
  141. * @return array
  142. */
  143. private function buildQuery($ip, $url, $method, $start, $end)
  144. {
  145. $query = array();
  146. if (!empty($ip)) {
  147. $query['ip'] = $ip;
  148. }
  149. if (!empty($url)) {
  150. $query['url'] = $url;
  151. }
  152. if (!empty($method)) {
  153. $query['method'] = $method;
  154. }
  155. if (!empty($start) || !empty($end)) {
  156. $query['time'] = array();
  157. }
  158. if (!empty($start)) {
  159. $query['time']['$gte'] = $start;
  160. }
  161. if (!empty($end)) {
  162. $query['time']['$lte'] = $end;
  163. }
  164. return $query;
  165. }
  166. /**
  167. * @return array
  168. */
  169. private function getData(array $data)
  170. {
  171. return array(
  172. 'token' => $data['_id'],
  173. 'parent' => isset($data['parent']) ? $data['parent'] : null,
  174. 'ip' => isset($data['ip']) ? $data['ip'] : null,
  175. 'method' => isset($data['method']) ? $data['method'] : null,
  176. 'url' => isset($data['url']) ? $data['url'] : null,
  177. 'time' => isset($data['time']) ? $data['time'] : null,
  178. 'data' => isset($data['data']) ? $data['data'] : null,
  179. 'status_code' => isset($data['status_code']) ? $data['status_code'] : null,
  180. );
  181. }
  182. /**
  183. * @return Profile
  184. */
  185. private function getProfile(array $data)
  186. {
  187. $profile = new Profile($data['token']);
  188. $profile->setIp($data['ip']);
  189. $profile->setMethod($data['method']);
  190. $profile->setUrl($data['url']);
  191. $profile->setTime($data['time']);
  192. $profile->setCollectors(unserialize(base64_decode($data['data'])));
  193. return $profile;
  194. }
  195. /**
  196. * @param string $dsn
  197. *
  198. * @return array|null Array($server, $database, $collection)
  199. */
  200. private function parseDsn($dsn)
  201. {
  202. if (!preg_match('#^(mongodb://.*)/(.*)/(.*)$#', $dsn, $matches)) {
  203. return;
  204. }
  205. $server = $matches[1];
  206. $database = $matches[2];
  207. $collection = $matches[3];
  208. preg_match('#^mongodb://(([^:]+):?(.*)(?=@))?@?([^/]*)(.*)$#', $server, $matchesServer);
  209. if ('' == $matchesServer[5] && '' != $matches[2]) {
  210. $server .= '/'.$matches[2];
  211. }
  212. return array($server, $database, $collection);
  213. }
  214. }