Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FTP API enhancements #140

Closed
thekid opened this issue Oct 17, 2011 · 3 comments
Closed

FTP API enhancements #140

thekid opened this issue Oct 17, 2011 · 3 comments

Comments

@thekid
Copy link
Member

thekid commented Oct 17, 2011

Scope of Change

The peer.ftp API will be enhanced in the following ways:

  • There will be a new method peer.ftp.FtpConnection::rootDir()
  • There will be a new method peer.ftp.FtpConnection::sendCommand()
  • The peer.ftp.FtpEntry class will be abstract and there will be a
    new class peer.ftp.FtpFile extending it.
  • There will be several new methods in peer.ftp.FtpDir
  • There will be a new class peer.ftp.FtpEntryList
  • There will be several new methods in peer.ftp.FtpEntry
  • There will methods in peer.ftp.FtpFile to up- and download which
    will work using the io.streams API

Rationale

Prevent having to use raw FTP functions, make common use-cases easier.

Functionality

FtpEntry base class

At the moment, an FtpEntry represents a file, and an FtpDir (which extends
FtpEntry) represents a directory. This poses several limitations on what
can be in the FtpEntry class and makes it harder to distinguish them from
each other.

New layout:

<?php
  abstract class FtpEntry extends Object {
    // Common functionality like name, size, permissions, owner, ...
  }

  class FtpFile extends FtpEntry { 
    // File specific functionality - up & downloads
  }

  class FtpDir extends FtpEntry {
    // Directory specific - getDir(), makeDir(), getFile(), hasFile(), ...
  }
?>

FtpEntryList class

<?php
  class FtpEntryList extends Object implements IteratorAggregate {

    /**
     * Returns an iterator for use in foreach()
     *
     * @see     php://language.oop5.iterations
     * @return  php.Iterator
     */
    public function getIterator() {
      // ...
    }

    /**
     * Returns the number of elements in this list.
     *
     * @return  int
     */
    public function size() {
      // ...
    }

    /**
     * Tests if this list has no elements.
     *
     * @return  bool
     */
    public function isEmpty() {
      // ...
    }

    /**
     * Returns all elements in this list as an array.
     *
     * @return  peer.ftp.FtpEntry[] an array of all entries
     * @throws  peer.SocketException in case of an I/O error
     */
    public function asArray() {
      // ...
    }
  }
?>

New methods

peer.ftp.FtpConnection:

<?php
  class FtpConnection extends Object {
    // ...

    /**
     * Returns an FtpDir instance representing this connection's
     * root directory
     *
     * @return  peer.ftp.FtpDir the instance
     * @throws  peer.SocketException in case of an I/O error
     */
    public function rootDir() {
      // ...
    }

    /**
     * Sends a raw command to the FTP server and returns the server's
     * response (unparsed) as an array of strings.
     *
     * Accepts a command which will be handled as format-string for
     * further arbitrary arguments, e.g.:
     *
     *   $c->sendCommand('CLNT %s', $clientName);
     *
     * @param   string command
     * @param   string* args
     * @return  string[] result
     * @throws  peer.SocketException in case of an I/O error
     */
    public function sendCommand($command) {
      // ...
    }

    // ...
  }
?>

peer.ftp.FtpEntry:

<?php
  abstract class FtpEntry {
    // ...

    /**
     * Checks whether this entry exists.
     *
     * @return  bool TRUE if the file exists, FALSE otherwise
     * @throws  peer.SocketException in case of an I/O error
     */
    public function exists() {
      // ...
    }

    /**
     * Rename this entry
     *
     * @param   string to the new name
     * @throws  io.IOException if an entry by the new name already exists
     * @throws  peer.SocketException in case of an I/O error
     */
    public function rename($to) {
      // ...
    }

    /**
     * Change this entry's permissions
     *
     * @param   int to the new permissions
     * @throws  io.IOException if an entry by the new name already exists
     * @throws  peer.SocketException in case of an I/O error
     */
    public function changePermissions($to) {
      // ...
    }

    /**
     * Delete this entry
     *
     * @throws  peer.SocketException in case of an I/O error
     */
    public abstract function delete();

    // ...
  }
?>

peer.ftp.FtpDir:

