How to get the data from PUT HTTP Request (form-data) - php

This is my function in my API I need to get the data from the form-data from the HTTP request.
I need to get the put values to update my data in my class query.
function method()
{
$method = $_SERVER['REQUEST_METHOD'];
return $method;
}
function updateUser()
{
$method = method();
if($method == 'PUT'){
$put_data = file_get_contents("php://input");
parse_str($put_data, $post_vars);
return $post_vars;
}else{
$status = '400 Bad Request';
return $status;
}
}
{"------WebKitFormBoundaryYKcobRh4FtrGCYaI\r\nContent-Disposition:_form-data;_name":"\"test\"\r\n\r\ntest_value\r\n------WebKitFormBoundaryYKcobRh4FtrGCYaI--\r\n"}
This is what i get from the return $post_vars
I need to have test: test_value

Change the encoding of the request from multipart/form-data to application/x-www-form-urlencoded

Try replacing
$method = method();
if($method == 'PUT'){
//dosomething
}else{
//error
}
With
if (!($putData = fopen("php://input", "r"))){
throw new Exception("Can't get PUT data."); }
else{ //dostuff
}
for more information see:
http://php.net/manual/en/features.file-upload.put-method.php

Related

Using symfony UnicodeString on laravel

I want to use unicodeString from symfoni in laravel controller
protected function validateWaWebhook($known_token, Request $request)
{
if (($signature = $request->headers->get('X-Hub-Signature-256')) == null) {
// throw new BadRequestHttpException('Header not set');
return "header not set";
}
$signature_parts = explode('=', $signature);
if (count($signature_parts) != 2) {
// throw new BadRequestHttpException('signature has invalid format');
return "Signature invalid format";
}
$type = UnicodeString($request->getContent())->normalize(UnicodeString::NFC);
$known_signature = hash_hmac('sha256', $type, $known_token,false);
if (! hash_equals($known_signature, $signature_parts[1])) {
// throw new UnauthorizedException('Could not verify request signature ' . $signature_parts[1]);
return "Could not verify signature";
}
return "Valid signature";
}
When i try it it show
Error: Call to undefined function App\Http\Controllers\UnicodeString()
I've called the symphony
use Symfony\Component\String\UnicodeString;
You need to instantiate the class first and then call normalize method like this.
$unicode = new UnicodeString($request->getContent());
$type = $unicode->normalize(UnicodeString::NFC);
or shorter
$type = (new UnicodeString($request->getContent()))->normalize(UnicodeString::NFC);

Need PHP related clarification

I have the following code written in a file name "backend_user.php"
CODE 1:
<?php
require_once '_db.php';
$json = file_get_contents('php://input');
$params = json_decode($json);
class Result {}
$response = new Result();
if ($user != null) {
$response->result = 'OK';
$response->user = $user['name'];
$response->message = 'Success';
}
else {
$response->result = 'Unauthorized';
$response->message = 'Invalid token';
}
header('Content-Type: application/json');
echo json_encode($response);
?>
The above code gets called by the code below stored in data.service.ts in Angular
CODE 2:
getUser(): Observable<any> {
return this.http.post("/api/backend_user.php", {}).pipe(map((response:any) => {
this.user = response.user;
return response;
}));
}
As you can see from above code, nothing gets passed in the request body. But in CODE 1, there is a variable named $user referred.
From where does $user get it's value from?
Can somebody clarify?
$user is just the response value. It's kind of like an arrow function in javascript so you can rename $user to $foo and do $foo['name'] and it will still work.
$user is just the given variable name for the response. So, $user['name'] is accessing HttpResonse.name effectively. name is just a property on the response.

PHP create override function only for certain parts of the original function

