yf_ssh class

SSH Client (based on SSH2 PHP extension).

Fileplugins/ssh/classes/yf_ssh.class.php
Use_class('ssh')

Public properties

A site sets them in its config with $PROJECT_CONF['ssh']['NAME'] = ...;

PropertyDefaultDescription
$AUTH_TYPE
enum('password','pubkey')
'password'
$CONNECT_TIMEOUT
bool
5
$DRIVER
string
'phpseclib' @conf_skip array('phpseclib','pecl_ssh2','auto')
$GZIP_PATH
string
'' Path to the gzip console programm
$LOG_ACTIONS
bool
false Save actions log or not
$LOG_FULL_EXEC
bool
0
$MASS_USE_ARCHIVES
bool
true Use archiving for mass actions
$MAX_RECONNECTS
bool
1
$TAR_PATH
string
'' Path to the tar archiver in console
$USE_GZIP
bool
true
$_INIT_OK
bool
false @conf_skip
$_TMP_DIR
string
'uploads/tmp' @conf_skip
$_debug
bool
[]
$_sftp_connected null
$_sftp_try_to_connect null
$_ssh_cache_os null
$_ssh_connected null
$_ssh_try_to_connect null
$shell null

Public methods 30

chmod

public function chmod($server_info = [], $path = '', $new_mode = null, $recursively = false)

Chmod remote file.

    public function chmod($server_info = [], $path = '', $new_mode = null, $recursively = false)
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $path, $new_mode, $recursively);
    }

chown

public function chown($server_info = [], $path = '', $new_owner = '', $new_group = '', $recursively = false)

Chown remote file.

    public function chown($server_info = [], $path = '', $new_owner = '', $new_group = '', $recursively = false)
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $path, $new_owner, $new_group, $recursively);
    }

clean_path

public function clean_path($path = '')

Clean path from SFTP prefix (usually for pretty output for user).

    public function clean_path($path = '')
    {
        $pattern = '#^(ssh2\.sftp://Resource id \#[0-9]+)#ims';
        if (is_array($path)) {
            // Get current resource string
            $cur = current($path);
            if (is_array($cur)) {
                $cur = current($cur);
            }
            preg_match($pattern, $cur, $m);
            return str_replace($m[1], '', $path);
        }
        return preg_replace($pattern, '', $path);
    }

connect

public function connect($server_info = [])

Connect to the remote server.

