首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PHP轻量级存储HTTP

PHP轻量级存储HTTP
EN

Code Review用户
提问于 2018-01-24 21:59:37
回答 1查看 78关注 0票数 2

我正在学习PHP和'web‘开发(有离线编程背景)。

我计划设置一个简单的存储服务器API,我将使用javascript应用程序进行添加。

以下代码:

  • 不考虑身份验证
  • 提供文件上传/删除,文件夹列表

我从吊箱架获得了一些关于错误值和action名称的灵感。

代码语言:javascript
运行
复制
* ?action=permanently_delete&path=
* ?action=upload&path=

The provided  values are joined to 'ROOT_FOLDER' constant.

Can't create folders or download files.

Needs PHP 5.5+ for `finally` clauses.
*/

error_reporting(0);

define('ROOT_FOLDER', 'files/' );                // IMPORTANT(nico) '/' at the end

//============================================================================

header('Access-Control-Allow-Origin: *');
header('Cache-Control: no-cache, must-revalidate');

$result = null;

try {

  $action = _filesystem_checked_GET('action');

  switch ($action) {

  case 'list_folder':
    $path = _filesystem_checked_GET('path');
    $result = _filesystem_list_folder($path);
    break;

  case 'permanently_delete':
    $path = _filesystem_checked_GET('path');
    $result = _filesystem_permanently_delete($path);
    break;

  case 'upload':
    $path = _filesystem_checked_GET('path');
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
      throw new _filesystem_HTTPException('Bad Request', 400, 'POST expected');
    }
    $result = _filesystem_upload($path);
    break;

  default:
    throw new _filesystem_HTTPException('Bad Request', 400, 'Unknown action');
  }
}
catch (_filesystem_HTTPException $e) {
  $e->setErrorHeader();
  $result = ['error'=> ['.tag'=> 'other'], 'error_summary'=> $e->content];
}
catch (Exception $e) {
  header("HTTP/1.1 500 Internal error");
  $result = ['error'=> ['.tag'=> 'other'], 'error_summary'=> $e->getMessage()];
}

header('Content-type:application/json;charset=utf-8');
echo json_encode($result);

//============================================================================

function _filesystem_upload($path) {

  $abspath = _filesystem_abspath($path);

  $temppath = tempnam(dirname($abspath), 'temp');
  if (!$temppath) {
    throw new _filesystem_HTTPException("`tempnam` failed", 500);
  }

  try {

    $dst = fopen($temppath, "wb");
    $src = fopen("php://input", "r"); // POST raw data

    try {

      if (!$src || !$dst) {
        throw new _filesystem_HTTPException("Could not create file", 500);
      }
      // copy streams
      while ($data = fread($src, 1024))
      {
        if ($data === FALSE) {
          throw new _filesystem_HTTPException("Could not read source data", 500);     // FIXME(nico) endpoint error ?
        }
        $written = fwrite($dst, $data, 1024);
        if ($written != strlen($written)) {
          throw new _filesystem_HTTPException("Could not write to file", 500);
        }
      }

    }
    finally {
      fclose($src);
      fclose($dst);
    }

    // finalize destination file
    if (!rename($temppath, $abspath)) {
      throw new _filesystem_HTTPException("Could not finalize file", 500);
    }

  }
  finally {

    if (file_exists($temppath)) {
      unlink($temppath);
    }
  }

  $name = basename($abspath);
  $result = _filesystem_metadata($name, $abspath);
  return $result;
}

//----------------------------------------------------------------------------

function _filesystem_permanently_delete($path) {

  $abspath = _filesystem_abspath($path);
  if (unlink($abspath)) {         // FIXME(nico) can trigger a warning, check file_exists first, and improve error reporting

    return null;
  }
  else {

    return ['error'=> ['.tag'=> 'other'], 'error_summary'=> "Could not unlink file"];
  }
}

//----------------------------------------------------------------------------

