Merge pull request #364 from terrafrost/sftp-sort2

SFTP: add the ability for nlist() and rawlist() to be sorted

* terrafrost/sftp-sort2:
  SFTP: update one last comment
  SFTP: update unit test comments
  SFTP: add new line to end of unit test
  SFTP: assertEquals -> assertSame
  SFTP: reset sort options every time and update unit test
  SFTP: rm whitespace
  SFTP: define $sortOptions
  SFTP: add the ability for nlist() and rawlist() to be sorted
This commit is contained in:
Andreas Fischer 2014-06-09 20:19:33 +02:00
commit 6e796d091a
2 changed files with 214 additions and 14 deletions

View File

@ -262,6 +262,16 @@ class Net_SFTP extends Net_SSH2
*/
var $use_stat_cache = true;
/**
* Sort Options
*
* @see Net_SFTP::_comparator()
* @see Net_SFTP::setListOrder()
* @var Array
* @access private
*/
var $sortOptions = array();
/**
* Default Constructor.
*
@ -809,16 +819,12 @@ class Net_SFTP extends Net_SSH2
/**
* Reads a list, be it detailed or not, of files in the given directory
*
* $realpath exists because, in the case of the recursive deletes and recursive chmod's $realpath has already
* been calculated.
*
* @param String $dir
* @param optional Boolean $raw
* @param optional Boolean $realpath
* @return Mixed
* @access private
*/
function _list($dir, $raw = true, $realpath = true)
function _list($dir, $raw = true)
{
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
return false;
@ -878,11 +884,7 @@ class Net_SFTP extends Net_SSH2
$attributes['type'] = $fileType;
}
}
if (!$raw) {
$contents[] = $shortname;
} else {
$contents[$shortname] = $attributes;
}
$contents[$shortname] = $attributes + array('filename' => $shortname);
if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) {
$this->_update_stat_cache($dir . '/' . $shortname, array());
@ -915,7 +917,111 @@ class Net_SFTP extends Net_SSH2
return false;
}
return $contents;
if (count($this->sortOptions)) {
uasort($contents, array(&$this, '_comparator'));
}
return $raw ? $contents : array_keys($contents);
}
/**
* Compares two rawlist entries using parameters set by setListOrder()
*
* Intended for use with uasort()
*
* @param Array $a
* @param Array $b
* @return Integer
* @access private
*/
function _comparator($a, $b)
{
switch (true) {
case $a['filename'] === '.' || $b['filename'] === '.':
if ($a['filename'] === $b['filename']) {
return 0;
}
return $a['filename'] === '.' ? -1 : 1;
case $a['filename'] === '..' || $b['filename'] === '..':
if ($a['filename'] === $b['filename']) {
return 0;
}
return $a['filename'] === '..' ? -1 : 1;
case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY:
if (!isset($b['type'])) {
return 1;
}
if ($b['type'] !== $a['type']) {
return -1;
}
break;
case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY:
return 1;
}
foreach ($this->sortOptions as $sort => $order) {
if (!isset($a[$sort]) || !isset($b[$sort])) {
if (isset($a[$sort])) {
return -1;
}
if (isset($b[$sort])) {
return 1;
}
return 0;
}
switch ($sort) {
case 'filename':
$result = strcasecmp($a['filename'], $b['filename']);
if ($result) {
return $order === SORT_DESC ? -$result : $result;
}
break;
case 'permissions':
case 'mode':
$a[$sort]&= 07777;
$b[$sort]&= 07777;
default:
if ($a[$sort] === $b[$sort]) {
break;
}
return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort];
}
}
}
/**
* Defines how nlist() and rawlist() will be sorted - if at all.
*
* If sorting is enabled directories and files will be sorted independently with
* directories appearing before files in the resultant array that is returned.
*
* Any parameter returned by stat is a valid sort parameter for this function.
* Filename comparisons are case insensitive.
*
* Examples:
*
* $sftp->setListOrder('filename', SORT_ASC);
* $sftp->setListOrder('size', SORT_DESC, 'filename', SORT_ASC);
* $sftp->setListOrder(true);
* Separates directories from files but doesn't do any sorting beyond that
* $sftp->setListOrder();
* Don't do any sort of sorting
*
* @access public
*/
function setListOrder()
{
$this->sortOptions = array();
$args = func_get_args();
if (empty($args)) {
return;
}
$len = count($args) & 0x7FFFFFFE;
for ($i = 0; $i < $len; $i+=2) {
$this->sortOptions[$args[$i]] = $args[$i + 1];
}
if (!count($this->sortOptions)) {
$this->sortOptions = array('bogus' => true);
}
}
/**

View File

@ -166,6 +166,48 @@ class Functional_Net_SFTPUserStoryTest extends PhpseclibFunctionalTestCase
/**
* @depends testPutSizeGetFile
*/
public function testTouch($sftp)
{
$this->assertTrue(
$sftp->touch('file2.txt'),
'Failed asserting that touch() successfully ran.'
);
$this->assertTrue(
$sftp->file_exists('file2.txt'),
'Failed asserting that touch()\'d file exists'
);
return $sftp;
}
/**
* @depends testTouch
*/
public function testTruncate($sftp)
{
$this->assertTrue(
$sftp->touch('file3.txt'),
'Failed asserting that touch() successfully ran.'
);
$this->assertTrue(
$sftp->truncate('file3.txt', 1024 * 1024),
'Failed asserting that touch() successfully ran.'
);
$this->assertSame(
1024 * 1024,
$sftp->size('file3.txt'),
'Failed asserting that truncate()\'d file has the expected length'
);
return $sftp;
}
/**
* @depends testTruncate
*/
public function testChDirOnFile($sftp)
{
$this->assertFalse(
@ -205,17 +247,17 @@ class Functional_Net_SFTPUserStoryTest extends PhpseclibFunctionalTestCase
public function testFileExistsIsFileIsDirFileNonexistent($sftp)
{
$this->assertFalse(
$sftp->file_exists('file2.txt'),
$sftp->file_exists('file4.txt'),
'Failed asserting that a nonexistent file does not exist.'
);
$this->assertFalse(
$sftp->is_file('file2.txt'),
$sftp->is_file('file4.txt'),
'Failed asserting that is_file() on nonexistent file returns false.'
);
$this->assertFalse(
$sftp->is_dir('file2.txt'),
$sftp->is_dir('file4.txt'),
'Failed asserting that is_dir() on nonexistent file returns false.'
);
@ -225,6 +267,58 @@ class Functional_Net_SFTPUserStoryTest extends PhpseclibFunctionalTestCase
/**
* @depends testFileExistsIsFileIsDirFileNonexistent
*/
public function testSortOrder($sftp)
{
$this->assertTrue(
$sftp->mkdir('temp'),
"Failed asserting that a new scratch directory temp could " .
'be created.'
);
$sftp->setListOrder('filename', SORT_DESC);
$list = $sftp->nlist();
$expected = array('.', '..', 'temp', 'file3.txt', 'file2.txt', 'file1.txt');
$this->assertSame(
$list,
$expected,
'Failed asserting that list sorted correctly.'
);
$sftp->setListOrder('filename', SORT_ASC);
$list = $sftp->nlist();
$expected = array('.', '..', 'temp', 'file1.txt', 'file2.txt', 'file3.txt');
$this->assertSame(
$list,
$expected,
'Failed asserting that list sorted correctly.'
);
$sftp->setListOrder('size', SORT_DESC);
$files = $sftp->nlist();
$last_size = 0x7FFFFFFF;
foreach ($files as $file) {
if ($sftp->is_file($file)) {
$cur_size = $sftp->size($file);
$this->assertLessThanOrEqual(
$last_size, $cur_size,
'Failed asserting that nlist() is in descending order'
);
$last_size = $cur_size;
}
}
return $sftp;
}
/**
* @depends testSortOrder
*/
public function testChDirUpHome($sftp)
{
$this->assertTrue(