@example $server_info = array( 'ssh_host' => '192.168.1.2', 'ssh_user' => 'root', 'ssh_pswd' => '111111', );
    public function connect($server_info = [])
    {
        if ( ! $this->_INIT_OK || ! $server_info) {
            return false;
        }
        $ssh_host = $server_info['base_ip'] ? $server_info['base_ip'] : $server_info['ssh_host'];
        $ssh_port = $server_info['ssh_port'] ? $server_info['ssh_port'] : 22;
        if ( ! $ssh_host) {
            trigger_error('SSH: missing server IP to connect', E_USER_WARNING);
            return false;
        }
        $_SERVER_ID = $this->_get_server_id($server_info);
        // Cache calls to the same server
        if (isset($this->_ssh_connected[$_SERVER_ID])) {
            return $this->_ssh_connected[$_SERVER_ID];
        }
        if ($this->_ssh_try_to_connect[$_SERVER_ID] >= $this->MAX_RECONNECTS) {
            return $this->_ssh_connected[$_SERVER_ID];
        }
        $ssh_user = $server_info['ssh_user'] ? $server_info['ssh_user'] : 'root';
        $ssh_pswd = $server_info['ssh_pswd'];
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        // Try to connect to server with selected params
        // This avoid long timeouts if server not connected
        $fp = fsockopen($ssh_host, $ssh_port, $errno, $errstr, $this->CONNECT_TIMEOUT);
        if ( ! $fp) {
            $this->_ssh_try_to_connect[$_SERVER_ID]++;
            trigger_error('SSH: cannot connect to: ' . $_SERVER_ID . '', E_USER_WARNING);
            return false;
        }
        fclose($fp);

        $con = null;
        $auth_result = null;
        // IMPORTANT: for best execution speed need to do: apt-get install php5-gmp php5-mcrypt php5-mhash
        if ($this->DRIVER == 'phpseclib') {
            $use_pswd = true;
            if ( ! empty($server_info['ssh_key_private'])) {
                $use_pswd = false;
            }
            if ( ! $use_pswd) {
                $key = new Crypt_RSA();
                if ($server_info['ssh_key_pswd']) {
                    $key->setPassword($server_info['ssh_key_pswd']); // password for key
                }
                $key_result = $key->loadKey(file_get_contents($server_info['ssh_key_private']));
                if ( ! $key_result) {
                    $this->_ssh_try_to_connect[$_SERVER_ID]++;
                    trigger_error('SSH: wrong key: ' . $server_info['ssh_key_private'] . ' for: ' . $_SERVER_ID . '', E_USER_WARNING);
                    return false;
                }
            }
            $con = new Net_SSH2($ssh_host);
            $auth_result = $con->login($ssh_user, $use_pswd ? $ssh_pswd : $key);
        } elseif ($this->DRIVER == 'pecl_ssh2') {
            $con = ssh2_connect($ssh_host, $ssh_port, null, []);
            // Try to authenticate
            $auth_result = false;
            if ($con) {
                if ( ! empty($server_info['ssh_key_public']) && ! empty($server_info['ssh_key_private'])) {
                    // using public key
                    $auth_result = ssh2_auth_pubkey_file($con, $ssh_user, $server_info['ssh_key_public'], $server_info['ssh_key_private']);
                } else {
                    // using plain password
                    $auth_result = ssh2_auth_password($con, $ssh_user, $ssh_pswd);
                }
            }
        }
        if (DEBUG_MODE) {
            $this->_debug['connect_time'] = microtime(true) - $time_start;
        }
        if ( ! $con) {
            $this->_ssh_try_to_connect[$_SERVER_ID]++;
            trigger_error('SSH: cannot connect to: ' . $_SERVER_ID, E_USER_WARNING);
            return false;
        }
        if ($auth_result) {
            $this->_ssh_connected[$_SERVER_ID] = $con;
            $this->_log($server_info, __FUNCTION__, 'user: ' . $ssh_user . ', auth successful');
            return $con;
        }
        trigger_error('SSH: auth on: ' . $ssh_host . ':' . $ssh_port . ' failed for ' . ($this->AUTH_TYPE == 'pubkey' ? 'pubkey: ' . $server_info['pubkey_path'] : 'user: ' . $ssh_user . ''), E_USER_WARNING);

        return false;
    }

download_dir

public function download_dir($server_info = [], $remote_dir = '', $local_dir = '', $pattern_include = '', $pattern_exclude = '', $level = null)

Copy remote dir structure into local one (bulk method).

    public function download_dir($server_info = [], $remote_dir = '', $local_dir = '', $pattern_include = '', $pattern_exclude = '', $level = null)
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $remote_dir, $local_dir, $pattern_include, $pattern_exclude, $level);
    }

exec

public function exec($server_info = [], $cmd = '')

Executes remote command on given shell and returns result.

    public function exec($server_info = [], $cmd = '')
    {
        $debug_info = null;
        if ( ! $this->_INIT_OK || ! $cmd || ! $server_info) {
            return false;
        }
        $con = $this->connect($server_info);
        if ( ! $con) {
            return false;
        }
        // Execute several commands per one call ($cmd as array)
        if (is_array($cmd)) {
            $result = [];
            foreach ((array) $cmd as $k => $v) {
                $result[$k] = $this->exec($server_info, $v);
            }
            return $result;
        }
        $data = false;
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        // execute a command
        if ($this->DRIVER == 'phpseclib') {
            $data = $con->exec($cmd);
            if (false === $data) {
                trigger_error('SSH: failed to execute remote command', E_USER_WARNING);
                return false;
            }
        } elseif ($this->DRIVER == 'pecl_ssh2') {
            if ( ! ($stream = ssh2_exec($con, $cmd, false))) {
                trigger_error('SSH: failed to execute remote command', E_USER_WARNING);
                return false;
            }
            // collect returning data from command
            stream_set_blocking($stream, true);
            $data = '';
            while ($buf = fgets($stream)) {
                $data .= $buf;
            }
            fclose($stream);
        }
        if (DEBUG_MODE) {
            $exec_time = microtime(true) - $time_start;
            $debug_info = '<b>' . common()->_format_time_value($exec_time) . ' sec</b>,' . PHP_EOL;
            $debug_info .= 'func: <b>' . __FUNCTION__ . '</b>, server: ' . $server_info['base_ip'] . ',' . PHP_EOL;
            $debug_info .= "cmd: \"<b style='color:blue;'>" . $cmd . '</b>" ' . ($this->LOG_FULL_EXEC ? ", <b>response</b>:<br />\n <pre><small>" . trim($data) . "</small></pre>\n" : '') . '<br />';
            $this->_debug['exec'][] = $debug_info;
            $this->_debug['time_sum'] += $exec_time;
        }
        $this->_log($server_info, __FUNCTION__, $cmd);
        return $data;
    }

