Also add Share::getWrite to non native shares

This commit is contained in:
Robin Appelman 2014-07-24 01:29:54 +02:00
commit 89d73346f2
7 changed files with 112 additions and 35 deletions

View file

@ -10,7 +10,8 @@
} }
], ],
"require" : { "require" : {
"php": ">=5.3" "php": ">=5.3",
"icewind/streams": "0.1.x"
}, },
"require-dev": { "require-dev": {
"satooshi/php-coveralls" : "dev-master" "satooshi/php-coveralls" : "dev-master"

View file

@ -75,14 +75,10 @@ class Connection extends RawConnection {
} }
} }
public function close() { public function close($terminate = true) {
if (is_resource($this->getInputStream())) { if (is_resource($this->getInputStream())) {
$this->write('close' . PHP_EOL); $this->write('close' . PHP_EOL);
} }
} parent::close($terminate);
public function __destruct() {
$this->close();
parent::__destruct();
} }
} }

View file

@ -93,4 +93,12 @@ interface IShare {
* @return resource a read only stream with the contents of the remote file * @return resource a read only stream with the contents of the remote file
*/ */
public function read($source); public function read($source);
/**
* Open a writable stream to a remote file
*
* @param string $target
* @return resource a write only stream to upload a remote file
*/
public function write($target);
} }

View file