Hi there and welcome to my first official problem! I'm trying to override a function, but only certain parts. The function i want to override, is different in several versions of the open source CMS system. The specific parts which i need to change in order for this to work, are the same in all of those versions.
The override has to work with all of those different versions of the CMS system.
I can't use PECL (apd) package or any other external PHP package for this. And as far i know and could find in the PHP manual, there is no function for this.
So, for example:
We have the different CMS system versions: 1, 2 and 3.
And we would have a original function like this one (THIS IS JUST AN EXAMPLE AND NOT THE ACTUAL FUNCTION I NEED CHANGED), which can only be used for version 3. Certain parts are the same in all versions, and some parts are different:
public function createAccessToken($body = false)
{
if (!$body) { //this is different
$body = 'grant_type=client_credentials'; //this is different
}
$this->action = 'POST'; //this is different
$this->endpoint = 'v1/oauth2/token'; //this is different
$response = $this->makeCall($body, "application/json", true); //this is different
if (!isset($response->access_token)) { //this is THE SAME
return false; //this is THE SAME
}
$this->token = $response->access_token; //this is different
return true; //this is different
}
And this is what i would like to change for all of those versions:
public function createAccessToken($body = false)
{
if (!$body) {
$body = 'grant_type=client_credentials';
}
$this->action = 'POST';
$this->endpoint = 'v1/oauth2/token';
$response = $this->makeCall($body, "application/json", true);
if (isset($response->access_token)) { //IT'S CHANGED! THE -> ! IS GONE
return false;
}
$this->token = $response->access_token;
return true;
}
But the function above (which has changed), will only work with version 3 of the CMS system.
Therefore, is there any way i can only override the specific part i need to change and "get" the code which doesn't have to change some other way so the function would still be executed? So again:
public function createAccessToken($body = false)
{
//some way to get the code above the part i need to change, untill i get to the part which needs to be changed. So the override "takes over" from here.
if (isset($response->access_token)) { //IT'S CHANGED! THE -> ! IS GONE
return false;
}
//some way to get the rest of the code and have the function continue agian
}
Hope you can help me out on this.
You could create your own middle layer. This is assuming you have access to some variable that defines your current VERSION.
public function createAccessToken($body = false) {
if (VERSION == 1 || VERSION == 2) { createAccessTokenOld($body); }
else { createAccessTokenNew($body); }
}
public function createAccessTokenOld($body = false)
{
if (!$body) {
$body = 'grant_type=client_credentials';
}
$this->action = 'POST';
$this->endpoint = 'v1/oauth2/token';
$response = $this->makeCall($body, "application/json", true);
if (!isset($response->access_token)) { //IT'S CHANGED! THE -> ! IS GONE
return false;
}
$this->token = $response->access_token;
return true;
}
public function createAccessTokenNew($body = false)
{
if (!$body) {
$body = 'grant_type=client_credentials';
}
$this->action = 'POST';
$this->endpoint = 'v1/oauth2/token';
$response = $this->makeCall($body, "application/json", true);
if (isset($response->access_token)) { //IT'S CHANGED! THE -> ! IS GONE
return false;
}
$this->token = $response->access_token;
return true;
}
You could also do it with a bit more code reuse:
public function createAccessToken($body = false) {
if (VERSION == 1 || VERSION == 2) { createAccessToken($body, true); }
else { createAccessToken($body, false); }
}
public function createAccessToken($body = false, $isOld = false)
{
if (!$body) {
$body = 'grant_type=client_credentials';
}
$this->action = 'POST';
$this->endpoint = 'v1/oauth2/token';
$response = $this->makeCall($body, "application/json", true);
if ($isOld) { if (!isset($response->access_token)) { return false; } }
else { if (isset($response->access_token)) { return false; } }
$this->token = $response->access_token;
return true;
}

Using GuzzleHttp/Psr7/Response Correctly

Not sure what is the correct way to display in a php page a Psr7 Guzzle Response.
Right now, I am doing:
use GuzzleHttp\Psr7\BufferStream;
use GuzzleHttp\Psr7\Response;
class Main extends \pla\igg\Main
{
function __construct()
{
$stream = new BufferStream();
$stream->write("Hello I am a buffer");
$response = new Response();
$response = $response->withBody($stream);
$response = $response->withStatus('201');
$response = $response->withHeader("Content-type", "text/plain");
$response = $response->withAddedHeader("IGG", "0.4.0");
//Outputing the response
http_response_code($response->getStatusCode());
foreach ($response->getHeaders() as $strName => $arrValue)
{
foreach ($arrValue as $strValue)
{
header("{$strName}:{$strValue}");
}
}
echo $response->getBody()->getContents();
}
}
Is there a more OOP way to display the response?
A more OOP way of doing the same thing is to create a Sender object that requires a ResponseInterface in its constructor. This class would be responsible for setting headers, clearing buffers and render the response:
use Psr\Http\Message\ResponseInterface;
class Sender
{
protected $response;
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
public function send(): void
{
$this->sendHeaders();
$this->sendContent();
$this->clearBuffers();
}
protected function sendHeaders(): void
{
$response = $this->response;
$headers = $response->getHeaders();
$version = $response->getProtocolVersion();
$status = $response->getStatusCode();
$reason = $response->getReasonPhrase();
$httpString = sprintf('HTTP/%s %s %s', $version, $status, $reason);
// custom headers
foreach ($headers as $key => $values) {
foreach ($values as $value) {
header($key.': '.$value, false);
}
}
// status
header($httpString, true, $status);
}
protected function sendContent()
{
echo (string) $this->response->getBody();
}
protected function clearBuffers()
{
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} elseif (PHP_SAPI !== 'cli') {
$this->closeOutputBuffers();
}
}
private function closeOutputBuffers()
{
if (ob_get_level()) {
ob_end_flush();
}
}
}
Use it like this:
$sender = new Sender($response);
$sender->send();
Better yet, you could inject the Sender into your app object and transform it in a class variable, so you'd call it like this:
function renderAllMyCoolStuff()
{
$this->sender->send();
}
I'll leave it as a reader's exercise to implement getters and setters for the Response object, plus a method to receive some content string and transform it into a Response object internally.
Guzzle is a library for doing HTTP calls inside your app, it has nothing to do with the end user communication.
If you need to send specific headers to your end user, just use http_response_code() (that you are already using), header() and echo. Or see the docs for your framework, if you use one (Symfony, Slim, whatever).

Set Request Methods for REST API

