Stream.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. <?php
  2. /**
  3. * SFTP Stream Wrapper
  4. *
  5. * Creates an sftp:// protocol handler that can be used with, for example, fopen(), dir(), etc.
  6. *
  7. * PHP version 5
  8. *
  9. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. * @category Net
  28. * @package Net_SFTP_Stream
  29. * @author Jim Wigginton <terrafrost@php.net>
  30. * @copyright 2013 Jim Wigginton
  31. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  32. * @link http://phpseclib.sourceforge.net
  33. */
  34. /**
  35. * SFTP Stream Wrapper
  36. *
  37. * @package Net_SFTP_Stream
  38. * @author Jim Wigginton <terrafrost@php.net>
  39. * @access public
  40. */
  41. class Net_SFTP_Stream
  42. {
  43. /**
  44. * SFTP instances
  45. *
  46. * Rather than re-create the connection we re-use instances if possible
  47. *
  48. * @var array
  49. */
  50. static $instances;
  51. /**
  52. * SFTP instance
  53. *
  54. * @var object
  55. * @access private
  56. */
  57. var $sftp;
  58. /**
  59. * Path
  60. *
  61. * @var string
  62. * @access private
  63. */
  64. var $path;
  65. /**
  66. * Mode
  67. *
  68. * @var string
  69. * @access private
  70. */
  71. var $mode;
  72. /**
  73. * Position
  74. *
  75. * @var int
  76. * @access private
  77. */
  78. var $pos;
  79. /**
  80. * Size
  81. *
  82. * @var int
  83. * @access private
  84. */
  85. var $size;
  86. /**
  87. * Directory entries
  88. *
  89. * @var array
  90. * @access private
  91. */
  92. var $entries;
  93. /**
  94. * EOF flag
  95. *
  96. * @var bool
  97. * @access private
  98. */
  99. var $eof;
  100. /**
  101. * Context resource
  102. *
  103. * Technically this needs to be publically accessible so PHP can set it directly
  104. *
  105. * @var resource
  106. * @access public
  107. */
  108. var $context;
  109. /**
  110. * Notification callback function
  111. *
  112. * @var callable
  113. * @access public
  114. */
  115. var $notification;
  116. /**
  117. * Registers this class as a URL wrapper.
  118. *
  119. * @param string $protocol The wrapper name to be registered.
  120. * @return bool True on success, false otherwise.
  121. * @access public
  122. */
  123. static function register($protocol = 'sftp')
  124. {
  125. if (in_array($protocol, stream_get_wrappers(), true)) {
  126. return false;
  127. }
  128. $class = function_exists('get_called_class') ? get_called_class() : __CLASS__;
  129. return stream_wrapper_register($protocol, $class);
  130. }
  131. /**
  132. * The Constructor
  133. *
  134. * @access public
  135. */
  136. function __construct()
  137. {
  138. if (defined('NET_SFTP_STREAM_LOGGING')) {
  139. echo "__construct()\r\n";
  140. }
  141. if (!class_exists('Net_SFTP')) {
  142. include_once 'Net/SFTP.php';
  143. }
  144. }
  145. /**
  146. * Path Parser
  147. *
  148. * Extract a path from a URI and actually connect to an SSH server if appropriate
  149. *
  150. * If "notification" is set as a context parameter the message code for successful login is
  151. * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE.
  152. *
  153. * @param string $path
  154. * @return string
  155. * @access private
  156. */
  157. function _parse_path($path)
  158. {
  159. $orig = $path;
  160. extract(parse_url($path) + array('port' => 22));
  161. if (isset($query)) {
  162. $path.= '?' . $query;
  163. } elseif (preg_match('/(\?|\?#)$/', $orig)) {
  164. $path.= '?';
  165. }
  166. if (isset($fragment)) {
  167. $path.= '#' . $fragment;
  168. } elseif ($orig[strlen($orig) - 1] == '#') {
  169. $path.= '#';
  170. }
  171. if (!isset($host)) {
  172. return false;
  173. }
  174. if (isset($this->context)) {
  175. $context = stream_context_get_params($this->context);
  176. if (isset($context['notification'])) {
  177. $this->notification = $context['notification'];
  178. }
  179. }
  180. if ($host[0] == '$') {
  181. $host = substr($host, 1);
  182. global ${$host};
  183. if (!is_object($$host) || get_class($$host) != 'Net_SFTP') {
  184. return false;
  185. }
  186. $this->sftp = $$host;
  187. } else {
  188. if (isset($this->context)) {
  189. $context = stream_context_get_options($this->context);
  190. }
  191. if (isset($context[$scheme]['session'])) {
  192. $sftp = $context[$scheme]['session'];
  193. }
  194. if (isset($context[$scheme]['sftp'])) {
  195. $sftp = $context[$scheme]['sftp'];
  196. }
  197. if (isset($sftp) && is_object($sftp) && get_class($sftp) == 'Net_SFTP') {
  198. $this->sftp = $sftp;
  199. return $path;
  200. }
  201. if (isset($context[$scheme]['username'])) {
  202. $user = $context[$scheme]['username'];
  203. }
  204. if (isset($context[$scheme]['password'])) {
  205. $pass = $context[$scheme]['password'];
  206. }
  207. if (isset($context[$scheme]['privkey']) && is_object($context[$scheme]['privkey']) && get_Class($context[$scheme]['privkey']) == 'Crypt_RSA') {
  208. $pass = $context[$scheme]['privkey'];
  209. }
  210. if (!isset($user) || !isset($pass)) {
  211. return false;
  212. }
  213. // casting $pass to a string is necessary in the event that it's a Crypt_RSA object
  214. if (isset(self::$instances[$host][$port][$user][(string) $pass])) {
  215. $this->sftp = self::$instances[$host][$port][$user][(string) $pass];
  216. } else {
  217. $this->sftp = new Net_SFTP($host, $port);
  218. $this->sftp->disableStatCache();
  219. if (isset($this->notification) && is_callable($this->notification)) {
  220. /* if !is_callable($this->notification) we could do this:
  221. user_error('fopen(): failed to call user notifier', E_USER_WARNING);
  222. the ftp wrapper gives errors like that when the notifier isn't callable.
  223. i've opted not to do that, however, since the ftp wrapper gives the line
  224. on which the fopen occurred as the line number - not the line that the
  225. user_error is on.
  226. */
  227. call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
  228. call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0);
  229. if (!$this->sftp->login($user, $pass)) {
  230. call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0);
  231. return false;
  232. }
  233. call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0);
  234. } else {
  235. if (!$this->sftp->login($user, $pass)) {
  236. return false;
  237. }
  238. }
  239. self::$instances[$host][$port][$user][(string) $pass] = $this->sftp;
  240. }
  241. }
  242. return $path;
  243. }
  244. /**
  245. * Opens file or URL
  246. *
  247. * @param string $path
  248. * @param string $mode
  249. * @param int $options
  250. * @param string $opened_path
  251. * @return bool
  252. * @access public
  253. */
  254. function _stream_open($path, $mode, $options, &$opened_path)
  255. {
  256. $path = $this->_parse_path($path);
  257. if ($path === false) {
  258. return false;
  259. }
  260. $this->path = $path;
  261. $this->size = $this->sftp->size($path);
  262. $this->mode = preg_replace('#[bt]$#', '', $mode);
  263. $this->eof = false;
  264. if ($this->size === false) {
  265. if ($this->mode[0] == 'r') {
  266. return false;
  267. } else {
  268. $this->sftp->touch($path);
  269. $this->size = 0;
  270. }
  271. } else {
  272. switch ($this->mode[0]) {
  273. case 'x':
  274. return false;
  275. case 'w':
  276. $this->sftp->truncate($path, 0);
  277. $this->size = 0;
  278. }
  279. }
  280. $this->pos = $this->mode[0] != 'a' ? 0 : $this->size;
  281. return true;
  282. }
  283. /**
  284. * Read from stream
  285. *
  286. * @param int $count
  287. * @return mixed
  288. * @access public
  289. */
  290. function _stream_read($count)
  291. {
  292. switch ($this->mode) {
  293. case 'w':
  294. case 'a':
  295. case 'x':
  296. case 'c':
  297. return false;
  298. }
  299. // commented out because some files - eg. /dev/urandom - will say their size is 0 when in fact it's kinda infinite
  300. //if ($this->pos >= $this->size) {
  301. // $this->eof = true;
  302. // return false;
  303. //}
  304. $result = $this->sftp->get($this->path, false, $this->pos, $count);
  305. if (isset($this->notification) && is_callable($this->notification)) {
  306. if ($result === false) {
  307. call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
  308. return 0;
  309. }
  310. // seems that PHP calls stream_read in 8k chunks
  311. call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size);
  312. }
  313. if (empty($result)) { // ie. false or empty string
  314. $this->eof = true;
  315. return false;
  316. }
  317. $this->pos+= strlen($result);
  318. return $result;
  319. }
  320. /**
  321. * Write to stream
  322. *
  323. * @param string $data
  324. * @return mixed
  325. * @access public
  326. */
  327. function _stream_write($data)
  328. {
  329. switch ($this->mode) {
  330. case 'r':
  331. return false;
  332. }
  333. $result = $this->sftp->put($this->path, $data, NET_SFTP_STRING, $this->pos);
  334. if (isset($this->notification) && is_callable($this->notification)) {
  335. if (!$result) {
  336. call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0);
  337. return 0;
  338. }
  339. // seems that PHP splits up strings into 8k blocks before calling stream_write
  340. call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data));
  341. }
  342. if ($result === false) {
  343. return false;
  344. }
  345. $this->pos+= strlen($data);
  346. if ($this->pos > $this->size) {
  347. $this->size = $this->pos;
  348. }
  349. $this->eof = false;
  350. return strlen($data);
  351. }
  352. /**
  353. * Retrieve the current position of a stream
  354. *
  355. * @return int
  356. * @access public
  357. */
  358. function _stream_tell()
  359. {
  360. return $this->pos;
  361. }
  362. /**
  363. * Tests for end-of-file on a file pointer
  364. *
  365. * In my testing there are four classes functions that normally effect the pointer:
  366. * fseek, fputs / fwrite, fgets / fread and ftruncate.
  367. *
  368. * Only fgets / fread, however, results in feof() returning true. do fputs($fp, 'aaa') on a blank file and feof()
  369. * will return false. do fread($fp, 1) and feof() will then return true. do fseek($fp, 10) on ablank file and feof()
  370. * will return false. do fread($fp, 1) and feof() will then return true.
  371. *
  372. * @return bool
  373. * @access public
  374. */
  375. function _stream_eof()
  376. {
  377. return $this->eof;
  378. }
  379. /**
  380. * Seeks to specific location in a stream
  381. *
  382. * @param int $offset
  383. * @param int $whence
  384. * @return bool
  385. * @access public
  386. */
  387. function _stream_seek($offset, $whence)
  388. {
  389. switch ($whence) {
  390. case SEEK_SET:
  391. if ($offset >= $this->size || $offset < 0) {
  392. return false;
  393. }
  394. break;
  395. case SEEK_CUR:
  396. $offset+= $this->pos;
  397. break;
  398. case SEEK_END:
  399. $offset+= $this->size;
  400. }
  401. $this->pos = $offset;
  402. $this->eof = false;
  403. return true;
  404. }
  405. /**
  406. * Change stream options
  407. *
  408. * @param string $path
  409. * @param int $option
  410. * @param mixed $var
  411. * @return bool
  412. * @access public
  413. */
  414. function _stream_metadata($path, $option, $var)
  415. {
  416. $path = $this->_parse_path($path);
  417. if ($path === false) {
  418. return false;
  419. }
  420. // stream_metadata was introduced in PHP 5.4.0 but as of 5.4.11 the constants haven't been defined
  421. // see http://www.php.net/streamwrapper.stream-metadata and https://bugs.php.net/64246
  422. // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592
  423. switch ($option) {
  424. case 1: // PHP_STREAM_META_TOUCH
  425. return $this->sftp->touch($path, $var[0], $var[1]);
  426. case 2: // PHP_STREAM_OWNER_NAME
  427. case 3: // PHP_STREAM_GROUP_NAME
  428. return false;
  429. case 4: // PHP_STREAM_META_OWNER
  430. return $this->sftp->chown($path, $var);
  431. case 5: // PHP_STREAM_META_GROUP
  432. return $this->sftp->chgrp($path, $var);
  433. case 6: // PHP_STREAM_META_ACCESS
  434. return $this->sftp->chmod($path, $var) !== false;
  435. }
  436. }
  437. /**
  438. * Retrieve the underlaying resource
  439. *
  440. * @param int $cast_as
  441. * @return resource
  442. * @access public
  443. */
  444. function _stream_cast($cast_as)
  445. {
  446. return $this->sftp->fsock;
  447. }
  448. /**
  449. * Advisory file locking
  450. *
  451. * @param int $operation
  452. * @return bool
  453. * @access public
  454. */
  455. function _stream_lock($operation)
  456. {
  457. return false;
  458. }
  459. /**
  460. * Renames a file or directory
  461. *
  462. * Attempts to rename oldname to newname, moving it between directories if necessary.
  463. * If newname exists, it will be overwritten. This is a departure from what Net_SFTP
  464. * does.
  465. *
  466. * @param string $path_from
  467. * @param string $path_to
  468. * @return bool
  469. * @access public
  470. */
  471. function _rename($path_from, $path_to)
  472. {
  473. $path1 = parse_url($path_from);
  474. $path2 = parse_url($path_to);
  475. unset($path1['path'], $path2['path']);
  476. if ($path1 != $path2) {
  477. return false;
  478. }
  479. $path_from = $this->_parse_path($path_from);
  480. $path_to = parse_url($path_to);
  481. if ($path_from === false) {
  482. return false;
  483. }
  484. $path_to = $path_to['path']; // the $component part of parse_url() was added in PHP 5.1.2
  485. // "It is an error if there already exists a file with the name specified by newpath."
  486. // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.5
  487. if (!$this->sftp->rename($path_from, $path_to)) {
  488. if ($this->sftp->stat($path_to)) {
  489. return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to);
  490. }
  491. return false;
  492. }
  493. return true;
  494. }
  495. /**
  496. * Open directory handle
  497. *
  498. * The only $options is "whether or not to enforce safe_mode (0x04)". Since safe mode was deprecated in 5.3 and
  499. * removed in 5.4 I'm just going to ignore it.
  500. *
  501. * Also, nlist() is the best that this function is realistically going to be able to do. When an SFTP client
  502. * sends a SSH_FXP_READDIR packet you don't generally get info on just one file but on multiple files. Quoting
  503. * the SFTP specs:
  504. *
  505. * The SSH_FXP_NAME response has the following format:
  506. *
  507. * uint32 id
  508. * uint32 count
  509. * repeats count times:
  510. * string filename
  511. * string longname
  512. * ATTRS attrs
  513. *
  514. * @param string $path
  515. * @param int $options
  516. * @return bool
  517. * @access public
  518. */
  519. function _dir_opendir($path, $options)
  520. {
  521. $path = $this->_parse_path($path);
  522. if ($path === false) {
  523. return false;
  524. }
  525. $this->pos = 0;
  526. $this->entries = $this->sftp->nlist($path);
  527. return $this->entries !== false;
  528. }
  529. /**
  530. * Read entry from directory handle
  531. *
  532. * @return mixed
  533. * @access public
  534. */
  535. function _dir_readdir()
  536. {
  537. if (isset($this->entries[$this->pos])) {
  538. return $this->entries[$this->pos++];
  539. }
  540. return false;
  541. }
  542. /**
  543. * Rewind directory handle
  544. *
  545. * @return bool
  546. * @access public
  547. */
  548. function _dir_rewinddir()
  549. {
  550. $this->pos = 0;
  551. return true;
  552. }
  553. /**
  554. * Close directory handle
  555. *
  556. * @return bool
  557. * @access public
  558. */
  559. function _dir_closedir()
  560. {
  561. return true;
  562. }
  563. /**
  564. * Create a directory
  565. *
  566. * Only valid $options is STREAM_MKDIR_RECURSIVE
  567. *
  568. * @param string $path
  569. * @param int $mode
  570. * @param int $options
  571. * @return bool
  572. * @access public
  573. */
  574. function _mkdir($path, $mode, $options)
  575. {
  576. $path = $this->_parse_path($path);
  577. if ($path === false) {
  578. return false;
  579. }
  580. return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE);
  581. }
  582. /**
  583. * Removes a directory
  584. *
  585. * Only valid $options is STREAM_MKDIR_RECURSIVE per <http://php.net/streamwrapper.rmdir>, however,
  586. * <http://php.net/rmdir> does not have a $recursive parameter as mkdir() does so I don't know how
  587. * STREAM_MKDIR_RECURSIVE is supposed to be set. Also, when I try it out with rmdir() I get 8 as
  588. * $options. What does 8 correspond to?
  589. *
  590. * @param string $path
  591. * @param int $mode
  592. * @param int $options
  593. * @return bool
  594. * @access public
  595. */
  596. function _rmdir($path, $options)
  597. {
  598. $path = $this->_parse_path($path);
  599. if ($path === false) {
  600. return false;
  601. }
  602. return $this->sftp->rmdir($path);
  603. }
  604. /**
  605. * Flushes the output
  606. *
  607. * See <http://php.net/fflush>. Always returns true because Net_SFTP doesn't cache stuff before writing
  608. *
  609. * @return bool
  610. * @access public
  611. */
  612. function _stream_flush()
  613. {
  614. return true;
  615. }
  616. /**
  617. * Retrieve information about a file resource
  618. *
  619. * @return mixed
  620. * @access public
  621. */
  622. function _stream_stat()
  623. {
  624. $results = $this->sftp->stat($this->path);
  625. if ($results === false) {
  626. return false;
  627. }
  628. return $results;
  629. }
  630. /**
  631. * Delete a file
  632. *
  633. * @param string $path
  634. * @return bool
  635. * @access public
  636. */
  637. function _unlink($path)
  638. {
  639. $path = $this->_parse_path($path);
  640. if ($path === false) {
  641. return false;
  642. }
  643. return $this->sftp->delete($path, false);
  644. }
  645. /**
  646. * Retrieve information about a file
  647. *
  648. * Ignores the STREAM_URL_STAT_QUIET flag because the entirety of Net_SFTP_Stream is quiet by default
  649. * might be worthwhile to reconstruct bits 12-16 (ie. the file type) if mode doesn't have them but we'll
  650. * cross that bridge when and if it's reached
  651. *
  652. * @param string $path
  653. * @param int $flags
  654. * @return mixed
  655. * @access public
  656. */
  657. function _url_stat($path, $flags)
  658. {
  659. $path = $this->_parse_path($path);
  660. if ($path === false) {
  661. return false;
  662. }
  663. $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path);
  664. if ($results === false) {
  665. return false;
  666. }
  667. return $results;
  668. }
  669. /**
  670. * Truncate stream
  671. *
  672. * @param int $new_size
  673. * @return bool
  674. * @access public
  675. */
  676. function _stream_truncate($new_size)
  677. {
  678. if (!$this->sftp->truncate($this->path, $new_size)) {
  679. return false;
  680. }
  681. $this->eof = false;
  682. $this->size = $new_size;
  683. return true;
  684. }
  685. /**
  686. * Change stream options
  687. *
  688. * STREAM_OPTION_WRITE_BUFFER isn't supported for the same reason stream_flush isn't.
  689. * The other two aren't supported because of limitations in Net_SFTP.
  690. *
  691. * @param int $option
  692. * @param int $arg1
  693. * @param int $arg2
  694. * @return bool
  695. * @access public
  696. */
  697. function _stream_set_option($option, $arg1, $arg2)
  698. {
  699. return false;
  700. }
  701. /**
  702. * Close an resource
  703. *
  704. * @access public
  705. */
  706. function _stream_close()
  707. {
  708. }
  709. /**
  710. * __call Magic Method
  711. *
  712. * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you.
  713. * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function
  714. * lets you figure that out.
  715. *
  716. * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not
  717. * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
  718. *
  719. * @param string
  720. * @param array
  721. * @return mixed
  722. * @access public
  723. */
  724. function __call($name, $arguments)
  725. {
  726. if (defined('NET_SFTP_STREAM_LOGGING')) {
  727. echo $name . '(';
  728. $last = count($arguments) - 1;
  729. foreach ($arguments as $i => $argument) {
  730. var_export($argument);
  731. if ($i != $last) {
  732. echo ',';
  733. }
  734. }
  735. echo ")\r\n";
  736. }
  737. $name = '_' . $name;
  738. if (!method_exists($this, $name)) {
  739. return false;
  740. }
  741. return call_user_func_array(array($this, $name), $arguments);
  742. }
  743. }
  744. Net_SFTP_Stream::register();