function _filesystem_list_folder($path) {

  $abspath = _filesystem_abspath($path);
  $names = array_diff(scandir($abspath), array('..', '.'));
  $result = [];
  foreach ($names as $name) {
    $path = _filesystem_path_join($abspath, $name);

    $size = filesize($path);
    $server_modified = date(DATE_ISO8601, filemtime($path));
    $tag = null;
    if (is_dir($path)) { $tag='folder'; }
    elseif (is_file($path)) { $tag='file'; }

    $metadata = _filesystem_metadata($name, $path);
    if ($metadata['.tag'] != null) {
      // NOTE(nico) do not include info on 'undefined' filesystem items
      $result[] = $metadata;
    }
  }
  return [ 'entries'=> $result, 'has_more'=> false ];
}

//============================================================================

function _filesystem_metadata($name, $path) {

  $size = filesize($path);
  $server_modified = date(DATE_ISO8601, filemtime($path));
  $tag = null;
  if (is_dir($path)) { $tag='folder'; }
  elseif (is_file($path)) { $tag='file'; }

  return [ ".tag"=>$tag, 'name'=>$name, 'server_modified'=>$server_modified, 'size'=>$size ];
}

function _filesystem_abspath($path) {

  return ROOT_FOLDER . $path;       // FIXME(nico) security check, path should be absolute starting with '/'
}

function _filesystem_path_join($root, $path) {

  return $root . $path;            // FIXME(nico) check '/' & stuff
}

function _filesystem_checked_GET($varname) {
  if (!isset($_GET[$varname])) {
    throw new _filesystem_HTTPException('Bad Request', 400, 'Missing parameter `' . $varname . '`');
  }
  return $_GET[$varname];
}

class _filesystem_HTTPException extends Exception {
  public $content = null;
  public function __construct($message = null, $code = 501, $content = null) {
    parent::__construct($message, $code);
    $this->content = $content;
  }
  public function setErrorHeader() {
    header("HTTP/1.1 " . $this->code . ' ' . $this->getMessage());
  }
}

//============================================================================

?>
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-01-24 23:58:07

