Jump to content

Recommended Posts

How do I simulating a POST request?  I do not wish to use cURL, but wish to execute the script directly in an IDE.  It needs to be more than just setting the $_POST super variable, and needs to actually set the HTTP body header.

 

Below is my unsuccessful attempt.

 

Thank you

 

PS.  Why doesn't var_dump(file_get_contents('php://input')); display anything?


<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

function simulateRequest($method,$url,$get,$post)
{
    $query=http_build_query($get);
    $_GET=$get;
    $_POST=$post;
    $_REQUEST=array_merge($_GET,$_POST,$_REQUEST);
    $_SERVER['REQUEST_METHOD']=$method;
    $_SERVER['REDIRECT_URL']=$url;
    $_SERVER['QUERY_STRING']=$query;
    $_SERVER['REQUEST_URI']=$url.($query?'?'.$query:null);
    if($query) {$_SERVER['REDIRECT_QUERY_STRING']=$query;}
    if(!empty($post)) {
        //HttpRequest::setBody (http_build_query($post)); //http://php.net/manual/it/httprequest.setbody.php
        $opts = ['http' =>['method'  => 'POST','header'  => 'Content-type: application/x-www-form-urlencoded','content' => http_build_query($post)]];
        $context = stream_context_create($opts);
        $rawInput = fopen('php://input', 'r');
        stream_copy_to_stream($context,$rawInput);
    }

}
simulateRequest('POST','/test/bla',[],['a'=>'A','b'=>'B','c'=>'C']);

var_dump(file_get_contents('php://input'));
print_r($_POST);

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';
$app = new \Slim\App();

$app->post('/test/{attr}', function (Request $request, Response $response) {
    echo('Attr:<pre>'.print_r($request->getAttribute('attr'),1).'</pre>');
    exit('Params<pre>'.print_r($request->getParsedBody(),1).'</pre>');
});
$app->get('/test/{attr}', function (Request $request, Response $response) {
    echo('Attr:<pre>'.print_r($request->getAttribute('attr'),1).'</pre>');
    exit('Params<pre>'.print_r($request->getQueryParams(),1).'</pre>');
});

$app->run();
Link to comment
https://forums.phpfreaks.com/topic/301984-simulating-post-request/
Share on other sites

You're trying to copy a stream context to the standard input. What is this supposed to do?

 

I tend to use Burp Suite for manual testing. It allows you to intercept actual requests, edit them, repeat them, create new requests etc. For automated tests, cURL actually is the right tool.

 

Trying to create a “fake” request doesn't really make sense to me. You have to go through plenty of parameters, and then you may still miss something, which means your test won't do what an actual request does.