file_exists

public function file_exists($server_info = [], $path = '')

Check if file exists remotely.

    public function file_exists($server_info = [], $path = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $path);
    }

file_info

public function file_info($server_info = [], $path = '')

Get selected file info.

    public function file_info($server_info = [], $path = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $path);
    }

mkdir

public function mkdir($server_info = [], $dir_name = '', $dir_mode = 755, $create_index_htmls = 0, $start_folder = '')

Alias for the mkdir_m.

    public function mkdir($server_info = [], $dir_name = '', $dir_mode = 755, $create_index_htmls = 0, $start_folder = '')
    {
        return $this->mkdir_m($server_info, $dir_name, $dir_mode, $create_index_htmls, $start_folder);
    }

mkdir_m

public function mkdir_m($server_info = [], $dir_name = '', $dir_mode = 755, $create_index_htmls = 0, $start_folder = '/')

Create multiple dirs at one time (eg. mkdir_m("some_dir1/some_dir2/some_dir3")).

    public function mkdir_m($server_info = [], $dir_name = '', $dir_mode = 755, $create_index_htmls = 0, $start_folder = '/')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $dir_name, $dir_mode, $create_index_htmls, $start_folder);
    }

read_file

public function read_file($server_info = [], $remote_file = '', $local_file = '')

Read remote file.

    public function read_file($server_info = [], $remote_file = '', $local_file = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $remote_file, $local_file);
    }

realpath

public function realpath($server_info = [], $path = '')

Resolve full path for the given file, dir or link.

    public function realpath($server_info = [], $path = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $path);
    }

rename

public function rename($server_info = [], $old_name = '', $new_name = '')

Rename remote file, dir or link.

    public function rename($server_info = [], $old_name = '', $new_name = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $old_name, $new_name);
    }

rmdir

public function rmdir($server_info = [], $path = '')

Remove remote dir.

    public function rmdir($server_info = [], $path = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $path);
    }

scan_dir

public function scan_dir($server_info = [], $start_dir = '', $pattern_include = '', $pattern_exclude = '', $level = 0, $single_file = '')

Scan remote dir and return array of files details.

    public function scan_dir($server_info = [], $start_dir = '', $pattern_include = '', $pattern_exclude = '', $level = 0, $single_file = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $start_dir, $pattern_include, $pattern_exclude, $level, $single_file);
    }

shell_exec

public function shell_exec($server_info = [], $cmd = '')

Remote shell exec.

    public function shell_exec($server_info = [], $cmd = '')
    {
        $debug_info = null;
        if ( ! $this->_INIT_OK || ! $cmd || ! $server_info) {
            return false;
        }
        if ( ! ($con = $this->connect($server_info))) {
            return false;
        }
        // Execute several commands per one call ($cmd as array)
        if (is_array($cmd)) {
            $result = [];
            foreach ((array) $cmd as $k => $v) {
                $result[$k] = $this->shell_exec($server_info, $v);
            }
            return $result;
        }
        $data = false;
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        // execute a command
        if ($this->DRIVER == 'phpseclib') {

            // Really not supported by lib, but should work as wrapper for exec
            $data = $con->exec($cmd);
            if (false === $data) {
                trigger_error('SSH: failed to execute remote command', E_USER_WARNING);
                return false;
            }
        } elseif ($this->DRIVER == 'pecl_ssh2') {
            if ( ! isset($this->shell)) {
                $this->shell = ssh2_shell($con, 'xterm');
            }

            if ( ! (fwrite($this->shell, $cmd . PHP_EOL))) {
                trigger_error('SSH: failed to execute remote command', E_USER_WARNING);
                return false;
            }
            // collect returning data from command
            stream_set_blocking($this->shell, true);
            $data = '';
            while ($buf = fgets($this->shell)) {
                $data .= $buf;
            }
            fclose($this->shell);
        }

        if (DEBUG_MODE) {
            $exec_time = microtime(true) - $time_start;
            $debug_info = '<b>' . common()->_format_time_value($exec_time) . " sec</b>,\n";
            $debug_info .= 'func: <b>' . __FUNCTION__ . '</b>, server: ' . $server_info['base_ip'] . ",\n";
            $debug_info .= "cmd: \"<b style='color:blue;'>" . $cmd . '</b>" ' . ($this->LOG_FULL_EXEC ? ", <b>response</b>:<br />\n <pre><small>" . trim($data) . "</small></pre>\n" : '') . '<br />';
            $this->_debug['exec'][] = $debug_info;
            $this->_debug['time_sum'] += $exec_time;
        }
        $this->_log($server_info, __FUNCTION__, $cmd);
        return $data;
    }

