Перезапись $_POST для запросов PUT или DELETE

В PHP я хотел бы иметь доступ к PUT и DELETE vars глобально, похожим на то, как к GET и POST к ним обращаются глобально. Первоначально я рассматривал возможность добавления данных в $_PUT и $_DELETE соответственно в глобальном пространстве имен, но затем я понял, что данные для каждого запроса хранятся в теле сообщения, поэтому нет возможности для этого иметь более одного набора данных из POST, PUT или DELETE.

Есть ли побочные эффекты перезаписи переменной $_POST?

то есть. str_parse( file_get_contents( 'php://input' ), $_POST );

Неужели я глуп, или есть лучший способ доступа к данным PUT и DELETE?


Изменить, чтобы прояснить мое мышление:

Я очень хорошо знаю источник данных в $_POST, на самом деле я упомянул об этом ранее в своем вопросе. Если на сервер отправляется запрос HTTP POST, данные хранятся в php://input. Если на сервер отправляется запрос HTTP PUT или DELETE, данные хранятся в том же месте, что означает, что $_POST будет пустым (поскольку данные не были POSTed, несмотря на доступность данных.

A GET запрос, с другой стороны, передается через строку запроса. Это позволяет одновременно передавать переменные $_POST и $_GET. Это не возможность одновременного прохождения переменных POST и PUT or DELETE.

Если я перезаписываю $_POST из php://input в PUT и /DELETE запросах, потеря данных отсутствует.

Альтернатива добавления:

global $_PUT;
global $_DELETE;

к началу функций кажется глупым, так как я могу использовать только по одному в любом случае.

Мой первый вопрос, который я действительно хочу получить, касается того, какие побочные эффекты или проблемы существуют при перезаписывании $_POST. Я не могу быть первым, кто попробует что-то глупое:

$_POST['foo'] = 'bar';

Я просто обеспокоен тем, что если я сделаю что-то похожее, чтобы оно не сохранилось по областям.

Ответы

Ответ 1

Вы увидите, что это называется "плохой практикой" по всему Интернету, но если вы действительно понимаете, почему это "плохая практика", ну, ответы становятся нечеткими. Самая конкретная причина - так называемый сценарий "удар по автобусу" - что, если проект передается новому разработчику?

Рука, отталкивающая в сторону (вы можете оставить комментарии, в конце концов), действительно нет убедительной причины не делать это так, но опять же, нет веских оснований это тоже. Почему бы не поместить значения в ключ $_SESSION, если вы хотите, чтобы они были глобальными? Или сделать глобальную переменную? Или сделать статический класс для доступа к значениям PUT/DELETE? Со всеми другими дополнительными подходами, я думаю, что перезапись $_POST, в то время как это не приведет к взрыву вашего сервера, скорее всего, приведет к головной боли по дороге.

Я бросил этот маленький статический класс вместе, вы захотите проверить это, прежде чем полагаться на него. Использование:

//To check if this is a rest request:
Rest::isRest();

//To get a parameter from PUT
$put_var = Rest::put('keyname', false);

//To get a parameter from DELETE
$dele_var = Rest::delete('keyname', false);

 class Rest {
    static private $put = false;
    static private $delete = false;
    static private $is_rest = false;
    function __construct() {
        self::$is_rest = true;
        switch ($_SERVER['REQUEST_METHOD']) {
            case 'PUT':
                parse_str(self::getVars(), self::$put);
                break;
            case 'DELETE':
                parse_str(self::getVars(), self::$delete);
                break;
            default:
                self::$is_rest = false;
        }
    }
    private static function getVars() {
        if (strlen(trim($vars = file_get_contents('php://input'))) === 0)
            $vars = false;
        return $vars;
    }
    public static function delete($key=false, $default=false) {
        if (self::$is_rest !== true)
            return $default;
        if (is_array(self::$delete) && array_key_exists($key, self::$delete))
            return self::$delete[$key];
        return $default;
    }
    public static function put($key=false, $default=false) {
        if (self::$is_rest !== true)
            return $default;
        if (is_array(self::$put) && array_key_exists($key, self::$put))
            return self::$put[$key];
        return $default;
    }
    public static function isRest() {
        return self::$is_rest;
    }
}