@ -62,8 +62,8 @@ class RawConnection {
* @param string $input * @param string $input
*/ */
public function write($input) { public function write($input) {
fwrite($this->pipes[0], $input); fwrite($this->getInputStream(), $input);
fflush($this->pipes[0]); fflush($this->getInputStream());
} }
/** /**
@ -72,7 +72,7 @@ class RawConnection {
* @return string * @return string
*/ */
public function read() { public function read() {
return trim(fgets($this->pipes[1])); return trim(fgets($this->getOutputStream()));
} }
/** /**
@ -88,12 +88,28 @@ class RawConnection {
return $output; return $output;
} }
public function getInputStream() {
return $this->pipes[0];
}
public function getOutputStream() { public function getOutputStream() {
return $this->pipes[1]; return $this->pipes[1];
} }
public function getInputStream() { public function getErrorStream() {
return $this->pipes[0]; return $this->pipes[2];
}
public function getAuthStream() {
return $this->pipes[3];
}
public function getFileInputStream() {
return $this->pipes[4];
}
public function getFileOutputStream() {
return $this->pipes[5];
} }
public function writeAuthentication($user, $password) { public function writeAuthentication($user, $password) {
@ -101,16 +117,25 @@ class RawConnection {
? "username=$user" ? "username=$user"
: "username=$user\npassword=$password"; : "username=$user\npassword=$password";
if (fwrite($this->pipes[3], $auth) === false) { if (fwrite($this->getAuthStream(), $auth) === false) {
fclose($this->pipes[3]); fclose($this->getAuthStream());
return false; return false;
} }
fclose($this->pipes[3]); fclose($this->getAuthStream());
return true; return true;
} }
public function __destruct() { public function close($terminate = true) {
if (!is_resource($this->process)) {
return;
}
if ($terminate) {
proc_terminate($this->process); proc_terminate($this->process);
}
proc_close($this->process); proc_close($this->process);
} }
public function __destruct() {
$this->close();
}
} }

View file

@ -8,6 +8,8 @@
namespace Icewind\SMB; namespace Icewind\SMB;
use Icewind\Streams\CallbackWrapper;
class Share implements IShare { class Share implements IShare {
/** /**
* @var Server $server * @var Server $server
@ -22,7 +24,7 @@ class Share implements IShare {
/** /**
* @var Connection $connection * @var Connection $connection
*/ */
private $connection; public $connection;
private $serverTimezone; private $serverTimezone;
@ -203,25 +205,68 @@ class Share implements IShare {
} }
/** /**
* Open a readable stream top a remote file * Open a readable stream to a remote file
* *
* @param string $source * @param string $source
* @return resource a read only stream with the contents of the remote file * @return resource a read only stream with the contents of the remote file
*/ */
public function read($source) { public function read($source) {
$source = $this->escapePath($source); $source = $this->escapePath($source);
// since we do binary transfer over STDOUT we create a new connection // since returned stream is closed by the caller we need to create a new instance
// since we can't re-use the same file descriptor over multiple calls
$command = Server::CLIENT . ' --authentication-file=/proc/self/fd/3' . $command = Server::CLIENT . ' --authentication-file=/proc/self/fd/3' .
' //' . $this->server->getHost() . '/' . $this->name ' //' . $this->server->getHost() . '/' . $this->name
. ' -c \'get ' . $source . ' -\''; . ' -c \'get ' . $source . ' /proc/self/fd/5\'';
$connection = new Connection($command); $connection = new Connection($command);
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword()); $connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
$fh = $connection->getOutputStream(); $fh = $connection->getFileOutputStream();
//save the connection as context of the stream to prevent it going out of scope and cleaning up the resource //save the connection as context of the stream to prevent it going out of scope and cleaning up the resource
stream_context_set_option($fh, 'file', 'connection', $connection); stream_context_set_option($fh, 'file', 'connection', $connection);
return $fh; return $fh;
} }
/**
* Open a writable stream to a remote file
*
* @param string $target
* @return resource a write only stream to upload a remote file
*/
public function write($target) {
$target = $this->escapePath($target);
// since returned stream is closed by the caller we need to create a new instance
// since we can't re-use the same file descriptor over multiple calls
$command = Server::CLIENT . ' --authentication-file=/proc/self/fd/3' .
' //' . $this->server->getHost() . '/' . $this->name
. ' -c \'put /proc/self/fd/4 ' . $target . '\'';
$connection = new RawConnection($command);
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
$fh = $connection->getFileInputStream();
// use a close callback to ensure the upload is finished before continuing
// this also serves as a way to keep the connection in scope
return CallbackWrapper::wrap($fh, null, null, function () use ($connection) {
$connection->close(false); // dont terminate, give the upload some time
});
}
/**
* @param resource $source
* @param callable $callback
* @return resource
*/
protected function addCloseCallback($source, $callback) {
$context = stream_context_create(array(
'callback' => array(
'source' => $source,
'close' => $callback
)
));
stream_wrapper_register('callback', '\Icewind\Streams\CallbackWrapper');
$stream = fopen('callback://', 'r+', false, $context);
stream_wrapper_unregister('callback');
return $stream;
}
/** /**
* @param string $command * @param string $command
* @return array * @return array

View file

@ -68,17 +68,6 @@ class NativeStream extends \PHPUnit_Framework_TestCase {
$this->assertEquals(120, ftell($fh)); $this->assertEquals(120, ftell($fh));
} }
public function testWrite() {
$fh = $this->share->write($this->root . '/foobar');
fwrite($fh, 'qwerty');
fclose($fh);
$tmpFile1 = tempnam('/tmp', 'smb_test_');
$this->share->get($this->root . '/foobar', $tmpFile1);
$this->assertEquals('qwerty', file_get_contents($tmpFile1));
unlink($tmpFile1);
}
public function testStat() { public function testStat() {
$sourceFile = $this->getTextFile(); $sourceFile = $this->getTextFile();
$this->share->put($sourceFile, $this->root . '/foobar'); $this->share->put($sourceFile, $this->root . '/foobar');

View file

@ -298,4 +298,17 @@ class Share extends \PHPUnit_Framework_TestCase {
$this->assertEquals(file_get_contents($sourceFile), $content); $this->assertEquals(file_get_contents($sourceFile), $content);
} }
public function testWriteStream() {
$fh = $this->share->write($this->root . '/foobar', 'qwerty');
fwrite($fh, 'qwerty');
fclose($fh);
// sleep(5);
$tmpFile1 = tempnam('/tmp', 'smb_test_');
$this->share->get($this->root . '/foobar', $tmpFile1);
$this->assertEquals('qwerty', file_get_contents($tmpFile1));
unlink($tmpFile1);
}
} }