upload_dir

public function upload_dir($server_info = [], $local_dir = '', $remote_dir = '', $pattern_include = '', $pattern_exclude = '', $level = null)

Copy local dir structure into remote one (bulk method).

    public function upload_dir($server_info = [], $local_dir = '', $remote_dir = '', $pattern_include = '', $pattern_exclude = '', $level = null)
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $local_dir, $remote_dir, $pattern_include, $pattern_exclude, $level);
    }

write_file

public function write_file($server_info = [], $local_file = '', $remote_file = '')

Write local file into remote file.

    public function write_file($server_info = [], $local_file = '', $remote_file = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $local_file, $remote_file);
    }

write_string

public function write_string($server_info = [], $string = '', $remote_file = '')

Write string into remote file.

    public function write_string($server_info = [], $string = '', $remote_file = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_files', 'classes/ssh/')->$f($server_info, $string, $remote_file);
    }

Methods starting with _ hooks and helpers; not routable as page actions

__call

public function __call($name, $args)

Catch missing method call.

    public function __call($name, $args)
    {
        return main()->extend_call($this, $name, $args);
    }
public function _get_remote_os($server_info = [])

Return remote OS string.

    public function _get_remote_os($server_info = [])
    {
        $_SERVER_ID = $this->_get_server_id($server_info);
        if (isset($this->_ssh_cache_os[$_SERVER_ID])) {
            return $this->_ssh_cache_os[$_SERVER_ID];
        }
        $result = strtoupper(trim($this->exec($server_info, 'uname')));
        $this->_ssh_cache_os[$_SERVER_ID] = $result;
        return $result;
    }
public function _get_server_id($server_info = [])

Return internal SERVER_ID (usually 'ssh_host:ssh_port').

    public function _get_server_id($server_info = [])
    {
        if ( ! $server_info) {
            return false;
        }
        $ssh_host = $server_info['base_ip'] ? $server_info['base_ip'] : $server_info['ssh_host'];
        $ssh_port = $server_info['ssh_port'] ? $server_info['ssh_port'] : 22;
        if ( ! $ssh_host) {
            return false;
        }
        return $ssh_host . ':' . $ssh_port;
    }

_init

public function _init()

Framework constructor.

    public function _init()
    {
        if ( ! $this->DRIVER) {
            $this->DRIVER = 'phpseclib';
        }
        if ($this->DRIVER == 'phpseclib') {
            require_php_lib('phpseclib_v1');
        }
        if ($this->DRIVER == 'phpseclib' && ! class_exists('Net_SSH2')) {
            trigger_error('phpseclib Net_SSH2 not found', E_USER_WARNING);
            return false;
        } elseif ($this->DRIVER == 'pecl_ssh2' && ! function_exists('ssh2_connect')) {
            trigger_error('function ssh2_connect does not exist', E_USER_WARNING);
            return false;
        }
        $this->_INIT_OK = true;
    }
public function _init_sftp_phpseclib($server_info = [])

