PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/http-kernel/Symfony/Component/HttpKernel/Profiler/RedisProfilerStorage.php

https://gitlab.com/xolotsoft/pumasruiz
PHP | 390 lines | 219 code | 74 blank | 97 comment | 40 complexity | d035413059f96912e413667138224646 MD5 | raw file
  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. /**
  12. * RedisProfilerStorage stores profiling information in Redis.
  13. *
  14. * @author Andrej Hudec <pulzarraider@gmail.com>
  15. * @author Stephane PY <py.stephane1@gmail.com>
  16. */
  17. class RedisProfilerStorage implements ProfilerStorageInterface
  18. {
  19. const TOKEN_PREFIX = 'sf_profiler_';
  20. const REDIS_OPT_SERIALIZER = 1;
  21. const REDIS_OPT_PREFIX = 2;
  22. const REDIS_SERIALIZER_NONE = 0;
  23. const REDIS_SERIALIZER_PHP = 1;
  24. protected $dsn;
  25. protected $lifetime;
  26. /**
  27. * @var \Redis
  28. */
  29. private $redis;
  30. /**
  31. * Constructor.
  32. *
  33. * @param string $dsn A data source name
  34. * @param string $username Not used
  35. * @param string $password Not used
  36. * @param int $lifetime The lifetime to use for the purge
  37. */
  38. public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
  39. {
  40. $this->dsn = $dsn;
  41. $this->lifetime = (int) $lifetime;
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function find($ip, $url, $limit, $method, $start = null, $end = null)
  47. {
  48. $indexName = $this->getIndexName();
  49. if (!$indexContent = $this->getValue($indexName, self::REDIS_SERIALIZER_NONE)) {
  50. return array();
  51. }
  52. $profileList = array_reverse(explode("\n", $indexContent));
  53. $result = array();
  54. foreach ($profileList as $item) {
  55. if ($limit === 0) {
  56. break;
  57. }
  58. if ($item == '') {
  59. continue;
  60. }
  61. list($itemToken, $itemIp, $itemMethod, $itemUrl, $itemTime, $itemParent) = explode("\t", $item, 6);
  62. $itemTime = (int) $itemTime;
  63. if ($ip && false === strpos($itemIp, $ip) || $url && false === strpos($itemUrl, $url) || $method && false === strpos($itemMethod, $method)) {
  64. continue;
  65. }
  66. if (!empty($start) && $itemTime < $start) {
  67. continue;
  68. }
  69. if (!empty($end) && $itemTime > $end) {
  70. continue;
  71. }
  72. $result[] = array(
  73. 'token' => $itemToken,
  74. 'ip' => $itemIp,
  75. 'method' => $itemMethod,
  76. 'url' => $itemUrl,
  77. 'time' => $itemTime,
  78. 'parent' => $itemParent,
  79. );
  80. --$limit;
  81. }
  82. return $result;
  83. }
  84. /**
  85. * {@inheritdoc}
  86. */
  87. public function purge()
  88. {
  89. // delete only items from index
  90. $indexName = $this->getIndexName();
  91. $indexContent = $this->getValue($indexName, self::REDIS_SERIALIZER_NONE);
  92. if (!$indexContent) {
  93. return false;
  94. }
  95. $profileList = explode("\n", $indexContent);
  96. $result = array();
  97. foreach ($profileList as $item) {
  98. if ($item == '') {
  99. continue;
  100. }
  101. if (false !== $pos = strpos($item, "\t")) {
  102. $result[] = $this->getItemName(substr($item, 0, $pos));
  103. }
  104. }
  105. $result[] = $indexName;
  106. return $this->delete($result);
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function read($token)
  112. {
  113. if (empty($token)) {
  114. return false;
  115. }
  116. $profile = $this->getValue($this->getItemName($token), self::REDIS_SERIALIZER_PHP);
  117. if (false !== $profile) {
  118. $profile = $this->createProfileFromData($token, $profile);
  119. }
  120. return $profile;
  121. }
  122. /**
  123. * {@inheritdoc}
  124. */
  125. public function write(Profile $profile)
  126. {
  127. $data = array(
  128. 'token' => $profile->getToken(),
  129. 'parent' => $profile->getParentToken(),
  130. 'children' => array_map(function ($p) { return $p->getToken(); }, $profile->getChildren()),
  131. 'data' => $profile->getCollectors(),
  132. 'ip' => $profile->getIp(),
  133. 'method' => $profile->getMethod(),
  134. 'url' => $profile->getUrl(),
  135. 'time' => $profile->getTime(),
  136. );
  137. $profileIndexed = false !== $this->getValue($this->getItemName($profile->getToken()));
  138. if ($this->setValue($this->getItemName($profile->getToken()), $data, $this->lifetime, self::REDIS_SERIALIZER_PHP)) {
  139. if (!$profileIndexed) {
  140. // Add to index
  141. $indexName = $this->getIndexName();
  142. $indexRow = implode("\t", array(
  143. $profile->getToken(),
  144. $profile->getIp(),
  145. $profile->getMethod(),
  146. $profile->getUrl(),
  147. $profile->getTime(),
  148. $profile->getParentToken(),
  149. ))."\n";
  150. return $this->appendValue($indexName, $indexRow, $this->lifetime);
  151. }
  152. return true;
  153. }
  154. return false;
  155. }
  156. /**
  157. * Internal convenience method that returns the instance of Redis.
  158. *
  159. * @return \Redis
  160. *
  161. * @throws \RuntimeException
  162. */
  163. protected function getRedis()
  164. {
  165. if (null === $this->redis) {
  166. $data = parse_url($this->dsn);
  167. if (false === $data || !isset($data['scheme']) || $data['scheme'] !== 'redis' || !isset($data['host']) || !isset($data['port'])) {
  168. throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Redis with an invalid dsn "%s". The minimal expected format is "redis://[host]:port".', $this->dsn));
  169. }
  170. if (!extension_loaded('redis')) {
  171. throw new \RuntimeException('RedisProfilerStorage requires that the redis extension is loaded.');
  172. }
  173. $redis = new \Redis();
  174. $redis->connect($data['host'], $data['port']);
  175. if (isset($data['path'])) {
  176. $redis->select(substr($data['path'], 1));
  177. }
  178. if (isset($data['pass'])) {
  179. $redis->auth($data['pass']);
  180. }
  181. $redis->setOption(self::REDIS_OPT_PREFIX, self::TOKEN_PREFIX);
  182. $this->redis = $redis;
  183. }
  184. return $this->redis;
  185. }
  186. /**
  187. * Set instance of the Redis.
  188. *
  189. * @param \Redis $redis
  190. */
  191. public function setRedis($redis)
  192. {
  193. $this->redis = $redis;
  194. }
  195. private function createProfileFromData($token, $data, $parent = null)
  196. {
  197. $profile = new Profile($token);
  198. $profile->setIp($data['ip']);
  199. $profile->setMethod($data['method']);
  200. $profile->setUrl($data['url']);
  201. $profile->setTime($data['time']);
  202. $profile->setCollectors($data['data']);
  203. if (!$parent && $data['parent']) {
  204. $parent = $this->read($data['parent']);
  205. }
  206. if ($parent) {
  207. $profile->setParent($parent);
  208. }
  209. foreach ($data['children'] as $token) {
  210. if (!$token) {
  211. continue;
  212. }
  213. if (!$childProfileData = $this->getValue($this->getItemName($token), self::REDIS_SERIALIZER_PHP)) {
  214. continue;
  215. }
  216. $profile->addChild($this->createProfileFromData($token, $childProfileData, $profile));
  217. }
  218. return $profile;
  219. }
  220. /**
  221. * Gets the item name.
  222. *
  223. * @param string $token
  224. *
  225. * @return string
  226. */
  227. private function getItemName($token)
  228. {
  229. $name = $token;
  230. if ($this->isItemNameValid($name)) {
  231. return $name;
  232. }
  233. return false;
  234. }
  235. /**
  236. * Gets the name of the index.
  237. *
  238. * @return string
  239. */
  240. private function getIndexName()
  241. {
  242. $name = 'index';
  243. if ($this->isItemNameValid($name)) {
  244. return $name;
  245. }
  246. return false;
  247. }
  248. private function isItemNameValid($name)
  249. {
  250. $length = strlen($name);
  251. if ($length > 2147483648) {
  252. throw new \RuntimeException(sprintf('The Redis item key "%s" is too long (%s bytes). Allowed maximum size is 2^31 bytes.', $name, $length));
  253. }
  254. return true;
  255. }
  256. /**
  257. * Retrieves an item from the Redis server.
  258. *
  259. * @param string $key
  260. * @param int $serializer
  261. *
  262. * @return mixed
  263. */
  264. private function getValue($key, $serializer = self::REDIS_SERIALIZER_NONE)
  265. {
  266. $redis = $this->getRedis();
  267. $redis->setOption(self::REDIS_OPT_SERIALIZER, $serializer);
  268. return $redis->get($key);
  269. }
  270. /**
  271. * Stores an item on the Redis server under the specified key.
  272. *
  273. * @param string $key
  274. * @param mixed $value
  275. * @param int $expiration
  276. * @param int $serializer
  277. *
  278. * @return bool
  279. */
  280. private function setValue($key, $value, $expiration = 0, $serializer = self::REDIS_SERIALIZER_NONE)
  281. {
  282. $redis = $this->getRedis();
  283. $redis->setOption(self::REDIS_OPT_SERIALIZER, $serializer);
  284. return $redis->setex($key, $expiration, $value);
  285. }
  286. /**
  287. * Appends data to an existing item on the Redis server.
  288. *
  289. * @param string $key
  290. * @param string $value
  291. * @param int $expiration
  292. *
  293. * @return bool
  294. */
  295. private function appendValue($key, $value, $expiration = 0)
  296. {
  297. $redis = $this->getRedis();
  298. $redis->setOption(self::REDIS_OPT_SERIALIZER, self::REDIS_SERIALIZER_NONE);
  299. if ($redis->exists($key)) {
  300. $redis->append($key, $value);
  301. return $redis->setTimeout($key, $expiration);
  302. }
  303. return $redis->setex($key, $expiration, $value);
  304. }
  305. /**
  306. * Removes the specified keys.
  307. *
  308. * @param array $keys
  309. *
  310. * @return bool
  311. */
  312. private function delete(array $keys)
  313. {
  314. return (bool) $this->getRedis()->delete($keys);
  315. }
  316. }