Passed
Branch dev (3fd540)
by Michael
22:28
created

AdminPageFramework_Debug_Base   F

Complexity

Total Complexity 68

Size/Duplication

Total Lines 432
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 68
eloc 162
dl 0
loc 432
rs 2.96
c 4
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A ___getCallableName() 0 12 4
A ___getValueLength() 0 9 3
A ___getLegibleString() 0 18 6
A ___getLegibleDetailedObject() 0 6 2
A ___getLegibleDetailedCallable() 0 2 1
A _getArrayRepresentationSanitized() 0 14 1
A ___getStackTraceArgument_unknown() 0 2 1
A ___getStackTraceArgument_boolean() 0 2 2
A ___getArgumentsOfEachStackTrace() 0 16 3
A ___getStackTraceArgument_object() 0 2 1
A ___getStackTraceArgument_scalar() 0 2 1
A ___getStackTraceArgument_string() 0 3 1
A _getLegibleDetails() 0 6 2
A getSlicedByDepth() 0 2 1
A ___getStackTraceArgument_array() 0 22 6
A ___getStackTraceArgument_resource() 0 2 1
A ___getStackTraceArgument_NULL() 0 2 1
A ___getLegibleDetailedScalar() 0 7 4
A _getSlicedByDepth() 0 23 5
A getLegibleDetailedValue() 0 7 3
A getObjectName() 0 5 2
A _getLegible() 0 20 6
A ___getLegibleDetailedArray() 0 6 2
A ___getLegibleDetailedNonScalar() 0 13 4
A getStackTrace() 0 34 5

How to fix   Complexity   

Complex Class

Complex classes like AdminPageFramework_Debug_Base often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AdminPageFramework_Debug_Base, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Admin Page Framework
4
 *
5
 * http://admin-page-framework.michaeluno.jp/
6
 * Copyright (c) 2013-2021, Michael Uno; Licensed MIT
7
 *
8
 */
9
10
/**
11
 * A base class of the debug class.
12
 *
13
 * Mainly provides methods for debug outputs.
14
 *
15
 * @since           3.8.9
16
 * @extends         AdminPageFramework_FrameworkUtility
17
 * @package         AdminPageFramework/Common/Utility
18
 */
