PageRenderTime 41ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 0ms

/analysis/src/lib/php/ChromePhp.php

https://github.com/cazzerson/Suma
PHP | 437 lines | 184 code | 63 blank | 190 comment | 25 complexity | 6cfd158a43f93c23d622ff101d85d84f MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright 2012 Craig Campbell
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * Server Side Chrome PHP debugger class
  19. *
  20. * @package ChromePhp
  21. * @author Craig Campbell <iamcraigcampbell@gmail.com>
  22. */
  23. class ChromePhp
  24. {
  25. /**
  26. * @var string
  27. */
  28. const VERSION = '3.0';
  29. /**
  30. * @var string
  31. */
  32. const HEADER_NAME = 'X-ChromePhp-Data';
  33. /**
  34. * @var string
  35. */
  36. const BACKTRACE_LEVEL = 'backtrace_level';
  37. /**
  38. * @var string
  39. */
  40. const LOG = 'log';
  41. /**
  42. * @var string
  43. */
  44. const WARN = 'warn';
  45. /**
  46. * @var string
  47. */
  48. const ERROR = 'error';
  49. /**
  50. * @var string
  51. */
  52. const GROUP = 'group';
  53. /**
  54. * @var string
  55. */
  56. const INFO = 'info';
  57. /**
  58. * @var string
  59. */
  60. const GROUP_END = 'groupEnd';
  61. /**
  62. * @var string
  63. */
  64. const GROUP_COLLAPSED = 'groupCollapsed';
  65. /**
  66. * @var string
  67. */
  68. protected $_php_version;
  69. /**
  70. * @var int
  71. */
  72. protected $_timestamp;
  73. /**
  74. * @var array
  75. */
  76. protected $_json = array(
  77. 'version' => self::VERSION,
  78. 'columns' => array('label', 'log', 'backtrace', 'type'),
  79. 'rows' => array()
  80. );
  81. /**
  82. * @var array
  83. */
  84. protected $_backtraces = array();
  85. /**
  86. * @var bool
  87. */
  88. protected $_error_triggered = false;
  89. /**
  90. * @var array
  91. */
  92. protected $_settings = array(
  93. self::BACKTRACE_LEVEL => 1
  94. );
  95. /**
  96. * @var ChromePhp
  97. */
  98. protected static $_instance;
  99. /**
  100. * Prevent recursion when working with objects referring to each other
  101. *
  102. * @var array
  103. */
  104. protected $_processed = array();
  105. /**
  106. * constructor
  107. */
  108. private function __construct()
  109. {
  110. $this->_php_version = phpversion();
  111. $this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER['REQUEST_TIME'] : time();
  112. $this->_json['request_uri'] = $_SERVER['REQUEST_URI'];
  113. }
  114. /**
  115. * gets instance of this class
  116. *
  117. * @return ChromePhp
  118. */
  119. public static function getInstance()
  120. {
  121. if (self::$_instance === null) {
  122. self::$_instance = new ChromePhp();
  123. }
  124. return self::$_instance;
  125. }
  126. /**
  127. * logs a variable to the console
  128. *
  129. * @param string label
  130. * @param mixed value
  131. * @param string severity ChromePhp::LOG || ChromePhp::WARN || ChromePhp::ERROR
  132. * @return void
  133. */
  134. public static function log()
  135. {
  136. $args = func_get_args();
  137. $severity = count($args) == 3 ? array_pop($args) : '';
  138. // save precious bytes
  139. if ($severity == self::LOG) {
  140. $severity = '';
  141. }
  142. return self::_log($args + array('type' => $severity));
  143. }
  144. /**
  145. * logs a warning to the console
  146. *
  147. * @param string label
  148. * @param mixed value
  149. * @return void
  150. */
  151. public static function warn()
  152. {
  153. return self::_log(func_get_args() + array('type' => self::WARN));
  154. }
  155. /**
  156. * logs an error to the console
  157. *
  158. * @param string label
  159. * @param mixed value
  160. * @return void
  161. */
  162. public static function error()
  163. {
  164. return self::_log(func_get_args() + array('type' => self::ERROR));
  165. }
  166. /**
  167. * sends a group log
  168. *
  169. * @param string value
  170. */
  171. public static function group()
  172. {
  173. return self::_log(func_get_args() + array('type' => self::GROUP));
  174. }
  175. /**
  176. * sends an info log
  177. *
  178. * @param string value
  179. */
  180. public static function info()
  181. {
  182. return self::_log(func_get_args() + array('type' => self::INFO));
  183. }
  184. /**
  185. * sends a collapsed group log
  186. *
  187. * @param string value
  188. */
  189. public static function groupCollapsed()
  190. {
  191. return self::_log(func_get_args() + array('type' => self::GROUP_COLLAPSED));
  192. }
  193. /**
  194. * ends a group log
  195. *
  196. * @param string value
  197. */
  198. public static function groupEnd()
  199. {
  200. return self::_log(func_get_args() + array('type' => self::GROUP_END));
  201. }
  202. /**
  203. * internal logging call
  204. *
  205. * @param string $type
  206. * @return void
  207. */
  208. protected static function _log(array $args)
  209. {
  210. $type = $args['type'];
  211. unset($args['type']);
  212. // nothing passed in, don't do anything
  213. if (count($args) == 0 && $type != self::GROUP_END) {
  214. return;
  215. }
  216. // default to single
  217. $label = null;
  218. $value = isset($args[0]) ? $args[0] : '';
  219. $logger = self::getInstance();
  220. // if there are two values passed in then the first one is the label
  221. if (count($args) == 2) {
  222. $label = $args[0];
  223. $value = $args[1];
  224. }
  225. $logger->_processed = array();
  226. $value = $logger->_convert($value);
  227. $backtrace = debug_backtrace(false);
  228. $level = $logger->getSetting(self::BACKTRACE_LEVEL);
  229. $backtrace_message = 'unknown';
  230. if (isset($backtrace[$level]['file']) && isset($backtrace[$level]['line'])) {
  231. $backtrace_message = $backtrace[$level]['file'] . ' : ' . $backtrace[$level]['line'];
  232. }
  233. $logger->_addRow($label, $value, $backtrace_message, $type);
  234. }
  235. /**
  236. * converts an object to a better format for logging
  237. *
  238. * @param Object
  239. * @return array
  240. */
  241. protected function _convert($object)
  242. {
  243. // if this isn't an object then just return it
  244. if (!is_object($object)) {
  245. return $object;
  246. }
  247. //Mark this object as processed so we don't convert it twice and it
  248. //Also avoid recursion when objects refer to each other
  249. $this->_processed[] = $object;
  250. $object_as_array = array();
  251. // first add the class name
  252. $object_as_array['___class_name'] = get_class($object);
  253. // loop through object vars
  254. $object_vars = get_object_vars($object);
  255. foreach ($object_vars as $key => $value) {
  256. // same instance as parent object
  257. if ($value === $object || in_array($value, $this->_processed, true)) {
  258. $value = 'recursion - parent object [' . get_class($value) . ']';
  259. }
  260. $object_as_array[$key] = $this->_convert($value);
  261. }
  262. $reflection = new ReflectionClass($object);
  263. // loop through the properties and add those
  264. foreach ($reflection->getProperties() as $property) {
  265. // if one of these properties was already added above then ignore it
  266. if (array_key_exists($property->getName(), $object_vars)) {
  267. continue;
  268. }
  269. $type = $this->_getPropertyKey($property);
  270. if ($this->_php_version >= 5.3) {
  271. $property->setAccessible(true);
  272. }
  273. try {
  274. $value = $property->getValue($object);
  275. } catch (ReflectionException $e) {
  276. $value = 'only PHP 5.3 can access private/protected properties';
  277. }
  278. // same instance as parent object
  279. if ($value === $object || in_array($value, $this->_processed, true)) {
  280. $value = 'recursion - parent object [' . get_class($value) . ']';
  281. }
  282. $object_as_array[$type] = $this->_convert($value);
  283. }
  284. return $object_as_array;
  285. }
  286. /**
  287. * takes a reflection property and returns a nicely formatted key of the property name
  288. *
  289. * @param ReflectionProperty
  290. * @return string
  291. */
  292. protected function _getPropertyKey(ReflectionProperty $property)
  293. {
  294. $static = $property->isStatic() ? ' static' : '';
  295. if ($property->isPublic()) {
  296. return 'public' . $static . ' ' . $property->getName();
  297. }
  298. if ($property->isProtected()) {
  299. return 'protected' . $static . ' ' . $property->getName();
  300. }
  301. if ($property->isPrivate()) {
  302. return 'private' . $static . ' ' . $property->getName();
  303. }
  304. }
  305. /**
  306. * adds a value to the data array
  307. *
  308. * @var mixed
  309. * @return void
  310. */
  311. protected function _addRow($label, $log, $backtrace, $type)
  312. {
  313. // if this is logged on the same line for example in a loop, set it to null to save space
  314. if (in_array($backtrace, $this->_backtraces)) {
  315. $backtrace = null;
  316. }
  317. if ($backtrace !== null) {
  318. $this->_backtraces[] = $backtrace;
  319. }
  320. $row = array($label, $log, $backtrace, $type);
  321. $this->_json['rows'][] = $row;
  322. $this->_writeHeader($this->_json);
  323. }
  324. protected function _writeHeader($data)
  325. {
  326. header(self::HEADER_NAME . ': ' . $this->_encode($data));
  327. }
  328. /**
  329. * encodes the data to be sent along with the request
  330. *
  331. * @param array $data
  332. * @return string
  333. */
  334. protected function _encode($data)
  335. {
  336. return base64_encode(utf8_encode(json_encode($data)));
  337. }
  338. /**
  339. * adds a setting
  340. *
  341. * @param string key
  342. * @param mixed value
  343. * @return void
  344. */
  345. public function addSetting($key, $value)
  346. {
  347. $this->_settings[$key] = $value;
  348. }
  349. /**
  350. * add ability to set multiple settings in one call
  351. *
  352. * @param array $settings
  353. * @return void
  354. */
  355. public function addSettings(array $settings)
  356. {
  357. foreach ($settings as $key => $value) {
  358. $this->addSetting($key, $value);
  359. }
  360. }
  361. /**
  362. * gets a setting
  363. *
  364. * @param string key
  365. * @return mixed
  366. */
  367. public function getSetting($key)
  368. {
  369. if (!isset($this->_settings[$key])) {
  370. return null;
  371. }
  372. return $this->_settings[$key];
  373. }
  374. }