Ответ 2

Оставьте сообщение и получите как есть. он не должен изменяться, поскольку он предназначен только для чтения. Создайте глобальные переменные $_PUT и $_DELETE:

//  globals
$_DELETE = array ();
$_PUT = array ();

switch ( $_SERVER['REQUEST_METHOD'] ) {
    case !strcasecmp($_SERVER['REQUEST_METHOD'],'DELETE'):
        parse_str( file_get_contents( 'php://input' ), $_DELETE );
        break;

    case !strcasecmp($_SERVER['REQUEST_METHOD'],'PUT'):
        parse_str( file_get_contents( 'php://input' ), $_PUT );
        break;
}

Не тестировалось, но вы должны получить эту идею. Несколько недель назад я искал рамки для отдыха и решил пойти с python. Перерыв (http://www.recessframework.org/) кажется многообещающим, хотя

Ответ 3

Вы не должны изменять $_POST напрямую, так как это представляет значения, исходящие от клиента. Рассмотрите его только для чтения и внесите какие-либо изменения в пользовательскую переменную.

В качестве контроля за доступом к данным PUT и DELETE в настоящее время нет суперглобального встроенного в PHP для прямого доступа к этим данным. Поскольку данные являются файловыми данными, которые могут быть довольно большими, полезность и эффективность чтения всего содержимого файла в типичном присваивании $variable = $_PUT['file']; сомнительна. Вместо этого его следует читать в кусках. Таким образом, использование согласуется с чтением с любого другого ресурса ввода файлов.

Подробнее о PUT здесь:

http://php.net/manual/en/features.file-upload.put-method.php

Ответ 4

Если вы создаете объект "запрос", то независимо от того, идет ли запрос через HTTP, командную строку или через веб-сокет HTML5, у вас будет единый способ доступа к данным запроса. Затем вы можете сделать объект запроса доступным в глобальной области или передать его как аргумент требуемым функциям или методам.

В идеале вы должны хранить данные, которые не зависят от запроса в статических или глобальных переменных, например. параметры, которые являются "статическими" независимо от запроса, и данными, специфичными для запроса в локальной переменной или объекте, которые могут использоваться вашей бизнес-логикой. Например, если у вас был сервер веб-сокетов, было бы проще обрабатывать несколько объектов запроса в одном PHP-процессе. Вот пример, который может помочь:

 $headers = getallheaders();
 $query = parse_str($_SERVER['QUERY_STRING']);
 $data = file_get_contents('php://input');

 if(strpos($headers['Content-Type'],'application/x-www-form-urlencoded') !== false)
 {
      $data = parse_str($data);
 }
 elseif(strpos($headers['Content-Type'],'application/json') !== false)
 {
      $data = json_decode($data);
 }
 elseif(strpos($headers['Content-Type'],'application/soap+xml') !== false)
 {
      $data = // parse soap
 }
 elseif(strpos($headers['Content-Type'],'application/xml') !== false)
 {
      $data = // parse xml
 }
 // else ...


 $request = new Request($_SERVER['REQUEST_METHOD'],$data,$query);

 // example business logic

    $method = $request->get_request_method();

    $obj = new BlogPost();
    if($method == 'GET')
    {
        $obj->id($request->get_query('id'));
        $obj->load();
    }
    elseif($method == 'PUT')
    {
        $obj->id($request->get_query('id'));
        $obj->title($request->get_data('title'));
        $obj->body($request->get_data('body'));
        $obj->save();
    }
    elseif($method == 'POST')
    {
        $obj->title($request->get_data('title'));
        $obj->body($request->get_data('body'));
        $obj->save();
    }
    elseif($method == 'DELETE')
    {
        $obj->id($request->get_query('id'));
        $obj->wipe();
    }

Независимо от того, является ли это PUT, POST, PATCH или DELETE, в HTTP-запросе имеется только один массив данных, поэтому вашему приложению не нужен комплексный объект запроса. Объект запроса может сделать ваш контроллер (если вы используете MVC) очень просто.