19
class AdminPageFramework_Debug_Base extends AdminPageFramework_FrameworkUtility {
20
21
    /**
22
     * @var int
23
     * @since   3.8.19
24
     */
25
    static public $iLegibleArrayDepthLimit = 50;
26
27
    /**
28
     * Character length limit to truncate.
29
     */
30
    static public $iLegibleStringCharacterLimit = 99999;
31
32
    /**
33
     * Returns a legible value representation with value details.
34
     * @param   mixed   $mValue
35
     * @param   integer $iStringLengthLimit
36
     * @param   integer $iArrayDepthLimit
37
     * @return  string
38
     * @since   3.8.9
39
     */
40
    static protected function _getLegibleDetails( $mValue, $iStringLengthLimit=0, $iArrayDepthLimit=0 ) {
41
        if ( is_array( $mValue ) ) {
42
            return '(array, length: ' . count( $mValue ).') '
43
                . self::getAsString( print_r( self::___getLegibleDetailedArray( $mValue, $iStringLengthLimit, $iArrayDepthLimit ) , true ) );
44
        }
45
        return self::getAsString( print_r( self::getLegibleDetailedValue( $mValue, $iStringLengthLimit ), true ) );
46
    }
47
48
    /**
49
     * Returns a object name if it is an object. Otherwise, the value itself.
50
     * This is used to convert objects into a string in array-walk functions
51
     * as objects tent to get large when they are converted to a string representation.
52
     * @since       3.8.9
53
     * @since       3.8.32  Changed the visibility scope to public from private to be passed as a callback for outside the current class scope.
54
     * And renamed from `___getObjectName()`.
55
     * @param mixed $mItem
56
     * @return mixed
57
     */
58
    static public function getObjectName( $mItem ) {
59
        if ( is_object( $mItem ) ) {
60
            return '(object) ' . get_class( $mItem );
61
        }
62
        return $mItem;
63
    }
64
65
    /**
66
     * Returns a string representation of the given value with no variable details.
67
     *
68
     * @param  $mValue
69
     * @param  integer $iStringLengthLimit
70
     * @param  integer $iArrayDepthLimit
71
     * @return string
72
     * @since  3.8.9
73
     * @since  3.8.22  Added the `$sStringLengthLimit` and `$iArrayDepthLimit` parameters.
74
     */
75
    static protected function _getLegible( $mValue, $iStringLengthLimit=0, $iArrayDepthLimit=0 ) {
76
77
        $iArrayDepthLimit = $iArrayDepthLimit ? $iArrayDepthLimit : self::$iLegibleArrayDepthLimit;
78
        $mValue           = is_object( $mValue )
79
            ? ( method_exists( $mValue, '__toString' )
80
                ? ( string ) $mValue          // cast string
81
                : ( array ) $mValue           // cast array
82
            )
83
            : $mValue;
84
        $mValue = is_array( $mValue )
85
            ? self::getArrayMappedRecursive(
86
                array( __CLASS__, 'getObjectName' ),
87
                self::_getSlicedByDepth( $mValue, $iArrayDepthLimit ),
88
                array()
89
            )
90
            : $mValue;
91
        $mValue = is_string( $mValue )
92
            ? self::___getLegibleString( $mValue,  $iStringLengthLimit, false )
93
            : $mValue;
94
        return self::_getArrayRepresentationSanitized( self::getAsString( print_r( $mValue, true ) ) );
95
96
    }
97
98
        /**
99
         * @since       3.8.9
100
         * @param       callable     $asoCallable
101
         * @return      string
102
         */
103
        static private function ___getLegibleDetailedCallable( $asoCallable ) {
104
            return '(callable) ' . self::___getCallableName( $asoCallable );
105
        }
106
            /**
107
             * @since       3.8.9
108
             * @param       callable     $asoCallable
109
             * @return      string
110
             */
111
            static public function ___getCallableName( $asoCallable ) {
112
113
                if ( is_string( $asoCallable ) ) {
114
                    return $asoCallable;
115
                }
116
                if ( is_object( $asoCallable ) ) {
117
                    return get_class( $asoCallable );
118
                }
119
                $_sSubject = is_object( $asoCallable[ 0 ] )
120
                    ? get_class( $asoCallable[ 0 ] )
121
                    : ( string ) $asoCallable[ 0 ];
122
                return $_sSubject . '::' . ( string ) $asoCallable[ 1 ];
123
124
            }
125
126
        /**
127
         * @since       3.8.9
128
         * @param       object      $oObject
129
         * @return      string
130
         */
131
        static private function ___getLegibleDetailedObject( $oObject ) {
132
133
            if ( method_exists( $oObject, '__toString' ) ) {
134
                return ( string ) $oObject;
135
            }
136
            return '(object) ' . get_class( $oObject ) . ' ' . count( get_object_vars( $oObject ) ) . ' properties.';
137
138
        }
139
140
        /**
141
         * Returns an array representation with value types in each element.
142
         * The element deeper than 10 dimensions will be dropped.
143
         * @param  array   $aArray
144
         * @param  integer $iStringLengthLimit
145
         * @param  integer $iDepthLimit
146
         * @return array
147
         * @since  3.8.9
148
         * @since  3.8.22  Added the `$iDepthLimit` parameter
149
         * @since  3.8.22  Changed the scope to private from public.
150
         * @since  3.8.22  Renamed from `_getLegibleArray()`.
151
         */
152
        static private function ___getLegibleDetailedArray( array $aArray, $iStringLengthLimit=0, $iDepthLimit=0 ) {
153
            $_iDepthLimit = $iDepthLimit ? $iDepthLimit : self::$iLegibleArrayDepthLimit;
154
            return self::getArrayMappedRecursive(
155
                array( __CLASS__, 'getLegibleDetailedValue' ),
156
                self::_getSlicedByDepth( $aArray, $_iDepthLimit ),
157
                array( $iStringLengthLimit )
158
            );
159
        }
160
161
    /**
162
     * @param mixed $mItem
163
     * @param integer $iStringLengthLimit
164
     * @return      string
165
     * @since       3.8.22  Renamed from `_getLegibleValue()`.
166
     * @since       3.8.9
167
     * @since       3.8.32  Changed the visibility scope to public from private to be passed as a callback for outside the current class scope.
168
     * And renamed from `___getLegibleDetailedValue()`.
169
     */
170
    static public function getLegibleDetailedValue( $mItem, $iStringLengthLimit ) {
171
        if ( is_callable( $mItem ) ) {
172
            return self::___getLegibleDetailedCallable( $mItem );
173
        }
174
        return is_scalar( $mItem )
175
            ? self::___getLegibleDetailedScalar( $mItem, $iStringLengthLimit )
176
            : self::___getLegibleDetailedNonScalar( $mItem );
177
    }
178
        /**
179
         * @since       3.8.9
180
         * @since       3.8.22  Renamed from `_getLegibleNonScalar()`.
181
         * @return      string
182
         * @param       mixed   $mNonScalar
183
         */
184
        static private function ___getLegibleDetailedNonScalar( $mNonScalar ) {
185
186
            $_sType = gettype( $mNonScalar );
187
            if ( is_null( $mNonScalar ) ) {
188
                return '(null)';
189
            }
190
            if ( is_object( $mNonScalar ) ) {
191
                return self::___getLegibleDetailedObject( $mNonScalar );
192
            }
193
            if ( is_array( $mNonScalar ) ) {
194
                return '(' . $_sType . ') ' . count( $mNonScalar ) . ' elements';
195
            }
196
            return '(' . $_sType . ') ' . ( string ) $mNonScalar;
197
198
        }
199
        /**
200
         * @since  3.8.9
201
         * @since  3.8.22                       Renamed from `_getLegibleScalar()`.
202
         * @param  integer|float|boolean|string $sScalar
203
         * @param  integer $iStringLengthLimit
204
         * @return string
205
         */
206
        static private function ___getLegibleDetailedScalar( $sScalar, $iStringLengthLimit ) {
207
            if ( is_bool( $sScalar ) ) {
208
                return '(boolean) ' . ( $sScalar ? 'true' : 'false' );
209
            }
210
            return is_string( $sScalar )
211
                ? self::___getLegibleString( $sScalar, $iStringLengthLimit, true )
212
                : '(' . gettype( $sScalar ) . ', length: ' . self::___getValueLength( $sScalar ) .  ') ' . $sScalar;
213
        }
214
            /**
215
             * Returns a length of a value.
216
             * @internal
217
             * @since    3.5.3
218
             * @return   integer|null For string or integer, the string length. For array, the element lengths. For other types, null.
219
             * @param    mixed        $mValue
220
             */
221
            static private function ___getValueLength( $mValue ) {
222
                $_sVariableType = gettype( $mValue );
223
                if ( in_array( $_sVariableType, array( 'string', 'integer' ) ) ) {
224
                    return strlen( $mValue );
225
                }
226
                if ( 'array' === $_sVariableType ) {
227
                    return count( $mValue );
228
                }
229
                return null;
230
            }
231
            /**
232
             * @param       string      $sString
233
             * @param       integer     $iLengthLimit
234
             * @param       boolean     $bShowDetails
235
             * @return      string
236
             */
237
            static private function ___getLegibleString( $sString, $iLengthLimit, $bShowDetails=true ) {
238
239
                static $_iMBSupport;
240
                $_iMBSupport    = isset( $_iMBSupport ) ? $_iMBSupport : ( integer ) function_exists( 'mb_strlen' );
241
                $_aStrLenMethod = array( 'strlen', 'mb_strlen' );
242
                $_aSubstrMethod = array( 'substr', 'mb_substr' );
243
                $iCharLimit     = $iLengthLimit ? $iLengthLimit : self::$iLegibleStringCharacterLimit;
244
                $_iCharLength   = call_user_func_array( $_aStrLenMethod[ $_iMBSupport ], array( $sString ) );
245
246
                if ( $bShowDetails ) {
247
                    return $_iCharLength <= $iCharLimit
248
                        ? '(string, length: ' . $_iCharLength . ') ' . $sString
249
                        : '(string, length: ' . $_iCharLength . ') ' . call_user_func_array( $_aSubstrMethod[ $_iMBSupport ], array( $sString, 0, $iCharLimit ) )
250
                            . '...';
251
                }
252
                return $_iCharLength <= $iCharLimit
253
                    ? $sString
254
                    : call_user_func_array( $_aSubstrMethod[ $_iMBSupport ], array( $sString, 0, $iCharLimit ) );
255
256
            }
257
258
    /**
259
     * @param  string $sString
260
     * @since  3.8.9
261
     * @return string
262
     */
263
    static protected function _getArrayRepresentationSanitized( $sString ) {
264
265
        // Fix extra line breaks after `Array()`
266
        $sString = preg_replace(
267
            '/\)(\r\n?|\n)(?=(\r\n?|\n)\s+[\[)])/', // needle
268
            ')', // replacement
269
            $sString // subject
270
        );
271
272
        // Fix empty array output
273
        return preg_replace(
274
            '/Array(\r\n?|\n)\s+\((\r\n?|\n)\s+\)/', // needle
275
            'Array()', // replacement
276
            $sString // subject
277
        );
278
279
    }
280
281
    /**
282
     * Slices an array by the given depth.
283
     * @param array $aSubject
284
     * @param int $iDepth
285
     * @param string $sMore
286
     * @return array
287
     * @since  3.8.22
288
     */
289
    static public function getSlicedByDepth( array $aSubject, $iDepth=0, $sMore='(array truncated) ...' ) {
290
       return self::_getSlicedByDepth( $aSubject, $iDepth, $sMore );
291
    }
292
293
    /**
294
     * Slices an array by the given depth.
295
     *
296
     * @param array $aSubject
297
     * @param int $iDepth
298
     * @param string $sMore
299
     *
300
     * @return      array
301
     * @since       3.4.4
302
     * @since       3.8.9       Changed it not to convert an object into an array.
303
     * @since       3.8.9       Changed the scope to private.
304
     * @since       3.8.9       Renamed from `getSliceByDepth()`.
305
     * @since       3.8.22      Show a message when truncated by depth.
306
     * @since       3.8.22      Added the `$sMore` parameter.
307
     * @internal
308
     */
309
    static private function _getSlicedByDepth( array $aSubject, $iDepth=0, $sMore='(array truncated) ...' ) {
310
311
        foreach ( $aSubject as $_sKey => $_vValue ) {
312
313
            if ( is_array( $_vValue ) ) {
314
315
                $_iDepth = $iDepth;
316
                if ( $iDepth > 0 ) {
317
                    $aSubject[ $_sKey ] = self::_getSlicedByDepth( $_vValue, --$iDepth );
318
                    $iDepth = $_iDepth;
319
                    continue;
320
                }
321
322
                if ( strlen( $sMore ) ) {
323
                    $aSubject[ $_sKey ] = $sMore;
324
                    continue;
325
                }
326
                unset( $aSubject[ $_sKey ] );
327
328
            }
329
330
        }
331
        return $aSubject;
332
333
    }
334
335
    /**
336
     * @param   integer     $iSkip    The number of skipping records. This is used when the caller does not want to include the self function/method.
337
     * @param   null|mixed  $_deprecated
338
     * @return  string
339
     * @since   3.8.22
340
     * @since   3.8.23 Deprecated the `$oException` parameter.
341
     */
342
    static public function getStackTrace( $iSkip=0, $_deprecated=null ) {
343
344
        $_iSkip      = 1;   // need to skip this method trace itself
345
        $_oException = new Exception();
346
347
        // Backward compatibility.
348
        if ( is_object( $iSkip ) && $iSkip instanceof Exception ) {
0 ignored issues
show
introduced by
The condition is_object($iSkip) is always false.
Loading history...
349
            $_oException = $iSkip;
350
            $iSkip = ( integer ) $_deprecated;
351
        }
352
353
        $_iSkip      = $_iSkip + $iSkip;
354
355
        $_aTraces    = array();
356
        $_aFrames    = $_oException->getTrace();
357
        $_aFrames    = array_slice( $_aFrames, $_iSkip );
358
        foreach ( array_reverse( $_aFrames ) as $_iIndex => $_aFrame ) {
359
360
            $_aFrame     = $_aFrame + array(
361
                'file'  => null, 'line' => null, 'function' => null,
362
                'class' => null, 'args' => array(),
363
            );
364
            $_sArguments = self::___getArgumentsOfEachStackTrace( $_aFrame[ 'args' ] );
365
            $_aTraces[]  = sprintf(
366
                "#%s %s(%s): %s(%s)",
367
                $_iIndex + 1,
368
                $_aFrame[ 'file' ],
369
                $_aFrame[ 'line' ],
370
                isset( $_aFrame[ 'class' ] ) ? $_aFrame[ 'class' ] . '->' . $_aFrame[ 'function' ] : $_aFrame[ 'function' ],
371
                $_sArguments
372
            );
373
374
        }
375
        return implode( PHP_EOL, $_aTraces ) . PHP_EOL;
376
377
    }
378
        /**
379
         * @param array $aTraceArguments
380
         * @return string
381
         * @since 3.8.22
382
         * @internal
383
         */
384
        static private function ___getArgumentsOfEachStackTrace( array $aTraceArguments ) {
385
386
            $_aArguments = array();
387
            foreach ( $aTraceArguments as $_mArgument ) {
388
                $_sType        = gettype( $_mArgument );
389
                $_sType        = str_replace(
390
                    array( 'resource (closed)', 'unknown type', 'integer', 'double', ),
391
                    array( 'resource', 'unknown', 'scalar', 'scalar', ),
392
                    $_sType
393
                );
394
                $_sMethodName  = "___getStackTraceArgument_{$_sType}";
395
                $_aArguments[] = method_exists( __CLASS__, $_sMethodName )
396
                    ? self::{$_sMethodName}( $_mArgument )
397
                    : $_sType;
398
            }
399
            return join(", ",  $_aArguments );
400
        }
401
            /**
402
             * @since 3.8.22
403
             * @param mixed $mArgument
404
             * @internal
405
             * @return string
406
             */
407
            static private function ___getStackTraceArgument_string( $mArgument ) {
408
                $_sString = self::___getLegibleString( $mArgument, 200, true );
409
                return "'" . $_sString . "'";
410
            }
411
            static private function ___getStackTraceArgument_scalar( $mArgument ) {
412
                return $mArgument;
413
            }
414
            static private function ___getStackTraceArgument_boolean( $mArgument ) {
415
                return ( $mArgument ) ? "true" : "false";
416
            }
417
            static private function ___getStackTraceArgument_NULL( $mArgument ) {
418
                return 'NULL';
419
            }
420
            static private function ___getStackTraceArgument_object( $mArgument ) {
421
                return 'Object(' . get_class( $mArgument ) . ')';
422
            }
423
            static private function ___getStackTraceArgument_resource( $mArgument ) {
424
                return get_resource_type( $mArgument );
425
            }
426
            static private function ___getStackTraceArgument_unknown( $mArgument ) {
427
                return gettype( $mArgument );
428
            }
429
            static private function ___getStackTraceArgument_array( $mArgument ) {
430
                $_sOutput = '';
431
                $_iMax    = 10;
432
                $_iTotal  = count( $mArgument );
433
                $_iIndex  = 0;
434
                foreach( $mArgument as $_sKey => $_mValue ) {
435
                    $_iIndex++;
436
                    $_mValue   = is_scalar( $_mValue )
437
                        ? self::___getLegibleDetailedScalar( $_mValue, 100 )
438
                        : ucfirst( gettype( $_mValue ) ) . (
439
                            is_object( $_mValue )
440
                                ? ' (' . get_class( $_mValue ) . ')'
441
                                : ''
442
                        );
443
                    $_sOutput .= $_sKey . ': ' . $_mValue . ', ';
444
                    if ( $_iIndex > $_iMax && $_iTotal > $_iMax ) {
445
                        $_sOutput  = rtrim( $_sOutput, ','  ) . '...';
446
                        break;
447
                    }
448
                }
449
                $_sOutput = rtrim( $_sOutput, ',' );
450
                return "Array({$_sOutput})";
451
            }
452
453
}