Completed
Branch dev (acf3d0)
by Michael
05:39
created

AdminPageFramework_Debug::_getLegibleCallable()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 1
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * Admin Page Framework
4
 * 
5
 * http://en.michaeluno.jp/admin-page-framework/
6
 * Copyright (c) 2013-2016 Michael Uno; Licensed MIT
7
 * 
8
 */
9
10
/**
11
 * Provides debugging methods.
12
 * 
13
 * Use the methods of this class to check variable contents. 
14
 *
15
 * @image           http://admin-page-framework.michaeluno.jp/image/common/utility/debug.png
16
 * @since           2.0.0
17
 * @since           3.1.3       Extends AdminPageFramework_WPUtility
18
 * @since           3.7.1       Extends AdminPageFramework_FrameworkUtility
19
 * @extends         AdminPageFramework_FrameworkUtility
20
 * @package         AdminPageFramework
21
 * @subpackage      Common/Utility
22
 */
23
class AdminPageFramework_Debug extends AdminPageFramework_FrameworkUtility {
24
            
25
    /**
26
     * Prints out the given variable contents
27
     * 
28
     * If a file pass is given to the second parameter, it saves the output in the file.
29
     * 
30
     * @since       3.2.0
31
     * @remark      An alias of the dumpArray() method.
32
     * @param       array|string    $asArray        The variable to check its contents.
33
     * @param       string          $sFilePath      The file path for a log file.
34
     * @return      void
35
     */
36
    static public function dump( $asArray, $sFilePath=null ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
37
        echo self::get( $asArray, $sFilePath );
38
    }    
39
        /**
40
         * Prints out the given variable contents.
41
         * 
42
         * If a file pass is given, it saves the output in the file.
43
         * 
44
         * @since unknown
45
         * @deprecated      3.2.0
46
         */
47
        static public function dumpArray( $asArray, $sFilePath=null ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
48
            self::dump( $asArray, $sFilePath );
49
        }    
50
    
51
    /**
52
     * Returns a string representation of a given value with details.
53
     * @since       3.8.9
54
     * @return      string
55
     */
56
    static public function getDetails( $mValue, $bEscape=true ) {    
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
57
        $_sValueWithDetails = self::_getArrayRepresentationSanitized(
58
            self::_getLegible( $mValue )
59
        );
60
        return $bEscape
61
            ? "<pre class='dump-array'>" 
62
                    . htmlspecialchars( $_sValueWithDetails ) 
63
                . "</pre>" 
64
            : $_sValueWithDetails; // non-escape is used for exporting data into file.    
65
    }
66
        /**
67
         * @return      string
68
         * @since       3.8.9
69
         */
70
        static private function _getArrayRepresentationSanitized( $sString ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
71
            
72
            // Fix extra line breaks after `Array()`
73
            $sString = preg_replace(
74
                '/\)(\r\n?|\n)(?=(\r\n?|\n)\s+[\[\)])/', // needle                   
75
                ')', // replacement
76
                $sString // subject
77
            );            
78
            
79
            // Fix empty array output 
80
            $sString = preg_replace(
81
                '/Array(\r\n?|\n)\s+\((\r\n?|\n)\s+\)/', // needle   
82
                'Array()', // replacement
83
                $sString // subject
84
            );
85
            return $sString;
86
            
87
        }
88
    /**
89
     * Retrieves the output of the given variable contents.
90
     * 
91
     * If a file pass is given to the second parameter, it saves the output in the file.
92
     * 
93
     * @remark      An alias of getArray() method.
94
     * @since       3.2.0
95
     * @param       array|string    $asArray        The variable to check its contents.
96
     * @param       string          $sFilePath      The file path for a log file.
97
     * @param       boolean         $bEscape        Whether to escape characters.
98
     */
99
    static public function get( $asArray, $sFilePath=null, $bEscape=true ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
100
101
        if ( $sFilePath ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sFilePath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
102
            self::log( $asArray, $sFilePath );     
103
        }
104
        
105
        return $bEscape
106
            ? "<pre class='dump-array'>" 
107
                    . htmlspecialchars( self::getAsString( $asArray ) ) // `esc_html()` breaks with complex HTML code.
108
                . "</pre>" 
109
            : self::getAsString( $asArray ); // non-escape is used for exporting data into file.    
110
        
111
    }
112
        /**
113
         * Retrieves the output of the given array contents.
114
         * 
115
         * If a file pass is given, it saves the output in the file.
116
         * 
117
         * @since       2.1.6 The $bEncloseInTag parameter is added.
118
         * @since       3.0.0 Changed the $bEncloseInTag parameter to bEscape.
119
         * @deprecated  3.2.0
120
         */
121
        static public function getArray( $asArray, $sFilePath=null, $bEscape=true ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
122
            self::showDeprecationNotice( __CLASS__ .'::' . __FUNCTION__,  __CLASS__ .'::get()' );
123
            return self::get( $asArray, $sFilePath, $bEscape );
124
        }      
125
            
126
    /**
127
     * Logs the given variable output to a file.
128
     * 
129
     * <h4>Example</h4>
130
     * <code>
131
     * $_aValues = array( 'foo', 'bar' );
132
     * AdminPageFramework_Debug::log( $aValues );
133
     * </code>
134
     * 
135
     * @remark      The alias of the `logArray()` method.
136
     * @since       3.1.0
137
     * @since       3.1.3       Made it leave milliseconds and elapsed time from the last call of the method.
138
     * @since       3.3.0       Made it indicate the data type.
139
     * @since       3.3.1       Made it indicate the data length.
140
     * @param       mixed       $mValue         The value to log.  
141
     * @param       string      $sFilePath      The log file path.
142
     * @return      void
143
     **/
144
    static public function log( $mValue, $sFilePath=null ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
145
                
146
        static $_fPreviousTimeStamp = 0;
147
        
148
        $_oCallerInfo       = debug_backtrace();
149
        $_sCallerFunction   = self::getElement(
150
            $_oCallerInfo,  // subject array
151
            array( 1, 'function' ), // key
152
            ''      // default
153
        );                        
154
        $_sCallerClass      = self::getElement(
155
            $_oCallerInfo,  // subject array
156
            array( 1, 'class' ), // key
157
            ''      // default
158
        );           
159
        $_fCurrentTimeStamp = microtime( true );
160
        
161
        file_put_contents( 
162
            self::_getLogFilePath( $sFilePath, $_sCallerClass ), 
163
            self::_getLogHeadingLine( 
164
                $_fCurrentTimeStamp,
165
                round( $_fCurrentTimeStamp - $_fPreviousTimeStamp, 3 ),     // elapsed time
166
                $_sCallerClass,
167
                $_sCallerFunction
168
            ) . PHP_EOL
169
            . self::_getLegible( $mValue ) . PHP_EOL . PHP_EOL,
170
            FILE_APPEND 
171
        );     
172
        
173
        $_fPreviousTimeStamp = $_fCurrentTimeStamp;
174
        
175
    }   
176
        /**
177
         * Determines the log file path.
178
         * @since       3.5.3 
179
         * @internal    
180
         * @return      string      The path of the file to log the contents.
181
         */
182
        static private function _getLogFilePath( $bsFilePath, $sCallerClass ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
183
        
184
            $_bFileExists = self::_createFile( $bsFilePath );
185
            if ( $_bFileExists ) {
186
                return $bsFilePath;
187
            }
188
            // Return a generated default log path.
189
            if ( true === $bsFilePath ) {
190
                return WP_CONTENT_DIR . DIRECTORY_SEPARATOR . basename( get_class() ) . '_' . date( "Ymd" ) . '.log';
191
            }
192
            return WP_CONTENT_DIR . DIRECTORY_SEPARATOR . basename( get_class() ) . '_' . basename( $sCallerClass ) . '_' . date( "Ymd" ) . '.log';
193
            
194
        }
195
            /**
196
             * Creates a file.
197
             * @return      boolean
198
             * @internal
199
             */
200
            static private function _createFile( $sFilePath ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
201
                if ( ! $sFilePath || true === $sFilePath ) {
202
                    return false;
203
                }
204
                if ( file_exists( $sFilePath ) ) {
205
                    return true;
206
                }
207
                // Otherwise, create a file.
208
                $_bhResrouce = fopen( $sFilePath, 'w' );
209
                return ( boolean ) $_bhResrouce;                
210
            }
211
212
        /**
213
         * Returns the heading part of a log item.
214
         * @since       3.5.3
215
         * @internal
216
         * @return      string      the heading part of a log item.
217
         */
218
        static private function _getLogHeadingLine( $fCurrentTimeStamp, $nElapsed, $sCallerClass, $sCallerFunction ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
219
            
220
            static $_iPageLoadID; // identifies the page load.
221
            static $_nGMTOffset;
222
            
223
            $_nGMTOffset        = isset( $_nGMTOffset ) 
224
                ? $_nGMTOffset 
225
                : get_option( 'gmt_offset' );
226
            $_iPageLoadID       = $_iPageLoadID 
227
                ? $_iPageLoadID 
228
                : uniqid();
229
            $_nNow              = $fCurrentTimeStamp + ( $_nGMTOffset * 60 * 60 );
230
            $_nMicroseconds     = str_pad( round( ( $_nNow - floor( $_nNow ) ) * 10000 ), 4, '0' );
231
            
232
            $_aOutput           = array(
233
                date( "Y/m/d H:i:s", $_nNow ) . '.' . $_nMicroseconds,
234
                self::_getFormattedElapsedTime( $nElapsed ),
235
                $_iPageLoadID,
236
                AdminPageFramework_Registry::getVersion(),
237
                $sCallerClass . '::' . $sCallerFunction,
238
                current_filter(),
239
                self::getCurrentURL(),
240
            );
241
            return implode( ' ', $_aOutput );         
242
            
243
        }
244
            /**
245
             * Returns formatted elapsed time.
246
             * @since       3.5.3
247
             * @internal
248
             * @return      string      Formatted elapsed time.
249
             */
250
            static private function _getFormattedElapsedTime( $nElapsed ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
251
                
252
                $_aElapsedParts     = explode( ".", ( string ) $nElapsed );
253
                $_sElapsedFloat     = str_pad(
254
                    self::getElement(
255
                        $_aElapsedParts,  // subject array
256
                        1, // key
257
                        0      // default
258
                    ),      
259
                    3, 
260
                    '0'
261
                );
262
                $_sElapsed          = self::getElement(
263
                    $_aElapsedParts,  // subject array
264
                    0,  // key
265
                    0   // default
266
                );                                   
267
                $_sElapsed          = strlen( $_sElapsed ) > 1 
268
                    ? '+' . substr( $_sElapsed, -1, 2 ) 
269
                    : ' ' . $_sElapsed;
270
                return $_sElapsed . '.' . $_sElapsedFloat;
271
            
272
            }
273
        /**
274
         * Logs the given array output into the given file.
275
         * 
276
         * @since       2.1.1
277
         * @since       3.0.3   Changed the default log location and file name.
278
         * @deprecated  3.1.0   Use the `log()` method instead.
279
         */
280
        static public function logArray( $asArray, $sFilePath=null ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
281
            self::showDeprecationNotice( __CLASS__ .'::' . __FUNCTION__,  __CLASS__ .'::log()' );
282
            self::log( $asArray, $sFilePath );     
283
        }      
284
        
285
    /**
286
     * Returns a string representation of the given value.
287
     * @since       3.5.0
288
     * @param       mixed       $mValue     The value to get as a string
289
     * @internal
290
     */
291
    static public function getAsString( $mValue ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
292
             
293
        $mValue = is_object( $mValue )
294
            ? ( method_exists( $mValue, '__toString' ) 
295
                ? ( string ) $mValue          // cast string
296
                : ( array ) $mValue           // cast array
297
            )
298
            : $mValue;
299
        $mValue = is_array( $mValue )
300
            ? self::_getArrayMappedRecursive( 
301
                self::_getSliceByDepth( $mValue, 10 ), 
302
                array( __CLASS__, '_getObjectName' ) 
303
            )
304
            : $mValue;
305
        return self::_getArrayRepresentationSanitized( print_r( $mValue, true ) );
306
        
307
    }
308
        /**
309
         * Returns a object name if it is an object. Otherwise, the value itself.
310
         * This is used to convert objects into a string in array-walk functions 
311
         * as objects tent to get large when they are converted to a string representation.
312
         * @since       3.8.9
313
         */
314
        static private function _getObjectName( $mItem ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
315
            if ( is_object( $mItem ) ) {
316
                return '(object) ' . get_class( $mItem );
317
            }
318
            return $mItem;
319
        }
320
    
321
        /**
322
         * Returns a legible value representation.
323
         * @since       3.8.9
324
         * @return      string
325
         */
326
        static private function _getLegible( $mValue ) {                
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
327
            if ( is_array( $mValue ) ) {            
328
                return '(array, length: ' . count( $mValue ).') ' 
329
                    . print_r( self::_getLegibleArray( $mValue ) , true );
330
            }
331
            return print_r( self::_getLegibleValue( $mValue ), true );
332
        }
333
            
334
        /**
335
         * @since       3.8.9
336
         * @param       callable     $asoCallable
337
         * @return      string
338
         */
339
        static private function _getLegibleCallable( $asoCallable ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
340
            
341
            if ( is_string( $asoCallable ) ) {
342
                return '(callable) ' . $asoCallable;
343
            }
344
            if ( is_object( $asoCallable ) ) {
345
                return '(callable) ' . get_class( $asoCallable );
346
            }
347
            $_sSubject = is_object( $asoCallable[ 0 ] )
348
                ? get_class( $asoCallable[ 0 ] )
349
                : ( string ) $asoCallable[ 0 ];
350
351
            return '(callable) ' . $_sSubject . '::' . ( string ) $asoCallable[ 1 ];
352
            
353
        }        
354
        /**
355
         * @since       3.8.9
356
         * @param       object      $oObject
357
         * @return      string
358
         */
359
        static public function _getLegibleObject( $oObject ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
360
                        
361
            if ( method_exists( $oObject, '__toString' ) ) {
362
                return ( string ) $oObject;
363
            }
364
            return '(object) ' . get_class( $oObject ) . ' ' 
365
                . count( get_object_vars( $oObject ) ) . ' properties.';
366
            
367
        } 
368
        /**
369
         * Returns an array representation with value types in each element.
370
         * The element deeper than 10 dimensions will be dropped.
371
         * @since       3.8.9
372
         * @return      array
373
         */
374
        static public function _getLegibleArray( array $aArray ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
375
            return self::_getArrayMappedRecursive( 
376
                self::_getSliceByDepth( $aArray, 10 ), 
377
                array( __CLASS__, '_getLegibleValue' ) 
378
            );
379
        }
380
            /**
381
             * @since       3.8.9
382
             * @return      string
383
             */
384
            static private function _getLegibleValue( $mItem ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
385
                if ( is_callable( $mItem ) ) {
386
                    return self::_getLegibleCallable( $mItem );
387
                }
388
                return is_scalar( $mItem ) 
389
                    ? self::_getLegibleScalar( $mItem )
390
                    : self::_getLegibleNonScalar( $mItem );
391
            }
392
                /**
393
                 * @since       3.8.9
394
                 * @return      string
395
                 */
396
                static private function _getLegibleNonScalar( $mNonScalar ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
397
                    
398
                    $_sType = gettype( $mNonScalar );
399
                    if ( is_null( $mNonScalar ) ) {
400
                        return '(null)';
401
                    }                    
402
                    if ( is_object( $mNonScalar ) ) {
403
                        return '(' . $_sType . ') ' . get_class( $mNonScalar );
404
                    }
405
                    if ( is_array( $mNonScalar ) ) {
406
                        return '(' . $_sType . ') ' . count( $mNonScalar ) . ' elements';
407
                    }
408
                    return '(' . $_sType . ') ' . ( string ) $mNonScalar;
409
                    
410
                }
411
                /**
412
                 * @return      string
413
                 * @param       scalar      $sScalar        
414
                 * @param       integer     $iCharLimit     Character length limit to truncate.
0 ignored issues
show
Bug introduced by
There is no parameter named $iCharLimit. 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...
415
                 * @since       3.8.9
416
                 */
417
                static private function _getLegibleScalar( $sScalar ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
418
                 
419
                    if ( is_bool( $sScalar ) ) {
420
                        return '(boolean) ' . ( $sScalar ? 'true' : 'false' );
421
                    }                 
422
                    return is_string( $sScalar )
423
                        ? self::_getLegibleString( $sScalar )
424
                        : '(' . gettype( $sScalar ) . ', length: ' . self::_getValueLength( $sScalar ) .  ') ' . $sScalar;
425
                }
426
                    /**
427
                     * Returns a length of a value.
428
                     * @since       3.5.3
429
                     * @internal
430
                     * @return      integer|null        For string or integer, the string length. For array, the element lengths. For other types, null.
431
                     */
432
                    static private function _getValueLength( $mValue ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
433
                        $_sVariableType = gettype( $mValue );
434
                        if ( in_array( $_sVariableType, array( 'string', 'integer' ) ) ) {
435
                            return strlen( $mValue );
436
                        }
437
                        if ( 'array' === $_sVariableType ) {
438
                            return count( $mValue );
439
                        }
440
                        return null;
441
                        
442
                    }                
443
                    /**
444
                     * @return      string
445
                     */
446
                    static private function _getLegibleString( $sString, $iCharLimit=200 ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
447
                    
448
                        static $_iMBSupport;
449
                        $_iMBSupport    = isset( $_iMBSupport ) ? $_iMBSupport : ( integer ) function_exists( 'mb_strlen' );
450
                        $_aStrLenMethod = array( 'strlen', 'mb_strlen' );
451
                        $_aSubstrMethod = array( 'substr', 'mb_substr' );
452
                        
453
                        $_iCharLength   = call_user_func_array( $_aStrLenMethod[ $_iMBSupport ], array( $sString ) );
454
                        return $_iCharLength <= $iCharLimit
455
                            ? '(string, length: ' . $_iCharLength . ') ' . $sString
456
                            : '(string, length: ' . $_iCharLength . ') ' . call_user_func_array( $_aSubstrMethod[ $_iMBSupport ], array( $sString, 0, $iCharLimit ) )
457
                                . '...';
458
                        
459
                    }
460
                
461
        /**
462
         * Slices an array by the given depth.
463
         * 
464
         * @since       3.4.4
465
         * @since       3.8.9       Changed it not to convert an object into an array. Changed the scope to private.
466
         * @return      array
467
         * @internal
468
         */
469
        static private function _getSliceByDepth( array $aSubject, $iDepth=0 ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
470
471
            foreach ( $aSubject as $_sKey => $_vValue ) {
472
                if ( is_array( $_vValue ) ) {
473
                    $_iDepth = $iDepth;
474
                    if ( $iDepth > 0 ) {
475
                        $aSubject[ $_sKey ] = self::_getSliceByDepth( $_vValue, --$iDepth );
476
                        $iDepth = $_iDepth;
477
                        continue;
478
                    } 
479
                    unset( $aSubject[ $_sKey ] );
480
                }
481
            }
482
            return $aSubject;
483
            
484
        }
485
    
486
        /**
487
         * Performs `array_map()` recursively.
488
         * @return      array.
0 ignored issues
show
Documentation introduced by
The doc-type array. could not be parsed: Unknown type name "array." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
489
         * @since       3.8.9
490
         */
491
        static private function _getArrayMappedRecursive( array $aArray, $oCallable ) {
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
492
            
493
            self::$_oCurrentCallableForArrayMapRecursive = $oCallable;
494
            $_aArray = array_map( array( __CLASS__, '_getArrayMappedNested' ), $aArray );
495
            self::$_oCurrentCallableForArrayMapRecursive = null;
496
            return $_aArray;
497
            
498
        }
499
            static private $_oCurrentCallableForArrayMapRecursive;
500
            /**
501
             * @internal
502
             * @return      mixed       A modified value.
503
             * @since       3.8.9
504
             */
505
            static private function _getArrayMappedNested( $mItem ) {            
1 ignored issue
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
506
                return is_array( $mItem ) 
507
                    ? array_map( array( __CLASS__, '_getArrayMappedNested' ), $mItem ) 
508
                    : call_user_func( self::$_oCurrentCallableForArrayMapRecursive, $mItem );            
509
            }    
510
    
511
}
512