Команды PHP SFTP через постоянное соединение

Я работаю над веб-сайтом, который будет действовать как онлайн-клиент SFTP для моей домашней машины. Решение, которое у меня есть до сих пор, представляет собой индексный (основной) файл php, который содержит пользовательский интерфейс сайта, и удобный класс SFTP PHP, который подключается к phpseclib, диспетчеру соединений SFTP.

index.php

<?php
require_once "php/Membership.php";
require_once "php/ssh.php";
require_once "php/sftp.php";

$sftp = new SFTP();

error_reporting(E_ALL);  // will report any errors your code may have
ini_set("display_errors", 1);

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!ATTLIST td fileName CDATA #IMPLIED>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SFTP</title>
<link href="index.css" rel="stylesheet" type="text/css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="index.js"></script>
</head>

<body>
<h1 id="welcome">Welcome</h1>

<div id="container">
<div id="content">

<!--SFTP Files-->
<div style="height:1000px; overflow:auto;">
<?php $sftp->set_table(NULL, NULL);?>
</div>


</div>
</div>
</body>
</html>

SFTP.php

<?php

include('Net/SFTP.php');

class SFTP {

    private $sftp;

    function __construct() {

     $this->sftp = new Net_SFTP('99.99.9999.999');
     if (!$this->sftp->login('user', 'pwd')) {
         exit('Login Failed');
     }
        echo $this->sftp->pwd() . "\r\n";
    }

    function set_table($table, $directory) {
        if (isset($directory)) {
            $this->sftp->chdir($directory);
        }
        echo '<table id="sftpTable" style="border:1px solid;">';
        $result = $this->sftp->nlist();
        foreach ($result as $row) {
            if (substr($row, 0, 1) != '.') {
                echo "<tr>" . "<td class='columnSelect' id=" . $row . "><form method='post' action=''>" . $row . "<input type=\"hidden\" name=\"index\" value=\"" . $row . "\" /></form></td>";
                if (strpos($row,'.') !== false)
                    echo '<td>'. $this->parseBytes($this->sftp->_size($row)) . '</td></tr>';
            }   
        }
        echo '</table>';
    }

    function parseBytes($bytes) {
        if ($bytes / 1074000000 >= 1) {
            return $bytes / 1074000000 . 'GB';
        }
        if ($bytes / 1048576 >= 1) {
            return $bytes / 1048576 . 'MB';
        }
        if ($bytes / 1024 >= 1) {
            return $bytes / 1024 . 'KB';
        }
        return $bytes . 'B'; 
    }
}

?>

Теперь проблема, с которой я столкнулся, - это одна из тех, что кажутся круговой логикой. Моей первоначальной мыслью было иметь систему, которая работала бы следующим образом:

  1. Установить одноэлементное соединение с SFTP
  2. Делайте запросы к этому синглтону
  3. Представление результатов в пользовательском интерфейсе

Я планирую показать таблицу с интерактивными строками, представляющими различные элементы в каталоге на сервере. Когда пользователь щелкает одну из этих строк, я хочу, чтобы система возвращала новый список элементов для того каталога и соответствующим образом обновляла пользовательский интерфейс. Чтобы добиться этого, я попытался добавить скрытое поле в каждую строку таблицы, чтобы хранить имя списка для этой ячейки. Когда ячейка щелкнута, мне нужно будет извлечь значение этого скрытого поля и сбросить таблицу с новым каталогом. Тогда возникает вопрос о замене таблицы на экране, а не просто повторении новой.

Поэтому мой вопрос:

Каков наилучший способ сохранить каталог, относящийся к каждой ячейке, таким образом, чтобы при щелчке по этой ячейке синглтон SFTP сбрасывал таблицу one на основе нового каталога?

Обратите внимание, что приведенный выше код, вероятно, содержит логические ошибки, которые могут показаться новичку малопонятными, так как было предпринято много разных попыток. Кроме того, чтобы было ясно, я ищу методологическую точку в правильном направлении, а не кого-то, кто напишет за меня код.

Заранее спасибо.


person The Kraken    schedule 24.10.2013    source источник
comment
Я не думаю, что существует единственный способ сделать это в зависимости от того, насколько сложным вы хотите это сделать. Я бы вызвал класс SFTP через AJAX, чтобы загрузить дерево папок и вернуть его в данные JSON. Остальное будет обрабатываться Javascript (jquery или другая библиотека помогут изменить DOM). Таким образом, каждый раз, когда вы щелкаете папку, выполняется запрос AJAX для получения дерева (возможно, с ограничением в 2-3 подкаталога, если папок/файлов много). Также я бы кэшировал в Javascript это дерево и вызывал AJAX только в том случае, если выполняется какой-либо тайм-аут или другая логика.   -  person JScoobyCed    schedule 24.10.2013
comment
Я изучил вариант AJAX. Проблема, с которой я столкнулся, заключалась в том, что команды SFTP существуют как часть класса, который я не мог понять, как выполнить через AJAX (т.е. получить доступ к синглтону и вызвать для него метод). В противном случае, если я просто напишу обычный PHP-скрипт для вызова метода, это означает разрушение концепции синглтона и повторное подключение к серверу с каждой командой. Знаете ли вы, как вызвать метод класса singleton через AJAX?   -  person The Kraken    schedule 24.10.2013
comment
Ок, я как-то ждал твоего ответа. Я никогда не сталкивался с этой ситуацией, поэтому я не могу помочь. Хотя я жду ответа от кого-нибудь :)   -  person JScoobyCed    schedule 24.10.2013
comment
Ха-ха, хорошо. Тем не менее, я ценю ваш вклад.   -  person The Kraken    schedule 24.10.2013


