├── composer.json ├── README.md └── ChromePhp.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ccampbell/chromephp", 3 | "type": "library", 4 | "description": "Log variables to the Chrome console (via Chrome Logger Google Chrome extension).", 5 | "keywords": ["log","logging"], 6 | "homepage": "http://github.com/ccampbell/chromephp", 7 | "license": "Apache-2.0", 8 | "authors": [ 9 | { 10 | "name": "Craig Campbell", 11 | "email": "iamcraigcampbell@gmail.com", 12 | "homepage": "http://craig.is", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.0.0" 18 | }, 19 | "autoload": { 20 | "psr-0": { 21 | "ChromePhp": "" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | ChromePhp is a PHP library for the Chrome Logger Google Chrome extension. 3 | 4 | This library allows you to log variables to the Chrome console. 5 | 6 | ## Requirements 7 | - PHP 5 or later 8 | 9 | ## Installation 10 | 1. Install the Chrome extension from: https://chrome.google.com/extensions/detail/noaneddfkdjfnfdakjjmocngnfkfehhd 11 | 2. Click the extension icon in the browser to enable it for the current tab's domain 12 | 3. Put ChromePhp.php somewhere in your PHP include path 13 | 4. Log some data 14 | 15 | ```php 16 | include 'ChromePhp.php'; 17 | ChromePhp::log('Hello console!'); 18 | ChromePhp::log($_SERVER); 19 | ChromePhp::warn('something went wrong!'); 20 | ``` 21 | 22 | More information can be found here: 23 | http://www.chromelogger.com 24 | -------------------------------------------------------------------------------- /ChromePhp.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | class ChromePhp 25 | { 26 | /** 27 | * @var string 28 | */ 29 | const VERSION = '4.1.0'; 30 | 31 | /** 32 | * @var string 33 | */ 34 | const HEADER_NAME = 'X-ChromeLogger-Data'; 35 | 36 | /** 37 | * @var string 38 | */ 39 | const BACKTRACE_LEVEL = 'backtrace_level'; 40 | 41 | /** 42 | * @var string 43 | */ 44 | const LOG = 'log'; 45 | 46 | /** 47 | * @var string 48 | */ 49 | const WARN = 'warn'; 50 | 51 | /** 52 | * @var string 53 | */ 54 | const ERROR = 'error'; 55 | 56 | /** 57 | * @var string 58 | */ 59 | const GROUP = 'group'; 60 | 61 | /** 62 | * @var string 63 | */ 64 | const INFO = 'info'; 65 | 66 | /** 67 | * @var string 68 | */ 69 | const GROUP_END = 'groupEnd'; 70 | 71 | /** 72 | * @var string 73 | */ 74 | const GROUP_COLLAPSED = 'groupCollapsed'; 75 | 76 | /** 77 | * @var string 78 | */ 79 | const TABLE = 'table'; 80 | 81 | /** 82 | * @var string 83 | */ 84 | protected $_php_version; 85 | 86 | /** 87 | * @var int 88 | */ 89 | protected $_timestamp; 90 | 91 | /** 92 | * @var array 93 | */ 94 | protected $_json = array( 95 | 'version' => self::VERSION, 96 | 'columns' => array('log', 'backtrace', 'type'), 97 | 'rows' => array() 98 | ); 99 | 100 | /** 101 | * @var array 102 | */ 103 | protected $_backtraces = array(); 104 | 105 | /** 106 | * @var bool 107 | */ 108 | protected $_error_triggered = false; 109 | 110 | /** 111 | * @var array 112 | */ 113 | protected $_settings = array( 114 | self::BACKTRACE_LEVEL => 1 115 | ); 116 | 117 | /** 118 | * @var ChromePhp 119 | */ 120 | protected static $_instance; 121 | 122 | /** 123 | * Prevent recursion when working with objects referring to each other 124 | * 125 | * @var array 126 | */ 127 | protected $_processed = array(); 128 | 129 | /** 130 | * constructor 131 | */ 132 | private function __construct() 133 | { 134 | $this->_php_version = phpversion(); 135 | $this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER['REQUEST_TIME'] : time(); 136 | $this->_json['request_uri'] = $_SERVER['REQUEST_URI']; 137 | } 138 | 139 | /** 140 | * gets instance of this class 141 | * 142 | * @return ChromePhp 143 | */ 144 | public static function getInstance() 145 | { 146 | if (self::$_instance === null) { 147 | self::$_instance = new self(); 148 | } 149 | return self::$_instance; 150 | } 151 | 152 | /** 153 | * logs a variable to the console 154 | * 155 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...] 156 | * @return void 157 | */ 158 | public static function log() 159 | { 160 | $args = func_get_args(); 161 | return self::_log('', $args); 162 | } 163 | 164 | /** 165 | * logs a warning to the console 166 | * 167 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...] 168 | * @return void 169 | */ 170 | public static function warn() 171 | { 172 | $args = func_get_args(); 173 | return self::_log(self::WARN, $args); 174 | } 175 | 176 | /** 177 | * logs an error to the console 178 | * 179 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...] 180 | * @return void 181 | */ 182 | public static function error() 183 | { 184 | $args = func_get_args(); 185 | return self::_log(self::ERROR, $args); 186 | } 187 | 188 | /** 189 | * sends a group log 190 | * 191 | * @param string value 192 | */ 193 | public static function group() 194 | { 195 | $args = func_get_args(); 196 | return self::_log(self::GROUP, $args); 197 | } 198 | 199 | /** 200 | * sends an info log 201 | * 202 | * @param mixed $data,... unlimited OPTIONAL number of additional logs [...] 203 | * @return void 204 | */ 205 | public static function info() 206 | { 207 | $args = func_get_args(); 208 | return self::_log(self::INFO, $args); 209 | } 210 | 211 | /** 212 | * sends a collapsed group log 213 | * 214 | * @param string value 215 | */ 216 | public static function groupCollapsed() 217 | { 218 | $args = func_get_args(); 219 | return self::_log(self::GROUP_COLLAPSED, $args); 220 | } 221 | 222 | /** 223 | * ends a group log 224 | * 225 | * @param string value 226 | */ 227 | public static function groupEnd() 228 | { 229 | $args = func_get_args(); 230 | return self::_log(self::GROUP_END, $args); 231 | } 232 | 233 | /** 234 | * sends a table log 235 | * 236 | * @param string value 237 | */ 238 | public static function table() 239 | { 240 | $args = func_get_args(); 241 | return self::_log(self::TABLE, $args); 242 | } 243 | 244 | /** 245 | * internal logging call 246 | * 247 | * @param string $type 248 | * @return void 249 | */ 250 | protected static function _log($type, array $args) 251 | { 252 | // nothing passed in, don't do anything 253 | if (count($args) == 0 && $type != self::GROUP_END) { 254 | return; 255 | } 256 | 257 | $logger = self::getInstance(); 258 | 259 | $logger->_processed = array(); 260 | 261 | $logs = array(); 262 | foreach ($args as $arg) { 263 | $logs[] = $logger->_convert($arg); 264 | } 265 | 266 | $backtrace = debug_backtrace(false); 267 | $level = $logger->getSetting(self::BACKTRACE_LEVEL); 268 | 269 | $backtrace_message = 'unknown'; 270 | if (isset($backtrace[$level]['file']) && isset($backtrace[$level]['line'])) { 271 | $backtrace_message = $backtrace[$level]['file'] . ' : ' . $backtrace[$level]['line']; 272 | } 273 | 274 | $logger->_addRow($logs, $backtrace_message, $type); 275 | } 276 | 277 | /** 278 | * converts an object to a better format for logging 279 | * 280 | * @param Object 281 | * @return array 282 | */ 283 | protected function _convert($object) 284 | { 285 | // if this isn't an object then just return it 286 | if (!is_object($object)) { 287 | return $object; 288 | } 289 | 290 | //Mark this object as processed so we don't convert it twice and it 291 | //Also avoid recursion when objects refer to each other 292 | $this->_processed[] = $object; 293 | 294 | $object_as_array = array(); 295 | 296 | // first add the class name 297 | $object_as_array['___class_name'] = get_class($object); 298 | 299 | // loop through object vars 300 | $object_vars = get_object_vars($object); 301 | foreach ($object_vars as $key => $value) { 302 | 303 | // same instance as parent object 304 | if ($value === $object || in_array($value, $this->_processed, true)) { 305 | $value = 'recursion - parent object [' . get_class($value) . ']'; 306 | } 307 | $object_as_array[$key] = $this->_convert($value); 308 | } 309 | 310 | $reflection = new ReflectionClass($object); 311 | 312 | // loop through the properties and add those 313 | foreach ($reflection->getProperties() as $property) { 314 | 315 | // if one of these properties was already added above then ignore it 316 | if (array_key_exists($property->getName(), $object_vars)) { 317 | continue; 318 | } 319 | $type = $this->_getPropertyKey($property); 320 | 321 | if ($this->_php_version >= 5.3) { 322 | $property->setAccessible(true); 323 | } 324 | 325 | try { 326 | $value = $property->getValue($object); 327 | } catch (ReflectionException $e) { 328 | $value = 'only PHP 5.3 can access private/protected properties'; 329 | } 330 | 331 | // same instance as parent object 332 | if ($value === $object || in_array($value, $this->_processed, true)) { 333 | $value = 'recursion - parent object [' . get_class($value) . ']'; 334 | } 335 | 336 | $object_as_array[$type] = $this->_convert($value); 337 | } 338 | return $object_as_array; 339 | } 340 | 341 | /** 342 | * takes a reflection property and returns a nicely formatted key of the property name 343 | * 344 | * @param ReflectionProperty 345 | * @return string 346 | */ 347 | protected function _getPropertyKey(ReflectionProperty $property) 348 | { 349 | $static = $property->isStatic() ? ' static' : ''; 350 | if ($property->isPublic()) { 351 | return 'public' . $static . ' ' . $property->getName(); 352 | } 353 | 354 | if ($property->isProtected()) { 355 | return 'protected' . $static . ' ' . $property->getName(); 356 | } 357 | 358 | if ($property->isPrivate()) { 359 | return 'private' . $static . ' ' . $property->getName(); 360 | } 361 | } 362 | 363 | /** 364 | * adds a value to the data array 365 | * 366 | * @var mixed 367 | * @return void 368 | */ 369 | protected function _addRow(array $logs, $backtrace, $type) 370 | { 371 | // if this is logged on the same line for example in a loop, set it to null to save space 372 | if (in_array($backtrace, $this->_backtraces)) { 373 | $backtrace = null; 374 | } 375 | 376 | // for group, groupEnd, and groupCollapsed 377 | // take out the backtrace since it is not useful 378 | if ($type == self::GROUP || $type == self::GROUP_END || $type == self::GROUP_COLLAPSED) { 379 | $backtrace = null; 380 | } 381 | 382 | if ($backtrace !== null) { 383 | $this->_backtraces[] = $backtrace; 384 | } 385 | 386 | $row = array($logs, $backtrace, $type); 387 | 388 | $this->_json['rows'][] = $row; 389 | $this->_writeHeader($this->_json); 390 | } 391 | 392 | protected function _writeHeader($data) 393 | { 394 | header(self::HEADER_NAME . ': ' . $this->_encode($data)); 395 | } 396 | 397 | /** 398 | * encodes the data to be sent along with the request 399 | * 400 | * @param array $data 401 | * @return string 402 | */ 403 | protected function _encode($data) 404 | { 405 | return base64_encode(utf8_encode(json_encode($data))); 406 | } 407 | 408 | /** 409 | * adds a setting 410 | * 411 | * @param string key 412 | * @param mixed value 413 | * @return void 414 | */ 415 | public function addSetting($key, $value) 416 | { 417 | $this->_settings[$key] = $value; 418 | } 419 | 420 | /** 421 | * add ability to set multiple settings in one call 422 | * 423 | * @param array $settings 424 | * @return void 425 | */ 426 | public function addSettings(array $settings) 427 | { 428 | foreach ($settings as $key => $value) { 429 | $this->addSetting($key, $value); 430 | } 431 | } 432 | 433 | /** 434 | * gets a setting 435 | * 436 | * @param string key 437 | * @return mixed 438 | */ 439 | public function getSetting($key) 440 | { 441 | if (!isset($this->_settings[$key])) { 442 | return null; 443 | } 444 | return $this->_settings[$key]; 445 | } 446 | } 447 | --------------------------------------------------------------------------------