Looks like PhpEd has its own debugger they want you to use? If that doesn't work for you then you can look to see if there's a way to get Xdebug working with it instead. (Netbeans user here so I don't know.)

 

As for php://input, that will be empty unless you're running the script through the console with something piped into it. (I think - that might only be for stdin.) I don't think you can give it a value short of implementing your own php:// wrapper, which would suck. I think the best option would be two scripts: one script calls the other through the shell and pipes in the $post data, then the other to fill in $_GET and $_POST and such appropriately.

 

I've used that kind of strategy before with my own code... which is somewhere... I don't quite remember... It's used as part of a unit testing framework, albeit mostly for launching PHP processes under specific configurations.

 

// first script

function simulateRequest($method, $url, $get, $post) {
$query = http_build_query($get ?: []);
$input = http_build_query($post ?: []);

// pass most information through argv
$args = implode(" ", array(
escapeshellarg($path_to_second_script),
escapeshellarg($method),
escapeshellarg($url),
escapeshellarg($query)
));
$descriptors = array(
0 => array("pipe", "r"),
1 => STDOUT,
2 => STDERR
);

// launch php with the second script and write to it the post data
$p = proc_open(PHP_BINARY . " -- " . escapeshellarg($path_to_second_script) . " " . , $descriptors, $pipes);
fwrite($pipes[0], $input);
fclose($pipes[0]);
proc_close($p);
}

 

list(, $method, $url, $query) = $argv;

parse_str($query ?: "", $_GET);
parse_str(file_get_contents("php://input"), $_POST);
$_REQUEST = // really you should evaluate this according to request_order
// etc.

// then continue the startup process

 

Meanwhile

$opts = ['http' =>['method' => 'POST','header' => 'Content-type: application/x-www-form-urlencoded','content' => http_build_query($post)]];
$context = stream_context_create($opts);
$rawInput = fopen('php://input', 'r');
stream_copy_to_stream($context,$rawInput);

is really, really wrong.

 

 

 

OH FOR FRICK'S SAKE, why is the indentation broken? Blargh.

Edited by requinix

Thanks Requinx,

 

Yea, I know my streaming thing was REALLY wrong, but was just grasping at straws.

 

Your approach might work but that dang indention must have deleted half of it, and I will probably go with something like the following today.  I still would like a better solution.

<?php

class request
{
    public $method, $attr=[], $get=[], $post=[];
    public function __construct($method,$request,$attr)
    {
        $this->method=$method;
        if($method=='GET'){$this->get=$request;}
        else {$this->post=$request;}
        $this->attr=$attr;
    }    
    public function getMethod()
    {
        return $this->method;
    }
    public function getParsedBody()
    {
        return $this->post;        
    }
    public function getQueryParams()
    {
        return $this->get;
    }
    public function getAttribute($name)
    {
        return $this->attr[$name];
    }
}
class slim
{
    public function __construct($config)
    {
        $db = $config['db_sql'];
        $this->db=new \PDO("mysql:host={$db['host']};dbname={$db['dbname']};charset={$db['charset']}",$db['username'],$db['password'],array(\PDO::ATTR_EMULATE_PREPARES=>false,\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION,\PDO::ATTR_DEFAULT_FETCH_MODE=>\PDO::FETCH_ASSOC));
        $account=new \myApp\Accounts($this->db);
        $this->account=$account->get('main_key');
        $this->logger=null;
    }    
}

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
spl_autoload_register(function ($classname) {
    //Make better
    $parts = explode('\\', $classname);
    require "../classes/".end($parts).".php";
});

$config=parse_ini_file('../../config.ini',true);
$config=[
    'db_sql'=>$config['mysql'],
    'displayErrorDetails'=> true,
    'determineRouteBeforeAppMiddleware' => true,
];
$slim=new slim($config);
$request=new request('POST',['x'=>1],['y'=>2]);    //Request Method, GET or POST array, atributes array
$factory=new Factory($request,$slim);

$rs=$factory->testSomething(/*...*/);
echo('<pre>'.print_r($rs,1).'</pre>');

PHPEd allows you to specify POST values

 

Select "Run/Parameters" from the menu and enter the parameter names and values with type=post.

 

Run the script using either the embedded IE or embedded Mozilla browsers (doesn't work with with embedded Chrome for some reason)

  • Like 1

PHPEd allows you to specify POST values

 

Select "Run/Parameters" from the menu and enter the parameter names and values with type=post.

 

Run the script using either the embedded IE or embedded Mozilla browsers (doesn't work with with embedded Chrome for some reason)

 

Perfect!  Thank you!  PHPed doesn't seem real good about documenting some features, or maybe I just can't find them.  Do you know if there is something similar which will change the URI from say index.php to index.php/foo?

Hey Requinix,  I have been trying to implement your solution, but am stuck.  What should I witness when proc_open() is executed?  Note that the second file never seems to execute.  Thanks


    private function simulateRequest($method, $url, $data, $path_to_second_script, $keys) {
        syslog(LOG_INFO,"$method  |  $url  |  ".json_encode($data));
        foreach($keys as $key=>$value){
            $_SERVER["HTTP_X_$key"]=$value;            
        }
        $query = $method=="GET"?http_build_query($data):'';
        $input = $method!="GET"?http_build_query($data):'';

        // pass most information through argv
        $args = implode(" ", array(
            //escapeshellarg($path_to_second_script),
            escapeshellarg($method),
            escapeshellarg($url),
            escapeshellarg($query)
        ));
        $descriptors = array(
            0 => array("pipe", "r"),
            1 => array("pipe", "w"),
            2 => array("file", "/tmp/error-output.txt", "a")
        );

        // launch php with the second script and write to it the post data
        $p = proc_open(PHP_BINARY . " -- " . escapeshellarg($path_to_second_script) . " " .$args , $descriptors, $pipes);
        fwrite($pipes[0], $input);
        fclose($pipes[0]);
        proc_close($p);
    }
<?php // second script

syslog(LOG_INFO,"Got here!");
list(, $method, $url, $query) = $argv;

parse_str($query ?: "", $_GET);
parse_str(file_get_contents("php://input"), $_POST);
$_REQUEST = 123;// really you should evaluate this according to request_order
// etc.

echo('xxx');
//require('index.php');

Still nothing.  One thing I found was that PHP_BINARY is set to an empty string.  Any ideas why?  Regardless, I hard coded the location of PHP, and tried it again, but still no.  file1.php writes to the syslog, but not file2.php.  What might be the problem?  Thank you

<?php

openlog("myScriptLog", LOG_PID | LOG_PERROR, LOG_LOCAL1);
syslog(LOG_INFO,"File1");

$script=__DIR__.'/file2.php';

$url='hello';
$method='GET';
$data=['a'=>1,'b'=>2, 'c'=>3,'d'=>4];
$query = $method=="GET"?http_build_query($data):'';
$input = $method!="GET"?http_build_query($data):'';

$args = implode(" ", array(
    escapeshellarg($method),
    escapeshellarg($url),
    escapeshellarg($query)
));

$descriptors = array(
    0 => array("pipe", "r"),
    1 => array("pipe", "w"),
    2 => array("file", "/tmp/error-output.txt", "a")
);

// Why is PHP_BINARY set to an empty string?
var_dump(PHP_BINARY);

$command="/usr/bin/php -- " . escapeshellarg($script) . " " .$args;
var_dump($command);

$p = proc_open($command, $descriptors, $pipes);
var_dump($p);
var_dump($pipes);

fwrite($pipes[0], $input);
fclose($pipes[0]);
proc_close($p);

echo('File 1');
<?php
openlog("myScriptLog", LOG_PID | LOG_PERROR, LOG_LOCAL1);
syslog(LOG_INFO,"File2");

echo('file2');

Output of file1.php

string(0) ""
string(95) "/usr/bin/php -- '/var/www/datalogger/src/public/test/file2.php' 'GET' 'hello' 'a=1&b=2&c=3&d=4'"
resource(4) of type (process)
array(2) {
  [0]=>
  resource(2) of type (stream)
  [1]=>
  resource(3) of type (stream)
}
File 1

Bwahaha, I got the command wrong.

  args...          Arguments passed to script. Use -- args when first argument
                   starts with - or script is read from stdin
So I was a bit overzealous in using --. The command should be

$command="/usr/bin/php" . escapeshellarg($script) . " " .$args;

Ah, much better!

 

By the way, what does the -- do?  Granted, you didn't mean to include it, but I suppose it is typically use to do something, and it is one of those things that are impossible to google.

 

Also, any idea why I don't have a value for PHP_BINARY?

 

Thanks!

-- universally(ish) means to stop processing command line arguments and treat the rest as literals. If you did

php -f file.php -h
then PHP would think you wanted to see its help. If you did

php -f file.php -- -h
then PHP would run file.php and pass it "-h" in $argv[1].

 

If you used -f with the command then you would need --, but without -f PHP implicitly stops parsing arguments after the filename.

php filename args...
php -f filename -- args...

And no, I don't know why PHP_BINARY doesn't have a value. Are you calling the first script from the command line?

I think I once knew about "--", but failed to remember.  I really need to better commit it to memory!

 

No, not called from the command line.  Since tried it, and same results.  My PHP install was off some YUM repository, and is probably the culprit.  Off topic, but do you recommend compiling from source?

 

While I am glad I learned, I don't think this solution will address my immediate needs.  I wanted the debugger to magically jump to the second script and allow me to go line-by-line or skip to breakpoints, but I believe it will appear the same as a cURL required (i.e. here is your stuff).

 

Thank you for your help!

I wanted the debugger to magically jump to the second script and allow me to go line-by-line or skip to breakpoints

 

If you can't get this to work in your IDE, consider switching to XDebug and a mainstream IDE. PhpStorm is great (but expensive), Netbeans is fine.

Yeah, the debugger works on one single process - when you start the second script it won't follow along. If you had Xdebug then you could launch that second PHP with the right options to automatically start debugging, implying that you were not debugging the first script, but now it's starting to get complicated.

 

Are you sure you need php://input to work? Would you be able to hack it such that it uses a different path, like "php-dynamic://input", then register your own php-dynamic stream that wraps php normally and your testing library stuff when... testing? That sounds like something I would look into myself.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.