<?php
  class FtpDir extends FtpEntry {
    // ...

    /**
     * Returns a list of entries
     *
     * @return  peer.ftp.FtpEntryList
     * @throws  peer.SocketException in case of an I/O error
     */
    public function entries() {
      // ...
    }

    /**
     * Checks whether a file by the given name exists in this
     * directory.
     *
     * @param   string name
     * @return  bool TRUE if the file exists, FALSE otherwise
     * @throws  peer.SocketException in case of an I/O error
     */
    public function hasFile($name) {
      // ...
    }

    /**
     * Returns an FtpFile instance representing a file in this
     * directory.
     *
     * @param   string name
     * @return  peer.ftp.FtpFile the instance
     * @throws  io.FileNotFoundException in case the file was not found
     * @throws  peer.SocketException in case of an I/O error
     */
    public function getFile($name) {
      // ...
    }

    /**
     * Creates a file in this directory and returns an FtpFile instance
     * representing it.
     *
     * @param   string name
     * @return  peer.ftp.FtpFile the instance
     * @throws  io.IOException in case the file already exists
     * @throws  peer.SocketException in case of an I/O error
     */
    public function newFile($name) {
      // ...
    }

    /**
     * Returns an FtpFile instance representing a file in this
     * directory.
     *
     * Note: Same as getFile() but does not throw exceptions if the file
     * does not exist but will return an FtpFile in any case.
     *
     * @param   string name
     * @return  peer.ftp.FtpFile the instance
     * @throws  peer.SocketException in case of an I/O error
     */
    public function file($name) {
      // ...
    }

    /**
     * Checks whether a subdirectory by the given name exists in this
     * directory.
     *
     * @param   string name
     * @return  bool TRUE if the file exists, FALSE otherwise
     * @throws  peer.SocketException in case of an I/O error
     */
    public function hasDir($name) {
      // ...
    }

    /**
     * Returns an FtpDir instance representing a subdirectory in this
     * directory.
     *
     * @param   string name
     * @return  peer.ftp.FtpDir the instance
     * @throws  io.FileNotFoundException in case the file was not found
     * @throws  peer.SocketException in case of an I/O error
     */
    public function getDir($name) {
      // ...
    }

    /**
     * Creates a subdirectory in this directory and returns an FtpDir 
     * instance representing it.
     *
     * @param   string name
     * @return  peer.ftp.FtpDir the created instance
     * @throws  lang.IllegalStateException in case the directory already exists
     * @throws  io.IOException in case the directory could not be created
     */
    public function newDir($name) {
      // ...
    }

    /**
     * Returns an FtpDir instance representing a subdirectory in this
     * directory.
     *
     * Note: Same as getDir() but does not throw exceptions if the 
     * directory does not exist but will create it and thus return an 
     * FtpDir in any case.
     *
     * @param   string name
     * @return  peer.ftp.FtpDir the instance
     * @throws  lang.IllegalStateException in case the directory exists and is a file
     * @throws  io.IOException in case the directory could not be created
     */
    public function dir($name) {
      // ...
    }

    // ...
  }
?>

Examples

Checking for entry existance:

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality #1 {{{
  if (-1 == ftp_size($c->handle, '/'.$filename)) {
    // $filename does not exist
  }
  // }}}

  // Current functionality #2 {{{
  if (!($dir= $c->getDir('/'))) {
    throw new FileNotFoundException('Dir does not exist');
  }
  $exists= FALSE;
  while ($e= $dir->getEntry()) {
    if (!($e instanceof FtpDir) && $e->getName() == $filename) {
      $exists= TRUE;
      break;
    }
  }
  // }}}

  // New functionality {{{
  if (!$c->rootDir()->hasFile($filename)) {
    // $filename does not exist
  }
  // }}}
?>