SFTP subsystem for phpseclib.

    public function _init_sftp_phpseclib($server_info = [])
    {
        if ( ! $this->DRIVER == 'phpseclib') {
            return false;
        }
        if ( ! $this->_INIT_OK || ! $server_info) {
            return false;
        }
        $ssh_host = $server_info['base_ip'] ? $server_info['base_ip'] : $server_info['ssh_host'];
        $ssh_port = $server_info['ssh_port'] ? $server_info['ssh_port'] : 22;
        if ( ! $ssh_host) {
            trigger_error('SSH: missing server IP to connect', E_USER_WARNING);
            return false;
        }
        $_SERVER_ID = $this->_get_server_id($server_info);
        // Cache calls to the same server
        if (isset($this->_sftp_connected[$_SERVER_ID])) {
            return $this->_sftp_connected[$_SERVER_ID];
        }
        if ($this->_sftp_try_to_connect[$_SERVER_ID] >= $this->MAX_RECONNECTS) {
            return $this->_sftp_connected[$_SERVER_ID];
        }
        $ssh_user = $server_info['ssh_user'] ? $server_info['ssh_user'] : 'root';
        $ssh_pswd = $server_info['ssh_pswd'];
        $time_start = null;
        if (DEBUG_MODE) {
            $time_start = microtime(true);
        }
        // Try to connect to server with selected params
        // This avoid long timeouts if server not connected
        $fp = fsockopen($ssh_host, $ssh_port, $errno, $errstr, $this->CONNECT_TIMEOUT);
        if ( ! $fp) {
            $this->_ssh_try_to_connect[$_SERVER_ID]++;
            trigger_error('SSH: cannot connect to: ' . $_SERVER_ID, E_USER_WARNING);
            return false;
        }
        fclose($fp);


        $use_pswd = true;
        $key = null;
        if ($this->AUTH_TYPE == 'pubkey' && ! empty($server_info['ssh_key_private'])) {
            $use_pswd = false;
        }
        if ( ! $use_pswd) {
            $key = new Crypt_RSA();
            if ($server_info['ssh_key_pswd']) {
                $key->setPassword($server_info['ssh_key_pswd']); // password for key
            }
            $key_result = $key->loadKey(file_get_contents($server_info['ssh_key_private']));
            if ( ! $key_result) {
                $this->_ssh_try_to_connect[$_SERVER_ID]++;
                trigger_error('SSH: wrong key: ' . $server_info['ssh_key_private'] . ' for: ' . $_SERVER_ID . '', E_USER_WARNING);
                return false;
            }
        }

        require_once 'Net/SFTP.php';

        $con = new Net_SFTP($ssh_host);
        $auth_result = $con->login($ssh_user, $use_pswd ? $ssh_pswd : $key);

        if (DEBUG_MODE) {
            $this->_debug['connect_time'] += microtime(true) - $time_start;
        }
        if ( ! $con) {
            $this->_sftp_try_to_connect[$_SERVER_ID]++;
            trigger_error('SSH: cannot connect to: ' . $_SERVER_ID, E_USER_WARNING);
            return false;
        }
        if ($auth_result) {
            $this->_sftp_connected[$_SERVER_ID] = $con;
            $this->_log($server_info, __FUNCTION__, 'user: ' . $ssh_user . ', auth successful');
            return $con;
        }
        trigger_error('SSH: auth on ' . $ssh_host . ':' . $ssh_port . ' failed for ' . ($this->AUTH_TYPE == 'pubkey' ? 'pubkey: ' . $server_info['pubkey_path'] : 'user: ' . $ssh_user . ''), E_USER_WARNING);

        return false;
    }
public function _local_make_tar($files_list = [], $archive_path = '')

Compress files to tar archive (local).

    public function _local_make_tar($files_list = [], $archive_path = '')
    {
        $f = __FUNCTION__;
        return _class('ssh_tar', 'classes/ssh/')->$f($files_list, $archive_path);
    }

_log

public function _log($server_info = [], $action = '', $comment = '')

Log internal action (Currently we store here successful actions, not errors for debug).

    public function _log($server_info = [], $action = '', $comment = '')
    {
        if ( ! $this->LOG_ACTIONS) {
            return false;
        }
        $SERVER_ID = $this->_get_server_id($server_info);
        $sql_array = [
            'server_id' => _es($SERVER_ID),
            'microtime' => _es(str_replace(',', '.', microtime(true))),
            'init_type' => _es(MAIN_TYPE),
            'action' => _es($action),
            'comment' => _es($comment),
            'get_object' => _es($_GET['object'] ?? null),
            'get_action' => _es($_GET['action'] ?? null),
            'user_id' => (int) ($_SESSION['user_id'] ?? null),
            'user_group' => (int) ($_SESSION['user_group'] ?? null),
            'ip' => _es(common()->get_ip()),
        ];
        return db()->INSERT('log_ssh_action', $sql_array);
    }

