Subversion Repositories oidplus

Rev

Rev 846 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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