Retrieving the root directory:

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality {{{
  $root= $c->getDir('/');
  // }}}

  // New functionality {{{
  $root= $c->rootDir();
  // }}}
?>

Exceptions for makeDir():

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality {{{
  if (FALSE === $c->makeDir(new FtpDir('/admin'))) {
    // Failed creating directory "admin"
  }
  $dir= $c->getDir('/admin');
  // }}}

  // New functionality {{{
  try {
    $dir= $c->rootDir()->newDir('admin');
  } catch (IllegalStateException $e) {
    // Directory "admin" already exists
  } catch (IOException $e) {
    // Failed creating directory "admin"
  }
  // }}}
?>

Retrieving a single entry:

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality {{{
  if (!($dir= $c->getDir('admin'))) {
    throw new FileNotFoundException('Admin dir does not exist');
  }
  $entry= NULL;
  while ($e= $dir->getEntry()) {
    if (!($e instanceof FtpDir) && $e->getName() == $name) {
      $entry= $e;
      break;
    }
  }
  if (!$entry) {
    throw new FileNotFoundException('File not found');
  }
  // }}}

  // New functionality {{{
  $entry= $c->rootDir()->getDir('admin')->getFile($name);
  // }}}
?>

Downloading entries:

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality {{{
  try {
    $c->get('/'.$filename, $local);
  } catch (IOException $e) {
    // - Remote file did not exist
    // - Local file not writeable
    // - Other socket error
  }
  // }}}

  // New functionality {{{
  try {
    $c->rootDir()->getFile($filename)->downloadTo(new FileOutputStream(new File($local)));
  } catch (FileNotFoundException $e) {
    // Remote file not found
  } catch (SocketException $e) {
    // Socket error
  } catch (IOException $e) {
    // Local file not writeable
  }
  // }}}
?>

Uploading entries:

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality {{{
  try {
    $c->put($local, '/'.$filename);
  } catch (IOException $e) {
    // - Remote file not writeable
    // - Local file did not exist
    // - Other socket error
  }
  // }}}

  // New functionality {{{
  try {
    $c->rootDir()->file($filename)->uploadFrom(new FileInputStream(new File($local)));
  } catch (FileNotFoundException $e) {
    // Local file not found
  } catch (SocketException $e) {
    // Socket error
  } catch (IOException $e) {
    // Remote file not writeable
  }
  // }}}
?>

Retrieving all entries into an array:

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality {{{
  $root= $c->getDir('/');
  $entries= array();
  while ($entry= $root->getEntry()) {
    $entries[]= $entry;
  }
  // }}}

  // New functionality {{{
  $entries= $c->rootDir()->entries()->asArray();
  // }}}
?>

Entry iteration:

<?php
  $c= new FtpConnection('ftp://user:[email protected]');
  $c->connect();

  // Current functionality {{{
  $root= $c->getDir('/');
  $entries= array();
  while ($entry= $root->getEntry()) {
    // ... Handle entry
  }
  // }}}

  // New functionality {{{
  foreach ($c->rootDir()->entries() as $entry) {
    // ... Handle entry
  }
  // }}}
?>

Security considerations

n/a

Speed impact

n/a

Dependencies

Deprecated methods in peer.ftp.FtpConnection:

  public peer.ftp.FtpDir getDir([string $dir= NULL])
  public bool setDir(peer.ftp.FtpDir $f) 
  public bool makeDir(peer.ftp.FtpDir $f)
  public bool put(mixed $arg, [string $remote= NULL], [string $mode= 0]) 
  public bool get(string $remote, mixed $arg, [string $mode= 0])
  public bool delete(string $remote)
  public bool rename(string $src, string $target)
  public array quote(string $command)

Related documents

@thekid thekid closed this as completed Oct 17, 2011
@thekid
Copy link
Member Author

thekid commented Oct 17, 2011

Should the old methods be removed instantly (breaking BC) or should
they be available but deprecated?

friebe, Sun, 14 Oct 2007 19:26:33 +0200

@thekid
Copy link
Member Author

thekid commented Oct 17, 2011

Decided to add methods from old API as deprecated methods. They will
be removed in a future release!

friebe, Mon, 15 Oct 2007 22:02:46 +0200

thekid added a commit to xp-framework/ftp that referenced this issue Sep 26, 2021
@thekid
Copy link
Member Author

thekid commented Sep 26, 2021

The deprecated methods were removed now after almost 14 years - better late than never 😉

commit 1632ef8149cb0cc5919bf8f33bda8c01d4111065
Author: thekid <[email protected]>
Date:   Thu Oct 18 07:36:15 2007 +0000

    - Implement RFC #0140
    # HEADS UP: Deprecated methods in peer.ftp.FtpConnection:
    # * public peer.ftp.FtpDir getDir([string $dir= NULL]) throws peer.SocketException
    # * public bool setDir(peer.ftp.FtpDir $f) throws peer.SocketException
    # * public bool makeDir(peer.ftp.FtpDir $f)
    # * public bool put(mixed $arg, [string $remote= NULL], [string $mode= 0]) throws peer.SocketException
    # * public bool get(string $remote, mixed $arg, [string $mode= 0]) throws peer.SocketException
    # * public bool delete(string $remote)
    # * public bool rename(string $src, string $target)
    # * public array quote(string $command)
    # ==
    # HEADS UP: Deprecated methods in peer.ftp.FtpDir:
    # * public peer.ftp.FtpEntry getEntry() throws peer.SocketException

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant