Completed
Push — v1.5 ( 24344c...740eda )
by Andy
13:19
created

ChromePhp::_getPropertyKey()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 8
nop 1
dl 0
loc 14
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright 2010-2013 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
namespace UsabilityDynamics\AMD {
18
  /**
19
   * Server Side Chrome PHP debugger class
20
   *
21
   * @package ChromePhp
22
   * @author Craig Campbell <[email protected]>
23
   */
24
  class ChromePhp {
25
    /**
26
     * @var string
27
     */
28
    const VERSION = '4.1.0';
29
30
    /**
31
     * @var string
32
     */
33
    const HEADER_NAME = 'X-ChromeLogger-Data';
34
35
    /**
36
     * @var string
37
     */
38
    const BACKTRACE_LEVEL = 'backtrace_level';
39
40
    /**
41
     * @var string
42
     */
43
    const LOG = 'log';
44
45
    /**
46
     * @var string
47
     */
48
    const WARN = 'warn';
49
50
    /**
51
     * @var string
52
     */
53
    const ERROR = 'error';
54
55
    /**
56
     * @var string
57
     */
58
    const GROUP = 'group';
59
60
    /**
61
     * @var string
62
     */
63
    const INFO = 'info';
64
65
    /**
66
     * @var string
67
     */
68
    const GROUP_END = 'groupEnd';
69
70
    /**
71
     * @var string
72
     */
73
    const GROUP_COLLAPSED = 'groupCollapsed';
74
75
    /**
76
     * @var string
77
     */
78
    const TABLE = 'table';
79
80
    /**
81
     * @var string
82
     */
83
    protected $_php_version;
84
85
    /**
86
     * @var int
87
     */
88
    protected $_timestamp;
89
90
    /**
91
     * @var array
92
     */
93
    protected $_json = array(
94
      'version' => self::VERSION,
95
      'columns' => array( 'log', 'backtrace', 'type' ),
96
      'rows' => array()
97
    );
98
99
    /**
100
     * @var array
101
     */
102
    protected $_backtraces = array();
103
104
    /**
105
     * @var bool
106
     */
107
    protected $_error_triggered = false;
108
109
    /**
110
     * @var array
111
     */
112
    protected $_settings = array(
113
      self::BACKTRACE_LEVEL => 1
114
    );
115
116
    /**
117
     * @var ChromePhp
118
     */
119
    protected static $_instance;
120
121
    /**
122
     * Prevent recursion when working with objects referring to each other
123
     *
124
     * @var array
125
     */
126
    protected $_processed = array();
127
128
    /**
129
     * constructor
130
     */
131
    private function __construct() {
132
      $this->_php_version = phpversion();
133
      $this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER[ 'REQUEST_TIME' ] : time();
134
      $this->_json[ 'request_uri' ] = $_SERVER[ 'REQUEST_URI' ];
135
    }
136
137
    /**
138
     * gets instance of this class
139
     *
140
     * @return ChromePhp
141
     */
142
    public static function getInstance() {
143
      if( self::$_instance === null ) {
144
        self::$_instance = new self();
145
      }
146
      return self::$_instance;
147
    }
148
149
    /**
150
     * logs a variable to the console
151
     *
152
     * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
0 ignored issues
show
Bug introduced by
There is no parameter named $data,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
153
     * @return void
154
     */
155
    public static function log() {
156
      $args = func_get_args();
157
      return self::_log( '', $args );
158
    }
159
160
    /**
161
     * logs a warning to the console
162
     *
163
     * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
0 ignored issues
show
Bug introduced by
There is no parameter named $data,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
164
     * @return void
165
     */
166
    public static function warn() {
167
      $args = func_get_args();
168
      return self::_log( self::WARN, $args );
169
    }
170
171
    /**
172
     * logs an error to the console
173
     *
174
     * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
0 ignored issues
show
Bug introduced by
There is no parameter named $data,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
175
     * @return void
176
     */
177
    public static function error() {
178
      $args = func_get_args();
179
      return self::_log( self::ERROR, $args );
180
    }
181
182
    /**
183
     * sends a group log
184
     *
185
     * @param string value
186
     */
187
    public static function group() {
188
      $args = func_get_args();
189
      return self::_log( self::GROUP, $args );
190
    }
191
192
    /**
193
     * sends an info log
194
     *
195
     * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
0 ignored issues
show
Bug introduced by
There is no parameter named $data,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
196
     * @return void
197
     */
198
    public static function info() {
199
      $args = func_get_args();
200
      return self::_log( self::INFO, $args );
201
    }
202
203
    /**
204
     * sends a collapsed group log
205
     *
206
     * @param string value
207
     */
208
    public static function groupCollapsed() {
209
      $args = func_get_args();
210
      return self::_log( self::GROUP_COLLAPSED, $args );
211
    }
212
213
    /**
214
     * ends a group log
215
     *
216
     * @param string value
217
     */
218
    public static function groupEnd() {
219
      $args = func_get_args();
220
      return self::_log( self::GROUP_END, $args );
221
    }
222
223
    /**
224
     * sends a table log
225
     *
226
     * @param string value
227
     */
228
    public static function table() {
229
      $args = func_get_args();
230
      return self::_log( self::TABLE, $args );
231
    }
232
233
    /**
234
     * internal logging call
235
     *
236
     * @param string $type
237
     * @return void
238
     */
239
    protected static function _log( $type, array $args ) {
240
      // nothing passed in, don't do anything
241
      if( count( $args ) == 0 && $type != self::GROUP_END ) {
242
        return;
243
      }
244
245
      $logger = self::getInstance();
246
247
      $logger->_processed = array();
248
249
      $logs = array();
250
      foreach( $args as $arg ) {
251
        $logs[] = $logger->_convert( $arg );
252
      }
253
254
      $backtrace = debug_backtrace( false );
255
      $level = $logger->getSetting( self::BACKTRACE_LEVEL );
256
257
      $backtrace_message = 'unknown';
258
      if( isset( $backtrace[ $level ][ 'file' ] ) && isset( $backtrace[ $level ][ 'line' ] ) ) {
259
        $backtrace_message = $backtrace[ $level ][ 'file' ] . ' : ' . $backtrace[ $level ][ 'line' ];
260
      }
261
262
      $logger->_addRow( $logs, $backtrace_message, $type );
263
    }
264
265
    /**
266
     * converts an object to a better format for logging
267
     *
268
     * @param Object
269
     * @return array
270
     */
271
    protected function _convert( $object ) {
272
      // if this isn't an object then just return it
273
      if( !is_object( $object ) ) {
274
        return $object;
275
      }
276
277
      //Mark this object as processed so we don't convert it twice and it
278
      //Also avoid recursion when objects refer to each other
279
      $this->_processed[] = $object;
280
281
      $object_as_array = array();
282
283
      // first add the class name
284
      $object_as_array[ '___class_name' ] = get_class( $object );
285
286
      // loop through object vars
287
      $object_vars = get_object_vars( $object );
288
      foreach( $object_vars as $key => $value ) {
289
290
        // same instance as parent object
291 View Code Duplication
        if( $value === $object || in_array( $value, $this->_processed, true ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
292
          $value = 'recursion - parent object [' . get_class( $value ) . ']';
293
        }
294
        $object_as_array[ $key ] = $this->_convert( $value );
295
      }
296
297
      $reflection = new ReflectionClass( $object );
298
299
      // loop through the properties and add those
300
      foreach( $reflection->getProperties() as $property ) {
301
302
        // if one of these properties was already added above then ignore it
303
        if( array_key_exists( $property->getName(), $object_vars ) ) {
304
          continue;
305
        }
306
        $type = $this->_getPropertyKey( $property );
307
308
        if( $this->_php_version >= 5.3 ) {
309
          $property->setAccessible( true );
310
        }
311
312
        try {
313
          $value = $property->getValue( $object );
314
        } catch ( ReflectionException $e ) {
0 ignored issues
show
Bug introduced by
The class UsabilityDynamics\AMD\ReflectionException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
315
          $value = 'only PHP 5.3 can access private/protected properties';
316
        }
317
318
        // same instance as parent object
319 View Code Duplication
        if( $value === $object || in_array( $value, $this->_processed, true ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
          $value = 'recursion - parent object [' . get_class( $value ) . ']';
321
        }
322
323
        $object_as_array[ $type ] = $this->_convert( $value );
324
      }
325
      return $object_as_array;
326
    }
327
328
    /**
329
     * takes a reflection property and returns a nicely formatted key of the property name
330
     *
331
     * @param ReflectionProperty
332
     * @return string
333
     */
334
    protected function _getPropertyKey( ReflectionProperty $property ) {
335
      $static = $property->isStatic() ? ' static' : '';
336
      if( $property->isPublic() ) {
337
        return 'public' . $static . ' ' . $property->getName();
338
      }
339
340
      if( $property->isProtected() ) {
341
        return 'protected' . $static . ' ' . $property->getName();
342
      }
343
344
      if( $property->isPrivate() ) {
345
        return 'private' . $static . ' ' . $property->getName();
346
      }
347
    }
348
349
    /**
350
     * adds a value to the data array
351
     *
352
     * @var mixed
353
     * @return void
354
     */
355
    protected function _addRow( array $logs, $backtrace, $type ) {
356
      // if this is logged on the same line for example in a loop, set it to null to save space
357
      if( in_array( $backtrace, $this->_backtraces ) ) {
358
        $backtrace = null;
359
      }
360
361
      // for group, groupEnd, and groupCollapsed
362
      // take out the backtrace since it is not useful
363
      if( $type == self::GROUP || $type == self::GROUP_END || $type == self::GROUP_COLLAPSED ) {
364
        $backtrace = null;
365
      }
366
367
      if( $backtrace !== null ) {
368
        $this->_backtraces[] = $backtrace;
369
      }
370
371
      $row = array( $logs, $backtrace, $type );
372
373
      $this->_json[ 'rows' ][] = $row;
374
      $this->_writeHeader( $this->_json );
375
    }
376
377
    protected function _writeHeader( $data ) {
378
      header( self::HEADER_NAME . ': ' . $this->_encode( $data ) );
379
    }
380
381
    /**
382
     * encodes the data to be sent along with the request
383
     *
384
     * @param array $data
385
     * @return string
386
     */
387
    protected function _encode( $data ) {
388
      return base64_encode( utf8_encode( json_encode( $data ) ) );
389
    }
390
391
    /**
392
     * adds a setting
393
     *
394
     * @param string key
395
     * @param mixed value
396
     * @return void
397
     */
398
    public function addSetting( $key, $value ) {
399
      $this->_settings[ $key ] = $value;
400
    }
401
402
    /**
403
     * add ability to set multiple settings in one call
404
     *
405
     * @param array $settings
406
     * @return void
407
     */
408
    public function addSettings( array $settings ) {
409
      foreach( $settings as $key => $value ) {
410
        $this->addSetting( $key, $value );
411
      }
412
    }
413
414
    /**
415
     * gets a setting
416
     *
417
     * @param string key
418
     * @return mixed
419
     */
420
    public function getSetting( $key ) {
421
      if( !isset( $this->_settings[ $key ] ) ) {
422
        return null;
423
      }
424
      return $this->_settings[ $key ];
425
    }
426
  }
427
}