_perm_str2num

public function _perm_str2num($perm = '')

Convert string permission output to numerical.

    public function _perm_str2num($perm = '')
    {
        $own = $grp = $oth = null;
        $perm_len = strlen(trim($perm));
        if ($perm_len > 10 && $perm_len < 9) {
            return false;
        }
        if ($perm_len == 10) {
            $perm = substr($perm, 1);
        }
        // Compatibility with sticky bit, setuid, setgid (http://en.wikipedia.org/wiki/File_system_permissions)
        $perm = str_replace(['s', 'S', 't', 'T'], ['x', '-', 'x', '-'], $perm);
        $own = 0;
        $grp = 0;
        $oth = 0;

        foreach ((array) str_split($perm) as $k => $v) {
            if ($v == '-') {
                continue;
            }
            // Owner
            if ($k == 0) {
                $own += 4;
            }
            if ($k == 1) {
                $own += 2;
            }
            if ($k == 2) {
                $own += 1;
            }
            // Group
            if ($k == 3) {
                $grp += 4;
            }
            if ($k == 4) {
                $grp += 2;
            }
            if ($k == 5) {
                $grp += 1;
            }
            // Others
            if ($k == 6) {
                $oth += 4;
            }
            if ($k == 7) {
                $oth += 2;
            }
            if ($k == 8) {
                $oth += 1;
            }
        }
        return '0' . $own . '' . $grp . '' . $oth;
    }

_prepare_path

public function _prepare_path($path = '')

Prepare path, Prevent some hacks and misuses.

    public function _prepare_path($path = '')
    {
        if (is_array($path)) {
            foreach ((array) $path as $k => $v) {
                $path[$k] = $this->_prepare_path($v);
            }
            return $path;
        }
        $bad_chars = ['`', '"', "'", '..', '~', ' ', "\t", "\r", "\n", '|', '<', '>', '&'];
        $result = str_replace($bad_chars, '', rtrim(str_replace(['\\', '//', '///'], '/', trim($path)), '/'));
        return $result ? $result : '/';
    }
public function _skip_by_pattern($path = '', $_type = 'f', $pattern_include = '', $pattern_exclude = '')

Recursively scanning directory structure (including subdirectories) //.

    public function _skip_by_pattern($path = '', $_type = 'f', $pattern_include = '', $pattern_exclude = '')
    {
        $type = null;
        if ( ! $path) {
            return false;
        }
        if ( ! $pattern_include && ! $pattern_exclude) {
            return false;
        }
        if ( ! $_type || ! in_array($_type, ['f', 'd', 'l'])) {
            return false;
        }
        $_path_clean = trim(str_replace('//', '/', str_replace('\\', '/', $path)));
        $_index = null;
        // Include files only if they match the mask
        if ($_type == 'd') {
            $_index = 0;
            $_path_clean = rtrim($_path_clean, '/');
        } elseif ($_type == 'f') {
            $_index = 1;
        } elseif ($_type == 'l') {
            $_index = 2;
        }
        if (is_array($pattern_include)) {
            $pattern_include = $pattern_include[$_index];
        }
        if (is_array($pattern_exclude)) {
            $pattern_exclude = $pattern_exclude[$_index];
        }
        if ( ! empty($pattern_include) && is_string($pattern_include)) {
            // Matching file type: "-d" alone, or "-f /regex/"
            if ($pattern_include[0] == '-') {
                if ($_type != ($pattern_include[1] ?? '')) {
                    return true;
                }
                $pattern_include = trim(substr($pattern_include, 2));
            }
            // Regex searching
            if ($pattern_include !== '' && ! preg_match($pattern_include . 'ims', $_path_clean)) {
                return true;
            }
        }
        // Exclude files from list by mask
        if ( ! empty($pattern_exclude) && is_string($pattern_exclude)) {
            // Matching file type: "-d" alone, or "-f /regex/"
            if ($pattern_exclude[0] == '-') {
                if ($_type != ($pattern_exclude[1] ?? '')) {
                    return false;
                }
                $pattern_exclude = trim(substr($pattern_exclude, 2));
            }
            // Regex searching
            if ($pattern_exclude === '' || preg_match($pattern_exclude . 'ims', $_path_clean)) {
                return true;
            }
        }
        return false;
    }