84

I am working on my REST client library for CodeIgniter and I am struggling to work out how to send PUT and DELETE arguments in PHP.

In a few places I have seen people using the options:

$this->option(CURLOPT_PUT, TRUE);
$this->option(CURLOPT_POSTFIELDS, $params);

Annoyingly, this seems to do nothing. Is this the correct way to set PUT parameters?

If so, how do I set DELETE parameters?

$this->option() is part of my library, it simply builds up an array of CURLOPT_XX constants and sends them to curl_setopt_array() when the built up cURL request is executed.

I am attempting to read PUT and DELETE parameters using the following code:

        case 'put':
            // Set up out PUT variables
            parse_str(file_get_contents('php://input'), $this->_put_args);
        break;

        case 'delete':
            // Set up out PUT variables
            parse_str(file_get_contents('php://input'), $this->_delete_args);
        break;

There are two options here, I am approaching this in the wrong way or there is a bug somewhere in my libraries. If you could let me know if this should theoretically work I can just hammer away on debug until I solve it.

I dont want to waste any more time on an approach that is fundamentally wrong.

hakre
  • 193,403
  • 52
  • 435
  • 836
Phil Sturgeon
  • 30,637
  • 12
  • 78
  • 117

6 Answers6

68

Instead of using CURLOPT_PUT = TRUE use CURLOPT_CUSTOMREQUEST = 'PUT' and CURLOPT_CUSTOMREQUEST = 'DELETE' then just set values with CURLOPT_POSTFIELDS.

Bhavik Ambani
  • 6,557
  • 14
  • 55
  • 86
Phil Sturgeon
  • 30,637
  • 12
  • 78
  • 117
49

Here is some code which may be helpful for others wanting to handle PUT and DELETE params. You are able to set $_PUT and $_DELETE via $GLOBALS[], but they will not be directly accessible in functions unless declared global or accessed via $GLOBALS[]. To get around this, I've made a simple class for reading GET/POST/PUT/DELETE request arguments. This also populates $_REQUEST with PUT/DELETE params.

This class will parse the PUT/DELETE params and support GET/POST as well.

class Params {
  private $params = Array();

  public function __construct() {
    $this->_parseParams();
  }

  /**
    * @brief Lookup request params
    * @param string $name Name of the argument to lookup
    * @param mixed $default Default value to return if argument is missing
    * @returns The value from the GET/POST/PUT/DELETE value, or $default if not set
    */
  public function get($name, $default = null) {
    if (isset($this->params[$name])) {
      return $this->params[$name];
    } else {
      return $default;
    }
  }

  private function _parseParams() {
    $method = $_SERVER['REQUEST_METHOD'];
    if ($method == "PUT" || $method == "DELETE") {
        parse_str(file_get_contents('php://input'), $this->params);
        $GLOBALS["_{$method}"] = $this->params;
        // Add these request vars into _REQUEST, mimicing default behavior, PUT/DELETE will override existing COOKIE/GET vars
        $_REQUEST = $this->params + $_REQUEST;
    } else if ($method == "GET") {
        $this->params = $_GET;
    } else if ($method == "POST") {
        $this->params = $_POST;
    }
  }
}
Bryan Drewery
  • 2,569
  • 19
  • 13
16

Just remember, most webservers do not handle PUT & DELETE requests. Since you're making a library, I'd suggest thinking about accounting for this. Typically, there are two conventions you can use to mimic PUT & DELETE via POST.

  1. use a custom POST variable (ex. _METHOD=PUT) which overrides POST
  2. set a custom HTTP header (ex. X-HTTP-Method-Override: PUT)

Generally speaking, most RESTful services that don't allow PUT & DELETE directly will support at least one of those strategies. You can use cURL to set a custom header if you need via the CURLOPT_HTTPHEADER option.

// ex...
curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-HTTP-Method-Override: PUT') );
Owen
  • 82,995
  • 21
  • 120
  • 115
3

I think you're mixing your verbs - PUT is for putting a file, POST is for posting variables (although you can POST a file).

To set the post variables, use CURLOPT_POSTFIELDS with either a string of param1=val1&param2=val2 or an associative array.

To do a DELETE, you'll want to use the curl option CURLOPT_CUSTOMREQUEST

Adam Hopkinson
  • 28,281
  • 7
  • 65
  • 99
  • 1
    PUT and DELETE both support the sending of additional arguments. You can do this via the command line when working with curl but I cannot work out how to send with PHP-cURL. For example, as this is for REST, I need a way to mark what I am deleting. If I am deleting user id, I want to be able to pass the id without cluttering the URL. API's like Twitter only support GET/POST so you can put that in the URL, but other REST API's may not support this sort of thing, so I need to send a delete parameter for it. – Phil Sturgeon Jan 17 '10 at 17:47
  • 1
    Ok I have PUT parameters working, I swapped CURLOPT_PUT = TRUE for CURLOPT_CUSTOMREQUEST = 'PUT' and thats working fine. Now, how the hell do I set DELETE? – Phil Sturgeon Jan 17 '10 at 18:16
  • CURLOPT_CUSTOMREQUEST = 'DELETE', then use query string for the parameters maybe – Adam Hopkinson Jan 17 '10 at 18:20
  • 1
    Close, I was trying to avoid using them in the query string as I have already mentioned. Turns out you can use CURLOPT_POSTFIELDS with PUT and DELETE when using CURLOPT_CUSTOMREQUEST = 'DELETE. Thanks for your feedback, but I solved it myself in the end :-) – Phil Sturgeon Jan 17 '10 at 18:27
  • 2
    Did you? I didn't help at all then? – Adam Hopkinson Jan 17 '10 at 18:38
  • Just to clarify PUT / POST act on resources, not necessarily files. The difference being POST is not idempotent, while GET, PUT, and DELETE should be. – Owen Jan 17 '10 at 19:02
2

This is my version of the DELETE for CI. It accepts GET-style arguments for the DELETE, even same name arguments, i.e.: GET /some/url?id=1&id=2&id=3

protected function _parse_delete()
{
    $query = $_SERVER['QUERY_STRING'];
    if ( !empty( $query ) )
    {
        foreach( explode('&', $query ) as $param )
        {
            list($k, $v) = explode('=', $param);
            $k = urldecode($k);
            $v = urldecode($v);
            if ( isset( $this->_delete_args[$k] ) )
            {
                if ( is_scalar( $this->_delete_args[$k] ) )
                {
                    $this->_delete_args[$k] = array( $this->_delete_args[$k] );
                }
                $this->_delete_args[$k][] = $v ;
            }
            else
            {
                $this->_delete_args[$k] = $v;
            }
        }
    }
}
Patrick Savalle
  • 4,068
  • 3
  • 22
  • 24
1

This is how i sole my DELETE problem:

==>> in REST_Controller.php i replace the delault _parse_delete() function by :

protected function _parse_delete()
{
    $this->_delete_args = $_DELETE;
    $this->request->format and $this->request->body = file_get_contents('php://input');
    // Set up out DELETE variables (which shouldn't really exist, but sssh!)
    parse_str(file_get_contents('php://input'), $this->_delete_args);
}

;) it works for me!

okobsamoht
  • 11
  • 6