这里有很多要点需要回顾:

  1. 选择编码风格,并坚持它(字符串与‘或",空格,大括号,大写.)
  2. 使用文件,把事情分开,避免头痛。
  3. 使用类,使代码更加清晰和可重用。
  4. 不要在访问点的根部公开所有内容。
  5. 不要依赖注释来使其工作(即"'/‘在末尾“可以由代码强制执行)
  6. 用显式、干净和可读的名称命名类、函数和变量。
  7. 重用代码
  8. 使用早期返回(保护)和简短的函数缩短代码

总的来说,我不认为做您想做的事情是一个好主意,您可能正在您的服务器上实现安全漏洞。您应该考虑使用数据库进行存储,或者快速了解安全性。

尽管如此,下面的代码可能并不完全工作(因为我没有测试它),但是应该给您一个很好的洞察力。从现在开始,一个很好的实践就是编写代码并运行一些单元测试。

web/index.php

代码语言:javascript
运行
复制
* ?action=permanently_delete&path=
* ?action=upload&path=

The provided  values are joined to 'ROOT_FOLDER' class constant.

Can't create folders or download files.

Needs PHP 5.5+ for `finally` clauses.
 */

error_reporting(0);
require_once('../lib/StorageAPI/Controler.php');
require_once('../lib/StorageAPI/Exception.php');
StorageAPIControler::run('files/'); // IMPORTANT(nico) '/' at the end

lib/StorageAPI/Controler.php

代码语言:javascript
运行
复制
getResult();
        }
        catch (Exception $exception) {
            $storage_api_exception = new StorageAPIException('Internal Server Error - '.$exception->getMessage(), 500);
            $result = $storage_api_exception->getResult();
        }

        self::output($result);
    }
    public static function output($result) {
        if(!isset($result['http_status_code']))
            $result['http_status_code'] = 200;
        if(!isset($result['http_status_message']))
            $result['http_status_message'] = 'OK';

        header('Access-Control-Allow-Origin: *');
        header('Cache-Control: no-cache, must-revalidate');
        header('Content-Type:application/json; charset=utf-8');
        echo json_encode($result);
    }

    public static function api_list_folder() {
        $path = self::readGET('path');
        $entries = self::list_folder($path);
        return [ 'entries'=> $result, 'has_more'=> false ];
    }
    public static function list_folder($path) {
        $abspath = self::abspath($path);
        $names = array_diff(scandir($abspath), array('..', '.'));
        $result = [];
        foreach ($names as $name) {
            $path = self::path_join($abspath, $name);

            $metadata = self::metadata($name, $path);
            if ($metadata['.tag'] != null) {
                // NOTE(nico) do not include info on 'undefined' filesystem items
                $result[] = $metadata;
            }
        }
        return $entries;
    }

    public static function api_permanently_delete() {
        $path = self::readGET('path');
        $status = self::permanently_delete($path);
        return []; // TODO
    }
    public static function permanently_delete($path) {
        $abspath = self::abspath($path);
        if (unlink($abspath)) {         // FIXME(nico) can trigger a warning, check file_exists first, and improve error reporting
            return true;
        }
        throw new StorageAPIException('Could not unlink file');
    }

    public static function api_upload() {
        $path = self::readGET('path');
        $result = self::upload($path);
    }
    public static function upload($path) {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            throw new StorageAPIException('Bad Request - POST expected', 400);
        }

        $abspath = self::abspath($path);

        $temppath = tempnam(dirname($abspath), 'temp');
        if (!$temppath) {
            throw new StorageAPIException('"tempnam" failed', 500);
        }

        try {
            $dst = fopen($temppath, 'wb');
            $src = fopen('php://input', 'r'); // POST raw data

            try {
                if (!$src || !$dst) {
                    throw new StorageAPIException('Could not create file', 500);
                }
                // copy streams
                while ($data = fread($src, 1024))
                {
                    if ($data === false) {
                        throw new StorageAPIException('Could not read source data', 500);     // FIXME(nico) endpoint error ?
                    }
                    $written = fwrite($dst, $data, 1024);
                    if ($written != strlen($written)) {
                        throw new StorageAPIException('Could not write to file', 500);
                    }
                }

            }
            finally {
                fclose($src);
                fclose($dst);
            }

            // finalize destination file
            if (!rename($temppath, $abspath)) {
                throw new StorageAPIException('Could not finalize file', 500);
            }
        }
        finally {
            if (file_exists($temppath)) {
                unlink($temppath);
            }
        }

        $name = basename($abspath);
        $result = self::metadata($name, $abspath);
        return $result;
    }

    public static function api_default() {
        throw new StorageAPIException('Bad Request - Unknown action', 400);
    }

    public static function readGET($varname) {
        if (!isset($_GET[$varname])) {
            throw new StorageAPIException('Bad Request - Missing parameter "'.$varname.'"', 400);
        }
        return $_GET[$varname];
    }
    public static function metadata($name, $path) {
        $size = filesize($path);
        $server_modified = date(DATE_ISO8601, filemtime($path));
        $tag = null;
        if (is_dir($path)) {
            $tag = 'folder';
        }
        elseif (is_file($path)) {
            $tag = 'file';
        }
        return [ '.tag'=>$tag, 'name'=>$name, 'server_modified'=>$server_modified, 'size'=>$size];
    }
    public static function abspath($path) {
        return self::$ROOT_FOLDER . $path;       // FIXME(nico) security check, path should be absolute starting with '/'
    }
    public static function path_join($root, $path) {
        return $root . $path;            // FIXME(nico) check '/' & stuff
    }
}

lib/StorageAPI/Exception.php

代码语言:javascript
运行
复制
['.tag'=>'other'], 'http_status_code'=>$this->getCode(), 'http_status_message'=>$this->getMessage()];
    }
}
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/185908

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档