ref::evaluate PHP Method

evaluate() protected method

Evaluates the given variable
protected evaluate ( &$subject, boolean $specialStr = false ) : mixed
$specialStr boolean Should this be interpreted as a special string?
return mixed Result (both HTML and text modes generate strings)
    protected function evaluate(&$subject, $specialStr = false)
    {
        switch ($type = gettype($subject)) {
            // https://github.com/digitalnature/php-ref/issues/13
            case 'unknown type':
                return $this->fmt->text('unknown');
                // null value
            // null value
            case 'NULL':
                return $this->fmt->text('null');
                // integer/double/float
            // integer/double/float
            case 'integer':
            case 'double':
                return $this->fmt->text($type, $subject, $type);
                // boolean
            // boolean
            case 'boolean':
                $text = $subject ? 'true' : 'false';
                return $this->fmt->text($text, $text, $type);
                // arrays
            // arrays
            case 'array':
                // empty array?
                if (empty($subject)) {
                    $this->fmt->text('array');
                    return $this->fmt->emptyGroup();
                }
                if (isset($subject[static::MARKER_KEY])) {
                    unset($subject[static::MARKER_KEY]);
                    $this->fmt->text('array');
                    $this->fmt->emptyGroup('recursion');
                    return;
                }
                // first recursion level detection;
                // this is optional (used to print consistent recursion info)
                foreach ($subject as $key => &$value) {
                    if (!is_array($value)) {
                        continue;
                    }
                    // save current value in a temporary variable
                    $buffer = $value;
                    // assign new value
                    $value = $value !== 1 ? 1 : 2;
                    // if they're still equal, then we have a reference
                    if ($value === $subject) {
                        $value = $buffer;
                        $value[static::MARKER_KEY] = true;
                        $this->evaluate($value);
                        return;
                    }
                    // restoring original value
                    $value = $buffer;
                }
                $this->fmt->text('array');
                $count = count($subject);
                if (!$this->fmt->startGroup($count)) {
                    return;
                }
                $max = max(array_map('static::strLen', array_keys($subject)));
                $subject[static::MARKER_KEY] = true;
                foreach ($subject as $key => &$value) {
                    // ignore our temporary marker
                    if ($key === static::MARKER_KEY) {
                        continue;
                    }
                    if ($this->hasInstanceTimedOut()) {
                        break;
                    }
                    $keyInfo = gettype($key);
                    if ($keyInfo === 'string') {
                        $encoding = static::$env['mbStr'] ? mb_detect_encoding($key) : '';
                        $keyLen = $encoding && $encoding !== 'ASCII' ? static::strLen($key) . '; ' . $encoding : static::strLen($key);
                        $keyInfo = "{$keyInfo}({$keyLen})";
                    } else {
                        $keyLen = strlen($key);
                    }
                    $this->fmt->startRow();
                    $this->fmt->text('key', $key, "Key: {$keyInfo}");
                    $this->fmt->colDiv($max - $keyLen);
                    $this->fmt->sep('=>');
                    $this->fmt->colDiv();
                    $this->evaluate($value, $specialStr);
                    $this->fmt->endRow();
                }
                unset($subject[static::MARKER_KEY]);
                $this->fmt->endGroup();
                return;
                // resource
            // resource
            case 'resource':
                $meta = array();
                $resType = get_resource_type($subject);
                $this->fmt->text('resource', strval($subject));
                if (!static::$config['showResourceInfo']) {
                    return $this->fmt->emptyGroup($resType);
                }
                // @see: http://php.net/manual/en/resource.php
                // need to add more...
                switch ($resType) {
                    // curl extension resource
                    case 'curl':
                        $meta = curl_getinfo($subject);
                        break;
                    case 'FTP Buffer':
                        $meta = array('time_out' => ftp_get_option($subject, FTP_TIMEOUT_SEC), 'auto_seek' => ftp_get_option($subject, FTP_AUTOSEEK));
                        break;
                        // gd image extension resource
                    // gd image extension resource
                    case 'gd':
                        $meta = array('size' => sprintf('%d x %d', imagesx($subject), imagesy($subject)), 'true_color' => imageistruecolor($subject));
                        break;
                    case 'ldap link':
                        $constants = get_defined_constants();
                        array_walk($constants, function ($value, $key) use(&$constants) {
                            if (strpos($key, 'LDAP_OPT_') !== 0) {
                                unset($constants[$key]);
                            }
                        });
                        // this seems to fail on my setup :(
                        unset($constants['LDAP_OPT_NETWORK_TIMEOUT']);
                        foreach (array_slice($constants, 3) as $key => $value) {
                            if (ldap_get_option($subject, (int) $value, $ret)) {
                                $meta[strtolower(substr($key, 9))] = $ret;
                            }
                        }
                        break;
                        // mysql connection (mysql extension is deprecated from php 5.4/5.5)
                    // mysql connection (mysql extension is deprecated from php 5.4/5.5)
                    case 'mysql link':
                    case 'mysql link persistent':
                        $dbs = array();
                        $query = @mysql_list_dbs($subject);
                        while ($row = @mysql_fetch_array($query)) {
                            $dbs[] = $row['Database'];
                        }
                        $meta = array('host' => ltrim(@mysql_get_host_info($subject), 'MySQL host info: '), 'server_version' => @mysql_get_server_info($subject), 'protocol_version' => @mysql_get_proto_info($subject), 'databases' => $dbs);
                        break;
                        // mysql result
                    // mysql result
                    case 'mysql result':
                        while ($row = @mysql_fetch_object($subject)) {
                            $meta[] = (array) $row;
                            if ($this->hasInstanceTimedOut()) {
                                break;
                            }
                        }
                        break;
                        // stream resource (fopen, fsockopen, popen, opendir etc)
                    // stream resource (fopen, fsockopen, popen, opendir etc)
                    case 'stream':
                        $meta = stream_get_meta_data($subject);
                        break;
                }
                if (!$meta) {
                    return $this->fmt->emptyGroup($resType);
                }
                if (!$this->fmt->startGroup($resType)) {
                    return;
                }
                $max = max(array_map('static::strLen', array_keys($meta)));
                foreach ($meta as $key => $value) {
                    $this->fmt->startRow();
                    $this->fmt->text('resourceProp', ucwords(str_replace('_', ' ', $key)));
                    $this->fmt->colDiv($max - static::strLen($key));
                    $this->fmt->sep(':');
                    $this->fmt->colDiv();
                    $this->evaluate($value);
                    $this->fmt->endRow();
                }
                $this->fmt->endGroup();
                return;
                // string
            // string
            case 'string':
                $length = static::strLen($subject);
                $encoding = static::$env['mbStr'] ? mb_detect_encoding($subject) : false;
                $info = $encoding && $encoding !== 'ASCII' ? $length . '; ' . $encoding : $length;
                if ($specialStr) {
                    $this->fmt->sep('"');
                    $this->fmt->text(array('string', 'special'), $subject, "string({$info})");
                    $this->fmt->sep('"');
                    return;
                }
                $this->fmt->text('string', $subject, "string({$info})");
                // advanced checks only if there are 3 characteres or more
                if (static::$config['showStringMatches'] && $length > 2 && trim($subject) !== '') {
                    $isNumeric = is_numeric($subject);
                    // very simple check to determine if the string could match a file path
                    // @note: this part of the code is very expensive
                    $isFile = $length < 2048 && max(array_map('strlen', explode('/', str_replace('\\', '/', $subject)))) < 128 && !preg_match('/[^\\w\\.\\-\\/\\\\:]|\\..*\\.|\\.$|:(?!(?<=^[a-zA-Z]:)[\\/\\\\])/', $subject);
                    if ($isFile) {
                        try {
                            $file = new \SplFileInfo($subject);
                            $flags = array();
                            $perms = $file->getPerms();
                            if (($perms & 0xc000) === 0xc000) {
                                // socket
                                $flags[] = 's';
                            } elseif (($perms & 0xa000) === 0xa000) {
                                // symlink
                                $flags[] = 'l';
                            } elseif (($perms & 0x8000) === 0x8000) {
                                // regular
                                $flags[] = '-';
                            } elseif (($perms & 0x6000) === 0x6000) {
                                // block special
                                $flags[] = 'b';
                            } elseif (($perms & 0x4000) === 0x4000) {
                                // directory
                                $flags[] = 'd';
                            } elseif (($perms & 0x2000) === 0x2000) {
                                // character special
                                $flags[] = 'c';
                            } elseif (($perms & 0x1000) === 0x1000) {
                                // FIFO pipe
                                $flags[] = 'p';
                            } else {
                                // unknown
                                $flags[] = 'u';
                            }
                            // owner
                            $flags[] = $perms & 0x100 ? 'r' : '-';
                            $flags[] = $perms & 0x80 ? 'w' : '-';
                            $flags[] = $perms & 0x40 ? $perms & 0x800 ? 's' : 'x' : ($perms & 0x800 ? 'S' : '-');
                            // group
                            $flags[] = $perms & 0x20 ? 'r' : '-';
                            $flags[] = $perms & 0x10 ? 'w' : '-';
                            $flags[] = $perms & 0x8 ? $perms & 0x400 ? 's' : 'x' : ($perms & 0x400 ? 'S' : '-');
                            // world
                            $flags[] = $perms & 0x4 ? 'r' : '-';
                            $flags[] = $perms & 0x2 ? 'w' : '-';
                            $flags[] = $perms & 0x1 ? $perms & 0x200 ? 't' : 'x' : ($perms & 0x200 ? 'T' : '-');
                            $size = is_dir($subject) ? '' : sprintf(' %.2fK', $file->getSize() / 1024);
                            $this->fmt->startContain('file', true);
                            $this->fmt->text('file', implode('', $flags) . $size);
                            $this->fmt->endContain();
                        } catch (\Exception $e) {
                            $isFile = false;
                        }
                    }
                    // class/interface/function
                    if (!preg_match('/[^\\w+\\\\]/', $subject) && $length < 96) {
                        $isClass = class_exists($subject, false);
                        if ($isClass) {
                            $this->fmt->startContain('class', true);
                            $this->fromReflector(new \ReflectionClass($subject));
                            $this->fmt->endContain();
                        }
                        if (!$isClass && interface_exists($subject, false)) {
                            $this->fmt->startContain('interface', true);
                            $this->fromReflector(new \ReflectionClass($subject));
                            $this->fmt->endContain('interface');
                        }
                        if (function_exists($subject)) {
                            $this->fmt->startContain('function', true);
                            $this->fromReflector(new \ReflectionFunction($subject));
                            $this->fmt->endContain('function');
                        }
                    }
                    // skip serialization/json/date checks if the string appears to be numeric,
                    // or if it's shorter than 5 characters
                    if (!$isNumeric && $length > 4) {
                        // url
                        if (static::$config['showUrls'] && static::$env['curlActive'] && filter_var($subject, FILTER_VALIDATE_URL)) {
                            $ch = curl_init($subject);
                            curl_setopt($ch, CURLOPT_NOBODY, true);
                            curl_exec($ch);
                            $nfo = curl_getinfo($ch);
                            curl_close($ch);
                            if ($nfo['http_code']) {
                                $this->fmt->startContain('url', true);
                                $contentType = explode(';', $nfo['content_type']);
                                $this->fmt->text('url', sprintf('%s:%d %s %.2fms (%d)', !empty($nfo['primary_ip']) ? $nfo['primary_ip'] : null, !empty($nfo['primary_port']) ? $nfo['primary_port'] : null, $contentType[0], $nfo['total_time'], $nfo['http_code']));
                                $this->fmt->endContain();
                            }
                        }
                        // date
                        if ($length < 128 && static::$env['supportsDate'] && !preg_match('/[^A-Za-z0-9.:+\\s\\-\\/]/', $subject)) {
                            try {
                                $date = new \DateTime($subject);
                                $errors = \DateTime::getLastErrors();
                                if ($errors['warning_count'] < 1 && $errors['error_count'] < 1) {
                                    $now = new \Datetime('now');
                                    $nowUtc = new \Datetime('now', new \DateTimeZone('UTC'));
                                    $diff = $now->diff($date);
                                    $map = array('y' => 'yr', 'm' => 'mo', 'd' => 'da', 'h' => 'hr', 'i' => 'min', 's' => 'sec');
                                    $timeAgo = 'now';
                                    foreach ($map as $k => $label) {
                                        if ($diff->{$k} > 0) {
                                            $timeAgo = $diff->format("%R%{$k}{$label}");
                                            break;
                                        }
                                    }
                                    $tz = $date->getTimezone();
                                    $offs = round($tz->getOffset($nowUtc) / 3600);
                                    if ($offs > 0) {
                                        $offs = "+{$offs}";
                                    }
                                    $timeAgo .= (int) $offs !== 0 ? ' ' . sprintf('%s (UTC%s)', $tz->getName(), $offs) : ' UTC';
                                    $this->fmt->startContain('date', true);
                                    $this->fmt->text('date', $timeAgo);
                                    $this->fmt->endContain();
                                }
                            } catch (\Exception $e) {
                                // not a date
                            }
                        }
                        // attempt to detect if this is a serialized string
                        static $unserializing = 0;
                        $isSerialized = $unserializing < 3 && ($subject[$length - 1] === ';' || $subject[$length - 1] === '}') && in_array($subject[0], array('s', 'a', 'O'), true) && ($subject[0] === 's' && $subject[$length - 2] !== '"' || preg_match("/^{$subject[0]}:[0-9]+:/s", $subject)) && ($unserialized = @unserialize($subject)) !== false;
                        if ($isSerialized) {
                            $unserializing++;
                            $this->fmt->startContain('serialized', true);
                            $this->evaluate($unserialized);
                            $this->fmt->endContain();
                            $unserializing--;
                        }
                        // try to find out if it's a json-encoded string;
                        // only do this for json-encoded arrays or objects, because other types have too generic formats
                        static $decodingJson = 0;
                        $isJson = !$isSerialized && $decodingJson < 3 && in_array($subject[0], array('{', '['), true);
                        if ($isJson) {
                            $decodingJson++;
                            $json = json_decode($subject);
                            if ($isJson = json_last_error() === JSON_ERROR_NONE) {
                                $this->fmt->startContain('json', true);
                                $this->evaluate($json);
                                $this->fmt->endContain();
                            }
                            $decodingJson--;
                        }
                        // attempt to match a regex
                        if ($length < 768) {
                            try {
                                $components = $this->splitRegex($subject);
                                if ($components) {
                                    $regex = '';
                                    $this->fmt->startContain('regex', true);
                                    foreach ($components as $component) {
                                        $this->fmt->text('regex-' . key($component), reset($component));
                                    }
                                    $this->fmt->endContain();
                                }
                            } catch (\Exception $e) {
                                // not a regex
                            }
                        }
                    }
                }
                return;
        }
        // if we reached this point, $subject must be an object
        // track objects to detect recursion
        static $hashes = array();
        // hash ID of this object
        $hash = spl_object_hash($subject);
        $recursion = isset($hashes[$hash]);
        // sometimes incomplete objects may be created from string unserialization,
        // if the class to which the object belongs wasn't included until the unserialization stage...
        if ($subject instanceof \__PHP_Incomplete_Class) {
            $this->fmt->text('object');
            $this->fmt->emptyGroup('incomplete');
            return;
        }
        // check cache at this point
        if (!$recursion && $this->fmt->didCache($hash)) {
            static::$debug['cacheHits']++;
            return;
        }
        $reflector = new \ReflectionObject($subject);
        $this->fmt->startContain('class');
        $this->fromReflector($reflector);
        $this->fmt->text('object', ' object');
        $this->fmt->endContain();
        // already been here?
        if ($recursion) {
            return $this->fmt->emptyGroup('recursion');
        }
        $hashes[$hash] = 1;
        $flags = \ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED;
        if (static::$config['showPrivateMembers']) {
            $flags |= \ReflectionProperty::IS_PRIVATE;
        }
        $props = $reflector->getProperties($flags);
        $methods = array();
        if (static::$config['showMethods']) {
            $flags = \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED;
            if (static::$config['showPrivateMembers']) {
                $flags |= \ReflectionMethod::IS_PRIVATE;
            }
            $methods = $reflector->getMethods($flags);
        }
        $constants = $reflector->getConstants();
        $interfaces = $reflector->getInterfaces();
        $traits = static::$env['is54'] ? $reflector->getTraits() : array();
        $parents = static::getParentClasses($reflector);
        // work-around for https://bugs.php.net/bug.php?id=49154
        // @see http://stackoverflow.com/questions/15672287/strange-behavior-of-reflectiongetproperties-with-numeric-keys
        if (!static::$env['is54']) {
            $props = array_values(array_filter($props, function ($prop) use($subject) {
                return !$prop->isPublic() || property_exists($subject, $prop->name);
            }));
        }
        // no data to display?
        if (!$props && !$methods && !$constants && !$interfaces && !$traits) {
            unset($hashes[$hash]);
            return $this->fmt->emptyGroup();
        }
        if (!$this->fmt->startGroup()) {
            return;
        }
        // show contents for iterators
        if (static::$config['showIteratorContents'] && $reflector->isIterateable()) {
            $itContents = iterator_to_array($subject);
            $this->fmt->sectionTitle(sprintf('Contents (%d)', count($itContents)));
            foreach ($itContents as $key => $value) {
                $keyInfo = gettype($key);
                if ($keyInfo === 'string') {
                    $encoding = static::$env['mbStr'] ? mb_detect_encoding($key) : '';
                    $length = $encoding && $encoding !== 'ASCII' ? static::strLen($key) . '; ' . $encoding : static::strLen($key);
                    $keyInfo = sprintf('%s(%s)', $keyInfo, $length);
                }
                $this->fmt->startRow();
                $this->fmt->text(array('key', 'iterator'), $key, sprintf('Iterator key: %s', $keyInfo));
                $this->fmt->colDiv();
                $this->fmt->sep('=>');
                $this->fmt->colDiv();
                $this->evaluate($value);
                //$this->evaluate($value instanceof \Traversable ? ((count($value) > 0) ? $value : (string)$value) : $value);
                $this->fmt->endRow();
            }
        }
        // display the interfaces this objects' class implements
        if ($interfaces) {
            $items = array();
            $this->fmt->sectionTitle('Implements');
            $this->fmt->startRow();
            $this->fmt->startContain('interfaces');
            $i = 0;
            $count = count($interfaces);
            foreach ($interfaces as $name => $interface) {
                $this->fromReflector($interface);
                if (++$i < $count) {
                    $this->fmt->sep(', ');
                }
            }
            $this->fmt->endContain();
            $this->fmt->endRow();
        }
        // traits this objects' class uses
        if ($traits) {
            $items = array();
            $this->fmt->sectionTitle('Uses');
            $this->fmt->startRow();
            $this->fmt->startContain('traits');
            $i = 0;
            $count = count($traits);
            foreach ($traits as $name => $trait) {
                $this->fromReflector($trait);
                if (++$i < $count) {
                    $this->fmt->sep(', ');
                }
            }
            $this->fmt->endContain();
            $this->fmt->endRow();
        }
        // class constants
        if ($constants) {
            $this->fmt->sectionTitle('Constants');
            $max = max(array_map('static::strLen', array_keys($constants)));
            foreach ($constants as $name => $value) {
                $meta = null;
                $type = array('const');
                foreach ($parents as $parent) {
                    if ($parent->hasConstant($name)) {
                        if ($parent !== $reflector) {
                            $type[] = 'inherited';
                            $meta = array('sub' => array(array('Prototype defined by', $parent->name)));
                        }
                        break;
                    }
                }
                $this->fmt->startRow();
                $this->fmt->sep('::');
                $this->fmt->colDiv();
                $this->fmt->startContain($type);
                $this->fmt->text('name', $name, $meta, $this->linkify($parent, $name));
                $this->fmt->endContain();
                $this->fmt->colDiv($max - static::strLen($name));
                $this->fmt->sep('=');
                $this->fmt->colDiv();
                $this->evaluate($value);
                $this->fmt->endRow();
            }
        }
        // object/class properties
        if ($props) {
            $this->fmt->sectionTitle('Properties');
            $max = 0;
            foreach ($props as $idx => $prop) {
                if (($propNameLen = static::strLen($prop->name)) > $max) {
                    $max = $propNameLen;
                }
            }
            foreach ($props as $idx => $prop) {
                if ($this->hasInstanceTimedOut()) {
                    break;
                }
                $bubbles = array();
                $sourceClass = $prop->getDeclaringClass();
                $inherited = $reflector->getShortName() !== $sourceClass->getShortName();
                $meta = $sourceClass->isInternal() ? null : static::parseComment($prop->getDocComment());
                if ($meta) {
                    if ($inherited) {
                        $meta['sub'] = array(array('Declared in', $sourceClass->getShortName()));
                    }
                    if (isset($meta['tags']['var'][0])) {
                        $meta['left'] = $meta['tags']['var'][0][0];
                    }
                    unset($meta['tags']);
                }
                if ($prop->isProtected() || $prop->isPrivate()) {
                    $prop->setAccessible(true);
                }
                $value = $prop->getValue($subject);
                $this->fmt->startRow();
                $this->fmt->sep($prop->isStatic() ? '::' : '->');
                $this->fmt->colDiv();
                $bubbles = array();
                if ($prop->isProtected()) {
                    $bubbles[] = array('P', 'Protected');
                }
                if ($prop->isPrivate()) {
                    $bubbles[] = array('!', 'Private');
                }
                $this->fmt->bubbles($bubbles);
                $type = array('prop');
                if ($inherited) {
                    $type[] = 'inherited';
                }
                if ($prop->isPrivate()) {
                    $type[] = 'private';
                }
                $this->fmt->colDiv(2 - count($bubbles));
                $this->fmt->startContain($type);
                $this->fmt->text('name', $prop->name, $meta, $this->linkify($prop));
                $this->fmt->endContain();
                $this->fmt->colDiv($max - static::strLen($prop->name));
                $this->fmt->sep('=');
                $this->fmt->colDiv();
                $this->evaluate($value);
                $this->fmt->endRow();
            }
        }
        // class methods
        if ($methods && !$this->hasInstanceTimedOut()) {
            $this->fmt->sectionTitle('Methods');
            foreach ($methods as $idx => $method) {
                $this->fmt->startRow();
                $this->fmt->sep($method->isStatic() ? '::' : '->');
                $this->fmt->colDiv();
                $bubbles = array();
                if ($method->isAbstract()) {
                    $bubbles[] = array('A', 'Abstract');
                }
                if ($method->isFinal()) {
                    $bubbles[] = array('F', 'Final');
                }
                if ($method->isProtected()) {
                    $bubbles[] = array('P', 'Protected');
                }
                if ($method->isPrivate()) {
                    $bubbles[] = array('!', 'Private');
                }
                $this->fmt->bubbles($bubbles);
                $this->fmt->colDiv(4 - count($bubbles));
                // is this method inherited?
                $inherited = $reflector->getShortName() !== $method->getDeclaringClass()->getShortName();
                $type = array('method');
                if ($inherited) {
                    $type[] = 'inherited';
                }
                if ($method->isPrivate()) {
                    $type[] = 'private';
                }
                $this->fmt->startContain($type);
                $name = $method->name;
                if ($method->returnsReference()) {
                    $name = "&{$name}";
                }
                $this->fromReflector($method, $name, $reflector);
                $paramCom = $method->isInternal() ? array() : static::parseComment($method->getDocComment(), 'tags');
                $paramCom = empty($paramCom['param']) ? array() : $paramCom['param'];
                $paramCount = $method->getNumberOfParameters();
                $this->fmt->sep('(');
                // process arguments
                foreach ($method->getParameters() as $idx => $parameter) {
                    $meta = null;
                    $paramName = "\${$parameter->name}";
                    $optional = $parameter->isOptional();
                    $variadic = static::$env['is56'] && $parameter->isVariadic();
                    if ($parameter->isPassedByReference()) {
                        $paramName = "&{$paramName}";
                    }
                    if ($variadic) {
                        $paramName = "...{$paramName}";
                    }
                    $type = array('param');
                    if ($optional) {
                        $type[] = 'optional';
                    }
                    $this->fmt->startContain($type);
                    // attempt to build meta
                    foreach ($paramCom as $tag) {
                        list($pcTypes, $pcName, $pcDescription) = $tag;
                        if ($pcName !== $paramName) {
                            continue;
                        }
                        $meta = array('title' => $pcDescription);
                        if ($pcTypes) {
                            $meta['left'] = $pcTypes;
                        }
                        break;
                    }
                    try {
                        $paramClass = $parameter->getClass();
                    } catch (\Exception $e) {
                        // @see https://bugs.php.net/bug.php?id=32177&edit=1
                    }
                    if (!empty($paramClass)) {
                        $this->fmt->startContain('hint');
                        $this->fromReflector($paramClass, $paramClass->name);
                        $this->fmt->endContain();
                        $this->fmt->sep(' ');
                    } elseif ($parameter->isArray()) {
                        $this->fmt->text('hint', 'array');
                        $this->fmt->sep(' ');
                    } else {
                        $hasType = static::$env['is7'] && $parameter->hasType();
                        if ($hasType) {
                            $type = $parameter->getType();
                            $this->fmt->text('hint', (string) $type);
                            $this->fmt->sep(' ');
                        }
                    }
                    $this->fmt->text('name', $paramName, $meta);
                    if ($optional) {
                        $paramValue = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null;
                        $this->fmt->sep(' = ');
                        if (static::$env['is546'] && !$parameter->getDeclaringFunction()->isInternal() && $parameter->isDefaultValueConstant()) {
                            $this->fmt->text('constant', $parameter->getDefaultValueConstantName(), 'Constant');
                        } else {
                            $this->evaluate($paramValue, true);
                        }
                    }
                    $this->fmt->endContain();
                    if ($idx < $paramCount - 1) {
                        $this->fmt->sep(', ');
                    }
                }
                $this->fmt->sep(')');
                $this->fmt->endContain();
                $hasReturnType = static::$env['is7'] && $method->hasReturnType();
                if ($hasReturnType) {
                    $type = $method->getReturnType();
                    $this->fmt->startContain('ret');
                    $this->fmt->sep(':');
                    $this->fmt->text('hint', (string) $type);
                    $this->fmt->endContain();
                }
                $this->fmt->endRow();
            }
        }
        unset($hashes[$hash]);
        $this->fmt->endGroup();
        $this->fmt->cacheLock($hash);
    }