I'm trying to implement a RESTful API for my web app.
I have learned from some tutorials how to write the classes for REST API service.
However, in one of the tutorials, they use AngularJS to send HTTP.delete requests to the server on button clicks.
If I don't want to use AngularJS, how can I set the request method in the HTML form to delete a specified user?
For example, if I go to localhost/app/api/user/3
I will get the information of User of id 3 in JSON representation because the default request method is GET.
How can I send delete request to localhost/app/api/user/3 and to delete this user?
In HTML forms, there are only POST and GET methods.
I also don't know how the PUT works...
The following are the classes:
<?php
class REST {
public $_request_args = array();
private $_method = "";
public $_content_type = "application/json";
private $_code = 200;
public function __construct() {
$this->inputs();
}
private function inputs() {
$this->_method = $this->get_request_method();
switch($this->_method){
case "POST":
$this->_request_args = $this->cleanInputs($_POST);
break;
case "GET":
$this->_request_args = $this->cleanInputs($_GET);
break;
case "DELETE":
case "PUT":
parse_str(file_get_contents("php://input"),$this->_request_args);
$this->_request_args = $this->cleanInputs($this->_request_args);
break;
default:
$this->response('Method Not Allowed',405);
break;
}
}
private function get_status_message(){
$status = array(
200 => 'OK',
204 => 'No Content',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
500 => 'Internal Server Error');
return ($status[$this->_code]) ? $status[$this->_code] : $status[500];
}
public function get_request_method(){
$request_method = $_SERVER['REQUEST_METHOD'];
if ($request_method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) {
if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') {
$request_method = 'DELETE';
} else if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') {
$request_method = 'PUT';
} else {
throw new Exception("Unexpected Header");
}
}
return $request_method;
}
private function cleanInputs($data){
$clean_input = array();
if (is_array($data)) {
foreach ($data as $k => $v) {
$clean_input[$k] = $this->cleanInputs($v);
}
} else {
if(get_magic_quotes_gpc()) {
$data = trim(stripslashes($data));
}
$data = strip_tags($data);
$clean_input = trim($data);
}
return $clean_input;
}
private function set_headers() {
header("HTTP/1.1 ".$this->_code." ".$this->get_status_message());
header("Content-Type:".$this->_content_type);
}
public function response($data, $status = 200) {
$this->_code = ($status)? $status : 200;
$this->set_headers();
if (is_array($data))
echo json_encode($data, JSON_PRETTY_PRINT);
else
echo $data;
exit;
}
}
?>
//EDIT::
<?php
class API extends REST {
public $data = "";
public function processApi() {
$request_args = explode('/', rtrim($_SERVER["PATH_INFO"], '/'));
$func = array_shift($request_args);
if(method_exists($this, $func))
$this->$func();
else {
$this->response('',404); // If the method not exist with in this class "Page not found".
}
}
private function users() {
if($this->get_request_method() != "GET"){
$this->response('',405);
}
$conn = DBManager::getConnection();
$stmt = $conn->prepare("SELECT * FROM USER");
$stmt->execute();
$result = $stmt->fetchAll();
$this->response($result);
}
private function courses() {
if($this->get_request_method() != "GET"){
$this->response('',405);
}
$conn = DBManager::getConnection();
$stmt = $conn->prepare("SELECT * FROM COURSE");
$stmt->execute();
$result = $stmt->fetchAll();
$this->response($result);
}
private function subjects() {
if($this->get_request_method() != "GET"){
$this->response('',405);
}
$conn = DBManager::getConnection();
$stmt = $conn->prepare("SELECT * FROM SUBJECT");
$stmt->execute();
$result = $stmt->fetchAll();
$this->response($result);
}
}
$api = new API;
$api->processApi();
?>
So my question is if I have a form like the following:
<form action='api/users/3' method='POST'>
<input type='submit' value='Delete User' />
</form>
How can I actually delete this user with that request even though the method is POST not DELETE?
I have checked other posts and there's no answer to this question!
Browsers do not support HTTP DELETE and PUT methods in HTML Forms. To get support for the DELETE method, you can insert a pseudo "method" (a hidden text element) in your FORM, this is how the Slim and Breeze router / "micro-framework" handles PUT and DELETE methods.
<form action='api/users/3' method='POST'>
<input type="hidden" name="_method" value="PUT | DELETE" />
<input type='submit' value='Delete User' />
</form>
In Breeze, using the DELETE method will look like this in your PHP script:
delete('/', function(){
echo 'you made a DELETE request';
});
You may be able to do something similar. Alternatively, you may consider switching to a REST router or micro-framework that comes with support for DELETE and PUT.
for Google Chrome, you can install a very helpful extension that helps you to develop and test your REST API: Advanced REST Client
Also, you need to specify the HTTP.delete case as the following:
case "DELETE":
parse_str(file_get_contents("php://input"),$this->_request_args);
$this->_request_args = $this->cleanInputs($this->_request_args);
break;
There seems to be something wrong with the processApi() function because that's not how you get the arguments.
Fix your HTACCESS file to redirect URLs properly.
Follow this tutorial for more info: http://coreymaynard.com/blog/creating-a-restful-api-with-php/

Categories