AloFramework Logger
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo

Namespaces

  • AloFramework
    • Log
  • None
  • PHP
  • Psr
    • Log

Classes

  • BaseUser
  • Kint
  • Kint_Decorators_Plain
  • Kint_Decorators_Rich
  • Kint_Objects_Closure
  • Kint_Objects_Smarty
  • Kint_Objects_SplFileInfo
  • Kint_Parsers_ClassMethods
  • Kint_Parsers_ClassStatics
  • Kint_Parsers_Color
  • Kint_Parsers_FsPath
  • Kint_Parsers_Json
  • Kint_Parsers_Microtime
  • Kint_Parsers_objectIterateable
  • Kint_Parsers_SplObjectStorage
  • Kint_Parsers_Timestamp
  • Kint_Parsers_Xml
  • KintObject
  • kintParser
  • kintVariableData
  • User
  • UserManager

Functions

  • d
  • dd
  • ddd
  • s
  • sd
  1 <?php
  2 
  3 abstract class kintParser extends kintVariableData
  4 {
  5     private static $_level = 0;
  6     private static $_customDataTypes;
  7     private static $_objectParsers;
  8     private static $_objects;
  9     private static $_marker;
 10 
 11     private static $_skipAlternatives = false;
 12 
 13     private static $_placeFullStringInValue = false;
 14 
 15 
 16     private static function _init()
 17     {
 18         $fh = opendir( KINT_DIR . 'parsers/custom/' );
 19         while ( $fileName = readdir( $fh ) ) {
 20             if ( substr( $fileName, -4 ) !== '.php' ) continue;
 21 
 22             require KINT_DIR . 'parsers/custom/' . $fileName;
 23             self::$_customDataTypes[] = substr( $fileName, 0, -4 );
 24         }
 25         $fh = opendir( KINT_DIR . 'parsers/objects/' );
 26         while ( $fileName = readdir( $fh ) ) {
 27             if ( substr( $fileName, -4 ) !== '.php' ) continue;
 28 
 29             require KINT_DIR . 'parsers/objects/' . $fileName;
 30             self::$_objectParsers[] = substr( $fileName, 0, -4 );
 31         }
 32     }
 33 
 34     public static function reset()
 35     {
 36         self::$_level   = 0;
 37         self::$_objects = self::$_marker = null;
 38     }
 39 
 40     /**
 41      * main and usually single method a custom parser must implement
 42      *
 43      * @param mixed $variable
 44      *
 45      * @return mixed [!!!] false is returned if the variable is not of current type
 46      */
 47     abstract protected function _parse( & $variable );
 48 
 49 
 50     /**
 51      * the only public entry point to return a parsed representation of a variable
 52      *
 53      * @static
 54      *
 55      * @param      $variable
 56      * @param null $name
 57      *
 58      * @throws Exception
 59      * @return \kintParser
 60      */
 61     public final static function factory( & $variable, $name = null )
 62     {
 63         isset( self::$_customDataTypes ) or self::_init();
 64 
 65         # save internal data to revert after dumping to properly handle recursions etc
 66         $revert = array(
 67             'level'   => self::$_level,
 68             'objects' => self::$_objects,
 69         );
 70 
 71         self::$_level++;
 72 
 73         $varData       = new kintVariableData;
 74         $varData->name = $name;
 75 
 76         # first parse the variable based on its type
 77         $varType = gettype( $variable );
 78         $varType === 'unknown type' and $varType = 'unknown'; # PHP 5.4 inconsistency
 79         $methodName = '_parse_' . $varType;
 80 
 81         # objects can be presented in a different way altogether, INSTEAD, not ALONGSIDE the generic parser
 82         if ( $varType === 'object' ) {
 83             foreach ( self::$_objectParsers as $parserClass ) {
 84                 $className = 'Kint_Objects_' . $parserClass;
 85 
 86                 /** @var $object KintObject */
 87                 $object = new $className;
 88                 if ( ( $alternativeTabs = $object->parse( $variable ) ) !== false ) {
 89                     self::$_skipAlternatives   = true;
 90                     $alternativeDisplay        = new kintVariableData;
 91                     $alternativeDisplay->type  = $object->name;
 92                     $alternativeDisplay->value = $object->value;
 93                     $alternativeDisplay->name  = $name;
 94 
 95                     foreach ( $alternativeTabs as $name => $values ) {
 96                         $alternative       = kintParser::factory( $values );
 97                         $alternative->type = $name;
 98                         if ( Kint::enabled() === Kint::MODE_RICH ) {
 99                             empty( $alternative->value ) and $alternative->value = $alternative->extendedValue;
100                             $alternativeDisplay->_alternatives[] = $alternative;
101                         } else {
102                             $alternativeDisplay->extendedValue[] = $alternative;
103                         }
104                     }
105 
106                     self::$_skipAlternatives = false;
107                     self::$_level   = $revert['level'];
108                     self::$_objects = $revert['objects'];
109                     return $alternativeDisplay;
110                 }
111             }
112         }
113 
114         # base type parser returning false means "stop processing further": e.g. recursion
115         if ( self::$methodName( $variable, $varData ) === false ) {
116             self::$_level--;
117             return $varData;
118         }
119 
120         if ( Kint::enabled() === Kint::MODE_RICH && !self::$_skipAlternatives ) {
121             # if an alternative returns something that can be represented in an alternative way, don't :)
122             self::$_skipAlternatives = true;
123 
124             # now check whether the variable can be represented in a different way
125             foreach ( self::$_customDataTypes as $parserClass ) {
126                 $className = 'Kint_Parsers_' . $parserClass;
127 
128                 /** @var $parser kintParser */
129                 $parser       = new $className;
130                 $parser->name = $name; # the parser may overwrite the name value, so set it first
131 
132                 if ( $parser->_parse( $variable ) !== false ) {
133                     $varData->_alternatives[] = $parser;
134                 }
135             }
136 
137 
138             # if alternatives exist, push extendedValue to their front and display it as one of alternatives
139             if ( !empty( $varData->_alternatives ) && isset( $varData->extendedValue ) ) {
140                 $_ = new kintVariableData;
141 
142                 $_->value = $varData->extendedValue;
143                 $_->type  = 'contents';
144                 $_->size  = null;
145 
146                 array_unshift( $varData->_alternatives, $_ );
147                 $varData->extendedValue = null;
148             }
149 
150             self::$_skipAlternatives = false;
151         }
152 
153         self::$_level   = $revert['level'];
154         self::$_objects = $revert['objects'];
155 
156         if ( strlen( $varData->name ) > 80 ) {
157             $varData->name =
158                 self::_substr( $varData->name, 0, 37 )
159                 . '...'
160                 . self::_substr( $varData->name, -38, null );
161         }
162         return $varData;
163     }
164 
165     private static function _checkDepth()
166     {
167         return Kint::$maxLevels != 0 && self::$_level >= Kint::$maxLevels;
168     }
169 
170     private static function _isArrayTabular( array $variable )
171     {
172         if ( Kint::enabled() !== Kint::MODE_RICH ) return false;
173 
174         $arrayKeys   = array();
175         $keys        = null;
176         $closeEnough = false;
177         foreach ( $variable as $row ) {
178             if ( !is_array( $row ) || empty( $row ) ) return false;
179 
180             foreach ( $row as $col ) {
181                 if ( !empty( $col ) && !is_scalar( $col ) ) return false; // todo add tabular "tolerance"
182             }
183 
184             if ( isset( $keys ) && !$closeEnough ) {
185                 # let's just see if the first two rows have same keys, that's faster and has the
186                 # positive side effect of easily spotting missing keys in later rows
187                 if ( $keys !== array_keys( $row ) ) return false;
188 
189                 $closeEnough = true;
190             } else {
191                 $keys = array_keys( $row );
192             }
193 
194             $arrayKeys = array_unique( array_merge( $arrayKeys, $keys ) );
195         }
196 
197         return $arrayKeys;
198     }
199 
200     private static function _decorateCell( kintVariableData $kintVar )
201     {
202         if ( $kintVar->extendedValue !== null || !empty( $kintVar->_alternatives ) ) {
203             return '<td>' . Kint_Decorators_Rich::decorate( $kintVar ) . '</td>';
204         }
205 
206         $output = '<td';
207 
208         if ( $kintVar->value !== null ) {
209             $output .= ' title="' . $kintVar->type;
210 
211             if ( $kintVar->size !== null ) {
212                 $output .= " (" . $kintVar->size . ")";
213             }
214 
215             $output .= '">' . $kintVar->value;
216         } else {
217             $output .= '>';
218 
219             if ( $kintVar->type !== 'NULL' ) {
220                 $output .= '<u>' . $kintVar->type;
221 
222                 if ( $kintVar->size !== null ) {
223                     $output .= "(" . $kintVar->size . ")";
224                 }
225 
226                 $output .= '</u>';
227             } else {
228                 $output .= '<u>NULL</u>';
229             }
230         }
231 
232 
233         return $output . '</td>';
234     }
235 
236 
237     public static function escape( $value, $encoding = null )
238     {
239         if ( empty( $value ) ) return $value;
240 
241         if ( Kint::enabled() === Kint::MODE_CLI ) {
242             $value = str_replace( "\x1b", "\\x1b", $value );
243         }
244 
245         if ( Kint::enabled() === Kint::MODE_CLI || Kint::enabled() === Kint::MODE_WHITESPACE ) return $value;
246 
247         $encoding or $encoding = self::_detectEncoding( $value );
248         $value = htmlspecialchars( $value, ENT_NOQUOTES, $encoding === 'ASCII' ? 'UTF-8' : $encoding );
249 
250 
251         if ( $encoding === 'UTF-8' ) {
252             // todo we could make the symbols hover-title show the code for the invisible symbol
253             # when possible force invisible characters to have some sort of display (experimental)
254             $value = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', '?', $value );
255         }
256 
257         # this call converts all non-ASCII characters into html chars of format
258         if ( function_exists( 'mb_encode_numericentity' ) ) {
259             $value = mb_encode_numericentity(
260                 $value,
261                 array( 0x80, 0xffff, 0, 0xffff, ),
262                 $encoding
263             );
264         }
265 
266         return $value;
267     }
268 
269 
270     private static $_dealingWithGlobals = false;
271 
272     private static function _parse_array( &$variable, kintVariableData $variableData )
273     {
274         isset( self::$_marker ) or self::$_marker = "\x00" . uniqid();
275 
276         # naturally, $GLOBALS variable is an intertwined recursion nightmare, use black magic
277         $globalsDetector = false;
278         if ( array_key_exists( 'GLOBALS', $variable ) && is_array( $variable['GLOBALS'] ) ) {
279             $globalsDetector = "\x01" . uniqid();
280 
281             $variable['GLOBALS'][ $globalsDetector ] = true;
282             if ( isset( $variable[ $globalsDetector ] ) ) {
283                 unset( $variable[ $globalsDetector ] );
284                 self::$_dealingWithGlobals = true;
285             } else {
286                 unset( $variable['GLOBALS'][ $globalsDetector ] );
287                 $globalsDetector = false;
288             }
289         }
290 
291         $variableData->type = 'array';
292         $variableData->size = count( $variable );
293 
294         if ( $variableData->size === 0 ) {
295             return;
296         }
297         if ( isset( $variable[ self::$_marker ] ) ) { # recursion; todo mayhaps show from where
298             if ( self::$_dealingWithGlobals ) {
299                 $variableData->value = '*RECURSION*';
300             } else {
301                 unset( $variable[ self::$_marker ] );
302                 $variableData->value = self::$_marker;
303             }
304             return false;
305         }
306         if ( self::_checkDepth() ) {
307             $variableData->extendedValue = "*DEPTH TOO GREAT*";
308             return false;
309         }
310 
311         $isSequential = self::_isSequential( $variable );
312 
313         if ( $variableData->size > 1 && ( $arrayKeys = self::_isArrayTabular( $variable ) ) !== false ) {
314             $variable[ self::$_marker ] = true; # this must be AFTER _isArrayTabular
315             $firstRow                   = true;
316             $extendedValue              = '<table class="kint-report"><thead>';
317 
318             foreach ( $variable as $rowIndex => & $row ) {
319                 # display strings in their full length
320                 self::$_placeFullStringInValue = true;
321 
322                 if ( $rowIndex === self::$_marker ) continue;
323 
324                 if ( isset( $row[ self::$_marker ] ) ) {
325                     $variableData->value = "*RECURSION*";
326                     return false;
327                 }
328 
329 
330                 $extendedValue .= '<tr>';
331                 if ( $isSequential ) {
332                     $output = '<td>' . '#' . ( $rowIndex + 1 )  . '</td>';
333                 } else {
334                     $output = self::_decorateCell( kintParser::factory( $rowIndex ) );
335                 }
336                 if ( $firstRow ) {
337                     $extendedValue .= '<th>&nbsp;</th>';
338                 }
339 
340                 # we iterate the known full set of keys from all rows in case some appeared at later rows,
341                 # as we only check the first two to assume
342                 foreach ( $arrayKeys as $key ) {
343                     if ( $firstRow ) {
344                         $extendedValue .= '<th>' . self::escape( $key ) . '</th>';
345                     }
346 
347                     if ( !array_key_exists( $key, $row ) ) {
348                         $output .= '<td class="kint-empty"></td>';
349                         continue;
350                     }
351 
352                     $var = kintParser::factory( $row[ $key ] );
353 
354                     if ( $var->value === self::$_marker ) {
355                         $variableData->value = '*RECURSION*';
356                         return false;
357                     } elseif ( $var->value === '*RECURSION*' ) {
358                         $output .= '<td class="kint-empty"><u>*RECURSION*</u></td>';
359                     } else {
360                         $output .= self::_decorateCell( $var );
361                     }
362                     unset( $var );
363                 }
364 
365                 if ( $firstRow ) {
366                     $extendedValue .= '</tr></thead><tr>';
367                     $firstRow = false;
368                 }
369 
370                 $extendedValue .= $output . '</tr>';
371             }
372             self::$_placeFullStringInValue = false;
373 
374             $variableData->extendedValue = $extendedValue . '</table>';
375 
376         } else {
377             $variable[ self::$_marker ] = true;
378             $extendedValue              = array();
379 
380             foreach ( $variable as $key => & $val ) {
381                 if ( $key === self::$_marker ) continue;
382 
383                 $output = kintParser::factory( $val );
384                 if ( $output->value === self::$_marker ) {
385                     $variableData->value = "*RECURSION*"; // recursion occurred on a higher level, thus $this is recursion
386                     return false;
387                 }
388                 if ( !$isSequential ) {
389                     $output->operator = '=>';
390                 }
391                 $output->name    = $isSequential ? null : "'" . $key . "'";
392                 $extendedValue[] = $output;
393             }
394             $variableData->extendedValue = $extendedValue;
395         }
396 
397         if ( $globalsDetector ) {
398             self::$_dealingWithGlobals = false;
399         }
400 
401         unset( $variable[ self::$_marker ] );
402     }
403 
404 
405     private static function _parse_object( &$variable, kintVariableData $variableData )
406     {
407         if ( function_exists( 'spl_object_hash' ) ) {
408             $hash = spl_object_hash( $variable );
409         } else {
410             ob_start();
411             var_dump( $variable );
412             preg_match( '[#(\d+)]', ob_get_clean(), $match );
413             $hash = $match[1];
414         }
415 
416         $castedArray        = (array) $variable;
417         $variableData->type = get_class( $variable );
418         $variableData->size = count( $castedArray );
419 
420         if ( isset( self::$_objects[ $hash ] ) ) {
421             $variableData->value = '*RECURSION*';
422             return false;
423         }
424         if ( self::_checkDepth() ) {
425             $variableData->extendedValue = "*DEPTH TOO GREAT*";
426             return false;
427         }
428 
429 
430         # ArrayObject (and maybe ArrayIterator, did not try yet) unsurprisingly consist of mainly dark magic.
431         # What bothers me most, var_dump sees no problem with it, and ArrayObject also uses a custom,
432         # undocumented serialize function, so you can see the properties in internal functions, but
433         # can never iterate some of them if the flags are not STD_PROP_LIST. Fun stuff.
434         if ( $variableData->type === 'ArrayObject' || is_subclass_of( $variable, 'ArrayObject' ) ) {
435             $arrayObjectFlags = $variable->getFlags();
436             $variable->setFlags( ArrayObject::STD_PROP_LIST );
437         }
438 
439         self::$_objects[ $hash ] = true; // todo store reflectorObject here for alternatives cache
440         $reflector               = new ReflectionObject( $variable );
441 
442         # add link to definition of userland objects
443         if ( Kint::enabled() === Kint::MODE_RICH && Kint::$fileLinkFormat && $reflector->isUserDefined() ) {
444             $url = Kint::getIdeLink( $reflector->getFileName(), $reflector->getStartLine() );
445 
446             $class              = ( strpos( $url, 'http://' ) === 0 ) ? 'class="kint-ide-link" ' : '';
447             $variableData->type = "<a {$class}href=\"{$url}\">{$variableData->type}</a>";
448         }
449         $variableData->size = 0;
450 
451         $extendedValue = array();
452         $encountered   = array();
453 
454         # copy the object as an array as it provides more info than Reflection (depends)
455         foreach ( $castedArray as $key => $value ) {
456             /* casting object to array:
457              * integer properties are inaccessible;
458              * private variables have the class name prepended to the variable name;
459              * protected variables have a '*' prepended to the variable name.
460              * These prepended values have null bytes on either side.
461              * http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
462              */
463             if ( $key{0} === "\x00" ) {
464 
465                 $access = $key{1} === "*" ? "protected" : "private";
466 
467                 // Remove the access level from the variable name
468                 $key = substr( $key, strrpos( $key, "\x00" ) + 1 );
469             } else {
470                 $access = "public";
471             }
472 
473             $encountered[ $key ] = true;
474 
475             $output           = kintParser::factory( $value, self::escape( $key ) );
476             $output->access   = $access;
477             $output->operator = '->';
478             $extendedValue[]  = $output;
479             $variableData->size++;
480         }
481 
482         foreach ( $reflector->getProperties() as $property ) {
483             $name = $property->name;
484             if ( $property->isStatic() || isset( $encountered[ $name ] ) ) continue;
485 
486             if ( $property->isProtected() ) {
487                 $property->setAccessible( true );
488                 $access = "protected";
489             } elseif ( $property->isPrivate() ) {
490                 $property->setAccessible( true );
491                 $access = "private";
492             } else {
493                 $access = "public";
494             }
495 
496             $value = $property->getValue( $variable );
497 
498             $output           = kintParser::factory( $value, self::escape( $name ) );
499             $output->access   = $access;
500             $output->operator = '->';
501             $extendedValue[]  = $output;
502             $variableData->size++;
503         }
504 
505         if ( isset( $arrayObjectFlags ) ) {
506             $variable->setFlags( $arrayObjectFlags );
507         }
508 
509         if ( $variableData->size ) {
510             $variableData->extendedValue = $extendedValue;
511         }
512     }
513 
514 
515     private static function _parse_boolean( &$variable, kintVariableData $variableData )
516     {
517         $variableData->type  = 'bool';
518         $variableData->value = $variable ? 'TRUE' : 'FALSE';
519     }
520 
521     private static function _parse_double( &$variable, kintVariableData $variableData )
522     {
523         $variableData->type  = 'float';
524         $variableData->value = $variable;
525     }
526 
527     private static function _parse_integer( &$variable, kintVariableData $variableData )
528     {
529         $variableData->type  = 'integer';
530         $variableData->value = $variable;
531     }
532 
533     private static function _parse_null( &$variable, kintVariableData $variableData )
534     {
535         $variableData->type = 'NULL';
536     }
537 
538     private static function _parse_resource( &$variable, kintVariableData $variableData )
539     {
540         $resourceType       = get_resource_type( $variable );
541         $variableData->type = "resource ({$resourceType})";
542 
543         if ( $resourceType === 'stream' && $meta = stream_get_meta_data( $variable ) ) {
544 
545             if ( isset( $meta['uri'] ) ) {
546                 $file = $meta['uri'];
547 
548                 if ( function_exists( 'stream_is_local' ) ) {
549                     // Only exists on PHP >= 5.2.4
550                     if ( stream_is_local( $file ) ) {
551                         $file = Kint::shortenPath( $file );
552                     }
553                 }
554 
555                 $variableData->value = $file;
556             }
557         }
558     }
559 
560     private static function _parse_string( &$variable, kintVariableData $variableData )
561     {
562         $variableData->type = 'string';
563 
564         $encoding = self::_detectEncoding( $variable );
565         if ( $encoding !== 'ASCII' ) {
566             $variableData->type .= ' ' . $encoding;
567         }
568 
569 
570         $variableData->size = self::_strlen( $variable, $encoding );
571         if ( Kint::enabled() !== Kint::MODE_RICH ) {
572             $variableData->value = '"' . self::escape( $variable, $encoding ) . '"';
573             return;
574         }
575 
576 
577         if ( !self::$_placeFullStringInValue ) {
578 
579             $strippedString = preg_replace( '[\s+]', ' ', $variable );
580             if ( Kint::$maxStrLength && $variableData->size > Kint::$maxStrLength ) {
581 
582                 // encode and truncate
583                 $variableData->value         = '"'
584                     . self::escape( self::_substr( $strippedString, 0, Kint::$maxStrLength, $encoding ), $encoding )
585                     . '&hellip;"';
586                 $variableData->extendedValue = self::escape( $variable, $encoding );
587 
588                 return;
589             } elseif ( $variable !== $strippedString ) { // omit no data from display
590 
591                 $variableData->value         = '"' . self::escape( $variable, $encoding ) . '"';
592                 $variableData->extendedValue = self::escape( $variable, $encoding );
593 
594                 return;
595             }
596         }
597 
598         $variableData->value = '"' . self::escape( $variable, $encoding ) . '"';
599     }
600 
601     private static function _parse_unknown( &$variable, kintVariableData $variableData )
602     {
603         $type                = gettype( $variable );
604         $variableData->type  = "UNKNOWN" . ( !empty( $type ) ? " ({$type})" : '' );
605         $variableData->value = var_export( $variable, true );
606     }
607 
608 }
AloFramework Logger API documentation generated byApiGen 2.8.0