yf_ssh classSSH Client (based on SSH2 PHP extension).
| File | plugins/ssh/classes/yf_ssh.class.php |
|---|---|
| Use | _class('ssh') |
A site sets them in its config with $PROJECT_CONF['ssh']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$AUTH_TYPEenum('password','pubkey') |
'password' |
|
$CONNECT_TIMEOUTbool |
5 |
|
$DRIVERstring |
'phpseclib' |
@conf_skip array('phpseclib','pecl_ssh2','auto') |
$GZIP_PATHstring |
'' |
Path to the gzip console programm |
$LOG_ACTIONSbool |
false |
Save actions log or not |
$LOG_FULL_EXECbool |
0 |
|
$MASS_USE_ARCHIVESbool |
true |
Use archiving for mass actions |
$MAX_RECONNECTSbool |
1 |
|
$TAR_PATHstring |
'' |
Path to the tar archiver in console |
$USE_GZIPbool |
true |
|
$_INIT_OKbool |
false |
@conf_skip |
$_TMP_DIRstring |
'uploads/tmp' |
@conf_skip |
$_debugbool |
[] |
|
$_sftp_connected |
null |
|
$_sftp_try_to_connect |
null |
|
$_ssh_cache_os |
null |
|
$_ssh_connected |
null |
|
$_ssh_try_to_connect |
null |
|
$shell |
null |
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);
}
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);
}
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);
}
public function connect($server_info = [])
Connect to the remote server.
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;
}
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);
}
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;
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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;
}
public function unlink($server_info = [], $path = '')
Unlink remote file or link.
public function unlink($server_info = [], $path = '')
{
$f = __FUNCTION__;
return _class('ssh_files', 'classes/ssh/')->$f($server_info, $path);
}
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);
}
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);
}
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);
}
_ hooks and helpers; not routable as page actionspublic 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;
}
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);
}
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);
}
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;
}
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;
}