Ответы (1)


In PHP, the singleton paradigm is considered "bad practice". См. ссылку: Рекомендации по одноэлементным классам PHP

Хотя ваш класс SFTP, по-видимому, не реализует одноэлементную логику в приведенном выше примере.

Ваша следующая проблема заключается в том, что когда вы вызываете веб-страницу как есть, вся веб-страница будет перезагружена. Без какого-либо кэширования на стороне сервера в вашей текущей архитектуре невозможно обновить только часть страницы. Как упоминалось в комментариях, AJAX будет вашим предпочтительным инструментом для этого.

Одиночек просто не существует МЕЖДУ обращениями браузера к веб-серверу в PHP. Каждый раз, когда ваш браузер подключается к веб-серверу, он обязательно создает новое SFTP-соединение. Каждый раз, когда ваш веб-сервер завершает запрос, он уничтожает используемое им соединение SFTP.

Методологический указатель, который вы ищете:

  1. Имейте страницу, которая загружает начальный пользовательский интерфейс (давайте назовем его представлением) в браузере. Вам, вероятно, нужно предоставить как минимум некоторую базовую конфигурацию, вы можете просто загрузить ее на 100% при первом вызове.
  2. Разработайте другие страницы, которые просто обслуживают данные, предпочтительно в формате json.
  3. Имейте javascript (ajax) в представлении, которое делает дискретные вызовы на сервер для данных (ваших таблиц).
  4. Когда вы щелкаете по странице, ajax делает дискретные вызовы на правильную страницу для обслуживания правильных данных, а затем обновляет/обновляет правильный элемент в пользовательском интерфейсе.

Лично я являюсь поклонником Memcached для кэширования данных сеанса между запросами к серверу. При этом существует большое количество служб кэширования, которые можно использовать для хранения списков каталогов на веб-сервере до тех пор, пока их не потребуется обновить с SFTP-сервера.

При поиске оптимального решения кэширования для вашей задачи стоит убедиться, что вы понимаете разницу между кэшированием кодов операций (существует множество кэшей кодов операций, доступных для использования. У вас есть APC, XCache, eAccelerator и Zend Platform.) и кэширование данных (сеанс, переменная, пространство пользователя — мы рекомендуем memcached).

Однако, если ваши данные достаточно велики (> 1 МБ), вы обычно не хотите кэшировать их в чем-либо вроде memcached, вам лучше кэшировать их в локальную файловую систему, вот пример того, как я недавно сделал это для очень большой массив.

/**
 * Will serialize, then write the array to disk, returning the filePath
 * 
 * @param array $array
 * @param string $filePath
 * @return string
 */
function putCacheData(array $array, $filePath = NULL){
    if (empty($filePath)){
        $filePath = tempnam(NULL, 'IMPORT');
    }
    $serializedData = serialize($array);
    file_put_contents($filePath, $serializedData);
    return $filePath;
}

/**
 * Reads the file, unserializes the data, and returns the array.
 * 
 * @param string $filePath
 * @return Array|FALSE
 */
function getCacheData($filePath){
    $array = array();
    if (empty($filePath)){
        logmessage("The filepath: [$filepath] is empty!");
        return $array;
    }

    if (! is_file($filePath)){
        putImportData($array, $filePath);
        return $array;
    }

    return unserialize( file_get_contents( $filePath ) );   
}

Затем мы просто сохраняем $filePath в данных сеанса пользователя (которые для нас отправляются в memcached), и когда я загружаю каждый запрос, я могу проверить сеанс на наличие пути, загрузить кэшированные данные, определить, истек ли срок их действия или нет, и, возможно, обновить его. Просто убедитесь, что данные записаны в файл до завершения активного запроса.

person bubba    schedule 24.10.2013
comment
По сути, даже при использовании Ajax мне придется каждый раз создавать новое соединение с сервером? Кроме того, какой рекомендуемый способ кэширования списков каталогов серверов по мере их получения (как я понимаю, это будет полезной идеей)? - person The Kraken; 24.10.2013
comment
Это правильно, по своей природе каждый запрос веб-браузера будет создавать новое соединение с веб-сервера на sftp-сервер. - person bubba; 24.10.2013